mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
Resolve conflicts
This commit is contained in:
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +0,0 @@
|
||||
[submodule "clients/apple"]
|
||||
path = clients/apple
|
||||
url = https://github.com/SagerNet/sing-box-for-apple.git
|
||||
[submodule "clients/android"]
|
||||
path = clients/android
|
||||
url = https://github.com/SagerNet/sing-box-for-android.git
|
||||
|
||||
197
.goreleaser.yaml
Normal file
197
.goreleaser.yaml
Normal file
@@ -0,0 +1,197 @@
|
||||
version: 2
|
||||
project_name: sing-box
|
||||
builds:
|
||||
- &template
|
||||
id: main
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
|
||||
- -s
|
||||
- -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
- with_masque
|
||||
- with_mtproxy
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOTOOLCHAIN=local
|
||||
targets:
|
||||
- linux_386
|
||||
- linux_amd64_v1
|
||||
- linux_arm64
|
||||
- linux_arm_6
|
||||
- linux_arm_7
|
||||
- linux_s390x
|
||||
- linux_riscv64
|
||||
- linux_mips
|
||||
- linux_mips_softfloat
|
||||
- linux_mipsle
|
||||
- linux_mipsle_softfloat
|
||||
- linux_mips64
|
||||
- linux_mips64le
|
||||
- windows_amd64_v1
|
||||
- windows_386
|
||||
- windows_arm64
|
||||
- darwin_amd64_v1
|
||||
- darwin_arm64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
- id: manager
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
|
||||
- -s
|
||||
- -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
- with_masque
|
||||
- with_mtproxy
|
||||
- with_manager
|
||||
- with_admin_panel
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOTOOLCHAIN=local
|
||||
targets:
|
||||
- linux_386
|
||||
- linux_amd64_v1
|
||||
- linux_arm64
|
||||
- linux_arm_6
|
||||
- linux_arm_7
|
||||
- linux_s390x
|
||||
- linux_riscv64
|
||||
- linux_mips
|
||||
- linux_mips_softfloat
|
||||
- linux_mipsle
|
||||
- linux_mipsle_softfloat
|
||||
- linux_mips64
|
||||
- linux_mips64le
|
||||
- windows_amd64_v1
|
||||
- windows_386
|
||||
- windows_arm64
|
||||
- darwin_amd64_v1
|
||||
- darwin_arm64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
- id: legacy
|
||||
<<: *template
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
- with_masque
|
||||
- with_mtproxy
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
targets:
|
||||
- windows_amd64_v1
|
||||
- windows_386
|
||||
- id: android
|
||||
<<: *template
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- GOTOOLCHAIN=local
|
||||
overrides:
|
||||
- goos: android
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
env:
|
||||
- CC=armv7a-linux-androideabi21-clang
|
||||
- CXX=armv7a-linux-androideabi21-clang++
|
||||
- goos: android
|
||||
goarch: arm64
|
||||
env:
|
||||
- CC=aarch64-linux-android21-clang
|
||||
- CXX=aarch64-linux-android21-clang++
|
||||
- goos: android
|
||||
goarch: 386
|
||||
env:
|
||||
- CC=i686-linux-android21-clang
|
||||
- CXX=i686-linux-android21-clang++
|
||||
- goos: android
|
||||
goarch: amd64
|
||||
goamd64: v1
|
||||
env:
|
||||
- CC=x86_64-linux-android21-clang
|
||||
- CXX=x86_64-linux-android21-clang++
|
||||
targets:
|
||||
- android_arm_7
|
||||
- android_arm64
|
||||
- android_386
|
||||
- android_amd64
|
||||
archives:
|
||||
- &template
|
||||
id: archive
|
||||
builds:
|
||||
- main
|
||||
- android
|
||||
formats:
|
||||
- tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats:
|
||||
- zip
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- LICENSE
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}-{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
- id: archive_with_manager
|
||||
builds:
|
||||
- manager
|
||||
formats:
|
||||
- tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats:
|
||||
- zip
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- LICENSE
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-with-manager-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}-{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
- id: archive-legacy
|
||||
<<: *template
|
||||
builds:
|
||||
- legacy
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-legacy'
|
||||
source:
|
||||
enabled: false
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}.source'
|
||||
prefix_template: '{{ .ProjectName }}-{{ .Version }}/'
|
||||
checksum:
|
||||
disable: true
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}.checksum'
|
||||
signs:
|
||||
- artifacts: checksum
|
||||
release:
|
||||
github:
|
||||
owner: shtorm-7
|
||||
name: sing-box-extended
|
||||
draft: true
|
||||
prerelease: auto
|
||||
mode: replace
|
||||
ids:
|
||||
- archive
|
||||
- package
|
||||
skip_upload: true
|
||||
8
Makefile
8
Makefile
@@ -70,14 +70,10 @@ update_certificates:
|
||||
go run ./cmd/internal/update_certificates
|
||||
|
||||
release:
|
||||
go run ./cmd/internal/build goreleaser release --clean --skip publish
|
||||
go run ./cmd/internal/build goreleaser release --skip=validate --clean -p 3 --skip publish
|
||||
mkdir dist/release
|
||||
mv dist/*.tar.gz \
|
||||
dist/*.zip \
|
||||
dist/*.deb \
|
||||
dist/*.rpm \
|
||||
dist/*_amd64.pkg.tar.zst \
|
||||
dist/*_arm64.pkg.tar.zst \
|
||||
dist/release
|
||||
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
|
||||
rm -r dist/release
|
||||
@@ -101,7 +97,7 @@ upload_android:
|
||||
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release_android
|
||||
rm -rf dist/release_android
|
||||
|
||||
release_android: lib_android update_android_version build_android upload_android
|
||||
release_android: lib_android update_android_version build_android
|
||||
|
||||
publish_android:
|
||||
cd ../sing-box-for-android && ./gradlew :app:publishPlayReleaseBundle && ./gradlew --stop
|
||||
|
||||
49
README.md
49
README.md
@@ -2,22 +2,47 @@
|
||||
|
||||
Sing-box with extended features.
|
||||
|
||||
## Features
|
||||
## 🔥 Features
|
||||
|
||||
* Amnezia 1.5
|
||||
* WARP
|
||||
* Tunneling
|
||||
* Mieru
|
||||
* XHTTP
|
||||
* SDNS (DNSCrypt)
|
||||
* Extended Wireguard options
|
||||
* Unified delay
|
||||
### 🌐 Protocols
|
||||
- **WARP**
|
||||
- **Masque**
|
||||
- **MTProxy**
|
||||
- **Mieru**
|
||||
- **VPN**
|
||||
- **Bond**
|
||||
- **Fallback**
|
||||
|
||||
## Examples
|
||||
### 🚦 Limiters
|
||||
- **Bandwidth Limiter**
|
||||
- **Connection Limiter**
|
||||
|
||||
### 🛡 Encryption & Obfuscation
|
||||
- **Amnezia 2.0**
|
||||
- **VLESS encryption**
|
||||
|
||||
### 🔄 Transports
|
||||
- **mKCP**
|
||||
- **XHTTP**
|
||||
|
||||
### 🛠 Services
|
||||
- **Admin Panel**
|
||||
- **Manager**
|
||||
- **Node Manager**
|
||||
|
||||
### ⚙ Miscellaneous
|
||||
- **Link parser**
|
||||
- **SDNS (DNSCrypt)**
|
||||
- **Extended WireGuard options**
|
||||
- **Unified Delay**
|
||||
|
||||
## 📚 Examples
|
||||
|
||||
Configuration examples are available here:
|
||||
|
||||
https://github.com/shtorm-7/sing-box-extended/tree/extended/examples
|
||||
|
||||
## Support the project
|
||||
## Support the Project
|
||||
|
||||
If you want to support the project, you can donate to the following addresses.
|
||||
|
||||
@@ -42,7 +67,7 @@ bc1qqx97p8k4dchqkyd47s4vf74hrqdfnmhqvcja7x
|
||||
0xAcc5919C22F2B3fAa0ec7E8BaD142da5B375FBF6
|
||||
```
|
||||
|
||||
## License
|
||||
## 📄 License
|
||||
|
||||
```
|
||||
Copyright (C) 2022 by nekohasekai <contact-sagernet@sekai.icu>
|
||||
|
||||
@@ -48,6 +48,7 @@ type CacheFile interface {
|
||||
RDRCStore
|
||||
|
||||
StoreWARPConfig() bool
|
||||
StoreMASQUEConfig() bool
|
||||
|
||||
LoadMode() string
|
||||
StoreMode(mode string) error
|
||||
@@ -59,6 +60,10 @@ type CacheFile interface {
|
||||
SaveRuleSet(tag string, set *SavedBinary) error
|
||||
LoadWARPConfig(tag string) *SavedBinary
|
||||
SaveWARPConfig(tag string, set *SavedBinary) error
|
||||
LoadMASQUEConfig(tag string) *SavedBinary
|
||||
SaveMASQUEConfig(tag string, set *SavedBinary) error
|
||||
LoadSubscription(tag string) *SavedBinary
|
||||
SaveSubscription(tag string, sub *SavedBinary) error
|
||||
}
|
||||
|
||||
type SavedBinary struct {
|
||||
|
||||
@@ -30,6 +30,7 @@ type UDPInjectableInbound interface {
|
||||
type InboundRegistry interface {
|
||||
option.InboundOptionsRegistry
|
||||
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) (Inbound, error)
|
||||
UnsafeCreate(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) (Inbound, error)
|
||||
}
|
||||
|
||||
type InboundManager interface {
|
||||
@@ -41,16 +42,15 @@ type InboundManager interface {
|
||||
}
|
||||
|
||||
type InboundContext struct {
|
||||
Inbound string
|
||||
InboundType string
|
||||
IPVersion uint8
|
||||
Network string
|
||||
Source M.Socksaddr
|
||||
Destination M.Socksaddr
|
||||
TunnelSource string
|
||||
TunnelDestination string
|
||||
User string
|
||||
Outbound string
|
||||
Inbound string
|
||||
InboundType string
|
||||
IPVersion uint8
|
||||
Network string
|
||||
Source M.Socksaddr
|
||||
Destination M.Socksaddr
|
||||
Gateway *netip.Addr
|
||||
User string
|
||||
Outbound string
|
||||
|
||||
// sniffer
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@ func (m *Registry) CreateOptions(outboundType string) (any, bool) {
|
||||
func (m *Registry) Create(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
return m.UnsafeCreate(ctx, router, logger, tag, outboundType, options)
|
||||
}
|
||||
|
||||
func (m *Registry) UnsafeCreate(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
|
||||
constructor, loaded := m.constructor[outboundType]
|
||||
if !loaded {
|
||||
return nil, E.New("outbound type not found: " + outboundType)
|
||||
|
||||
@@ -35,6 +35,7 @@ type DirectRouteOutbound interface {
|
||||
type OutboundRegistry interface {
|
||||
option.OutboundOptionsRegistry
|
||||
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
||||
UnsafeCreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
||||
}
|
||||
|
||||
type OutboundManager interface {
|
||||
|
||||
@@ -57,6 +57,10 @@ func (r *Registry) CreateOptions(outboundType string) (any, bool) {
|
||||
func (r *Registry) CreateOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Outbound, error) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
return r.UnsafeCreateOutbound(ctx, router, logger, tag, outboundType, options)
|
||||
}
|
||||
|
||||
func (r *Registry) UnsafeCreateOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Outbound, error) {
|
||||
constructor, loaded := r.constructors[outboundType]
|
||||
if !loaded {
|
||||
return nil, E.New("outbound type not found: " + outboundType)
|
||||
|
||||
51
adapter/provider.go
Normal file
51
adapter/provider.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
Type() string
|
||||
Tag() string
|
||||
Outbounds() []Outbound
|
||||
Outbound(tag string) (Outbound, bool)
|
||||
UpdatedAt() time.Time
|
||||
HealthCheck(ctx context.Context) (map[string]uint16, error)
|
||||
RegisterCallback(callback ProviderUpdateCallback) *list.Element[ProviderUpdateCallback]
|
||||
UnregisterCallback(element *list.Element[ProviderUpdateCallback])
|
||||
}
|
||||
|
||||
type ProviderUpdater interface {
|
||||
Update() error
|
||||
}
|
||||
|
||||
type ProviderSubscriptionInfo interface {
|
||||
SubscriptionInfo() SubscriptionInfo
|
||||
}
|
||||
|
||||
type ProviderRegistry interface {
|
||||
option.ProviderOptionsRegistry
|
||||
CreateProvider(ctx context.Context, router Router, logFactory log.Factory, tag string, providerType string, options any) (Provider, error)
|
||||
}
|
||||
|
||||
type ProviderManager interface {
|
||||
Lifecycle
|
||||
Providers() []Provider
|
||||
Get(tag string) (Provider, bool)
|
||||
Remove(tag string) error
|
||||
Create(ctx context.Context, router Router, logFactory log.Factory, tag string, providerType string, options any) error
|
||||
}
|
||||
|
||||
type SubscriptionInfo struct {
|
||||
Upload int64
|
||||
Download int64
|
||||
Total int64
|
||||
Expire int64
|
||||
}
|
||||
|
||||
type ProviderUpdateCallback = func(tag string) error
|
||||
267
adapter/provider/adapter.go
Normal file
267
adapter/provider/adapter.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
type Adapter struct {
|
||||
ctx context.Context
|
||||
outbound adapter.OutboundManager
|
||||
router adapter.Router
|
||||
logFactory log.Factory
|
||||
logger log.ContextLogger
|
||||
providerType string
|
||||
providerTag string
|
||||
outbounds []adapter.Outbound
|
||||
outboundsByTag map[string]adapter.Outbound
|
||||
ticker *time.Ticker
|
||||
checking atomic.Bool
|
||||
history adapter.URLTestHistoryStorage
|
||||
callbackAccess sync.Mutex
|
||||
callbacks list.List[adapter.ProviderUpdateCallback]
|
||||
|
||||
link string
|
||||
enabled bool
|
||||
timeout time.Duration
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func NewAdapter(ctx context.Context, router adapter.Router, outbound adapter.OutboundManager, logFactory log.Factory, logger log.ContextLogger, providerTag string, providerType string, options option.ProviderHealthCheckOptions) Adapter {
|
||||
timeout := time.Duration(options.Timeout)
|
||||
if timeout == 0 {
|
||||
timeout = 3 * time.Second
|
||||
}
|
||||
interval := time.Duration(options.Interval)
|
||||
if interval == 0 {
|
||||
interval = 10 * time.Minute
|
||||
}
|
||||
if interval < time.Minute {
|
||||
interval = time.Minute
|
||||
}
|
||||
return Adapter{
|
||||
ctx: ctx,
|
||||
outbound: outbound,
|
||||
router: router,
|
||||
logFactory: logFactory,
|
||||
logger: logger,
|
||||
providerType: providerType,
|
||||
providerTag: providerTag,
|
||||
|
||||
enabled: options.Enabled,
|
||||
link: options.URL,
|
||||
timeout: timeout,
|
||||
interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Adapter) Start() error {
|
||||
a.history = service.FromContext[adapter.URLTestHistoryStorage](a.ctx)
|
||||
if a.history == nil {
|
||||
if clashServer := service.FromContext[adapter.ClashServer](a.ctx); clashServer != nil {
|
||||
a.history = clashServer.HistoryStorage()
|
||||
} else {
|
||||
a.history = urltest.NewHistoryStorage()
|
||||
}
|
||||
}
|
||||
go a.loopCheck()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Adapter) Type() string {
|
||||
return a.providerType
|
||||
}
|
||||
|
||||
func (a *Adapter) Tag() string {
|
||||
return a.providerTag
|
||||
}
|
||||
|
||||
func (a *Adapter) Outbounds() []adapter.Outbound {
|
||||
return a.outbounds
|
||||
}
|
||||
|
||||
func (a *Adapter) Outbound(tag string) (adapter.Outbound, bool) {
|
||||
if a.outboundsByTag == nil {
|
||||
return nil, false
|
||||
}
|
||||
detour, ok := a.outboundsByTag[tag]
|
||||
return detour, ok
|
||||
}
|
||||
|
||||
func (a *Adapter) UpdateOutbounds(oldOpts []option.Outbound, newOpts []option.Outbound) {
|
||||
a.removeUseless(newOpts)
|
||||
var (
|
||||
oldOptByTag = make(map[string]option.Outbound)
|
||||
outbounds = make([]adapter.Outbound, 0, len(newOpts))
|
||||
outboundsByTag = make(map[string]adapter.Outbound)
|
||||
)
|
||||
for _, opt := range oldOpts {
|
||||
oldOptByTag[opt.Tag] = opt
|
||||
}
|
||||
for i, opt := range newOpts {
|
||||
var tag string
|
||||
if opt.Tag != "" {
|
||||
tag = F.ToString(a.providerTag, "/", opt.Tag)
|
||||
} else {
|
||||
tag = F.ToString(a.providerTag, "/", i)
|
||||
}
|
||||
outbound, exist := a.outbound.Outbound(tag)
|
||||
if !exist || !reflect.DeepEqual(opt, oldOptByTag[opt.Tag]) {
|
||||
err := a.outbound.Create(
|
||||
adapter.WithContext(a.ctx, &adapter.InboundContext{
|
||||
Outbound: tag,
|
||||
}),
|
||||
a.router,
|
||||
a.logFactory.NewLogger(F.ToString("outbound/", opt.Type, "[", tag, "]")),
|
||||
tag,
|
||||
opt.Type,
|
||||
opt.Options,
|
||||
)
|
||||
if err != nil {
|
||||
a.logger.Warn(err, " in ", tag, ", skip create this outbound")
|
||||
continue
|
||||
}
|
||||
outbound, _ = a.outbound.Outbound(tag)
|
||||
}
|
||||
outbounds = append(outbounds, outbound)
|
||||
outboundsByTag[tag] = outbound
|
||||
}
|
||||
if a.enabled && a.history != nil {
|
||||
go a.HealthCheck(a.ctx)
|
||||
}
|
||||
a.outbounds = outbounds
|
||||
a.outboundsByTag = outboundsByTag
|
||||
}
|
||||
|
||||
func (a *Adapter) HealthCheck(ctx context.Context) (map[string]uint16, error) {
|
||||
if a.ticker != nil {
|
||||
a.ticker.Reset(a.interval)
|
||||
}
|
||||
return a.healthcheck(ctx)
|
||||
}
|
||||
|
||||
func (a *Adapter) RegisterCallback(callback adapter.ProviderUpdateCallback) *list.Element[adapter.ProviderUpdateCallback] {
|
||||
a.callbackAccess.Lock()
|
||||
defer a.callbackAccess.Unlock()
|
||||
return a.callbacks.PushBack(callback)
|
||||
}
|
||||
|
||||
func (a *Adapter) UnregisterCallback(element *list.Element[adapter.ProviderUpdateCallback]) {
|
||||
a.callbackAccess.Lock()
|
||||
defer a.callbackAccess.Unlock()
|
||||
a.callbacks.Remove(element)
|
||||
}
|
||||
|
||||
func (a *Adapter) UpdateGroups() {
|
||||
for element := a.callbacks.Front(); element != nil; element = element.Next() {
|
||||
element.Value(a.providerTag)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Adapter) Close() error {
|
||||
if a.ticker != nil {
|
||||
a.ticker.Stop()
|
||||
}
|
||||
outbounds := a.outbounds
|
||||
a.outbounds = nil
|
||||
var err error
|
||||
for _, ob := range outbounds {
|
||||
if err2 := a.outbound.Remove(ob.Tag()); err2 != nil {
|
||||
err = E.Append(err, err2, func(err error) error {
|
||||
return E.Cause(err, "close outbound [", ob.Tag(), "]")
|
||||
})
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *Adapter) loopCheck() {
|
||||
if !a.enabled {
|
||||
return
|
||||
}
|
||||
a.ticker = time.NewTicker(a.interval)
|
||||
a.healthcheck(a.ctx)
|
||||
for {
|
||||
select {
|
||||
case <-a.ctx.Done():
|
||||
return
|
||||
case <-a.ticker.C:
|
||||
a.healthcheck(a.ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Adapter) healthcheck(ctx context.Context) (map[string]uint16, error) {
|
||||
result := make(map[string]uint16)
|
||||
if a.checking.Swap(true) {
|
||||
return result, nil
|
||||
}
|
||||
defer a.checking.Store(false)
|
||||
b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10))
|
||||
var resultAccess sync.Mutex
|
||||
checked := make(map[string]bool)
|
||||
for _, detour := range a.outbounds {
|
||||
tag := detour.Tag()
|
||||
if checked[tag] {
|
||||
continue
|
||||
}
|
||||
checked[tag] = true
|
||||
b.Go(tag, func() (any, error) {
|
||||
ctx, cancel := context.WithTimeout(a.ctx, a.timeout)
|
||||
defer cancel()
|
||||
t, err := urltest.URLTest(ctx, a.link, detour)
|
||||
if err != nil {
|
||||
a.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||
a.history.DeleteURLTestHistory(tag)
|
||||
} else {
|
||||
a.logger.Debug("outbound ", tag, " available: ", t, "ms")
|
||||
a.history.StoreURLTestHistory(tag, &adapter.URLTestHistory{
|
||||
Time: time.Now(),
|
||||
Delay: t,
|
||||
})
|
||||
resultAccess.Lock()
|
||||
result[tag] = t
|
||||
resultAccess.Unlock()
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
b.Wait()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *Adapter) removeUseless(newOpts []option.Outbound) {
|
||||
if len(a.outbounds) == 0 {
|
||||
return
|
||||
}
|
||||
exists := make(map[string]bool)
|
||||
for i, opt := range newOpts {
|
||||
var tag string
|
||||
if opt.Tag != "" {
|
||||
tag = F.ToString(a.providerTag, "/", opt.Tag)
|
||||
} else {
|
||||
tag = F.ToString(a.providerTag, "/", i)
|
||||
}
|
||||
exists[tag] = true
|
||||
}
|
||||
for _, opt := range a.outbounds {
|
||||
if !exists[opt.Tag()] {
|
||||
if err := a.outbound.Remove(opt.Tag()); err != nil {
|
||||
a.logger.Error(err, "close outbound [", opt.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
157
adapter/provider/manager.go
Normal file
157
adapter/provider/manager.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
var _ adapter.ProviderManager = (*Manager)(nil)
|
||||
|
||||
type Manager struct {
|
||||
logger log.ContextLogger
|
||||
registry adapter.ProviderRegistry
|
||||
access sync.Mutex
|
||||
started bool
|
||||
stage adapter.StartStage
|
||||
providers []adapter.Provider
|
||||
providerByTag map[string]adapter.Provider
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewManager(logger logger.ContextLogger, registry adapter.ProviderRegistry) *Manager {
|
||||
return &Manager{
|
||||
logger: logger,
|
||||
registry: registry,
|
||||
providerByTag: make(map[string]adapter.Provider),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Initialize() {
|
||||
}
|
||||
|
||||
func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
m.access.Lock()
|
||||
if m.started && m.stage >= stage {
|
||||
panic("already started")
|
||||
}
|
||||
m.started = true
|
||||
m.stage = stage
|
||||
providers := m.providers
|
||||
m.access.Unlock()
|
||||
for _, provider := range providers {
|
||||
err := adapter.LegacyStart(provider, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " provider/", provider.Type(), "[", provider.Tag(), "]")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
m.access.Lock()
|
||||
if !m.started {
|
||||
m.access.Unlock()
|
||||
return nil
|
||||
}
|
||||
m.started = false
|
||||
providers := m.providers
|
||||
m.providers = nil
|
||||
m.access.Unlock()
|
||||
var err error
|
||||
for _, provider := range providers {
|
||||
if closer, isCloser := provider.(io.Closer); isCloser {
|
||||
monitor.Start("close provider/", provider.Type(), "[", provider.Tag(), "]")
|
||||
err = E.Append(err, closer.Close(), func(err error) error {
|
||||
return E.Cause(err, "close provider/", provider.Type(), "[", provider.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Providers() []adapter.Provider {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
return m.providers
|
||||
}
|
||||
|
||||
func (m *Manager) Get(tag string) (adapter.Provider, bool) {
|
||||
m.access.Lock()
|
||||
provider, found := m.providerByTag[tag]
|
||||
m.access.Unlock()
|
||||
return provider, found
|
||||
}
|
||||
|
||||
func (m *Manager) Remove(tag string) error {
|
||||
m.access.Lock()
|
||||
provider, found := m.providerByTag[tag]
|
||||
if !found {
|
||||
m.access.Unlock()
|
||||
return os.ErrInvalid
|
||||
}
|
||||
delete(m.providerByTag, tag)
|
||||
index := common.Index(m.providers, func(it adapter.Provider) bool {
|
||||
return it == provider
|
||||
})
|
||||
if index == -1 {
|
||||
panic("invalid provider index")
|
||||
}
|
||||
m.providers = append(m.providers[:index], m.providers[index+1:]...)
|
||||
started := m.started
|
||||
m.access.Unlock()
|
||||
if started {
|
||||
return common.Close(provider)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Create(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, providerType string, options any) error {
|
||||
if tag == "" {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
provider, err := m.registry.CreateProvider(ctx, router, logFactory, tag, providerType, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
err = adapter.LegacyStart(provider, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " provider/", provider.Type(), "[", provider.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
if existsProvider, loaded := m.providerByTag[tag]; loaded {
|
||||
if m.started {
|
||||
err = common.Close(existsProvider)
|
||||
if err != nil {
|
||||
return E.Cause(err, "close provider", provider.Type(), "[", existsProvider.Tag(), "]")
|
||||
}
|
||||
}
|
||||
existsIndex := common.Index(m.providers, func(it adapter.Provider) bool {
|
||||
return it == existsProvider
|
||||
})
|
||||
if existsIndex == -1 {
|
||||
panic("invalid provider index")
|
||||
}
|
||||
m.providers = append(m.providers[:existsIndex], m.providers[existsIndex+1:]...)
|
||||
}
|
||||
m.providers = append(m.providers, provider)
|
||||
m.providerByTag[tag] = provider
|
||||
return nil
|
||||
}
|
||||
72
adapter/provider/registry.go
Normal file
72
adapter/provider/registry.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type ConstructorFunc[T any] func(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, options T) (adapter.Provider, error)
|
||||
|
||||
func Register[Options any](registry *Registry, providerType string, constructor ConstructorFunc[Options]) {
|
||||
registry.register(providerType, func() any {
|
||||
return new(Options)
|
||||
}, func(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, rawOptions any) (adapter.Provider, error) {
|
||||
var options *Options
|
||||
if rawOptions != nil {
|
||||
options = rawOptions.(*Options)
|
||||
}
|
||||
return constructor(ctx, router, logFactory, tag, common.PtrValueOrDefault(options))
|
||||
})
|
||||
}
|
||||
|
||||
var _ adapter.ProviderRegistry = (*Registry)(nil)
|
||||
|
||||
type (
|
||||
optionsConstructorFunc func() any
|
||||
constructorFunc func(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, options any) (adapter.Provider, error)
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
access sync.Mutex
|
||||
optionsType map[string]optionsConstructorFunc
|
||||
constructors map[string]constructorFunc
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
optionsType: make(map[string]optionsConstructorFunc),
|
||||
constructors: make(map[string]constructorFunc),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) CreateOptions(providerType string) (any, bool) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
optionsConstructor, loaded := r.optionsType[providerType]
|
||||
if !loaded {
|
||||
return nil, false
|
||||
}
|
||||
return optionsConstructor(), true
|
||||
}
|
||||
|
||||
func (r *Registry) CreateProvider(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, providerType string, options any) (adapter.Provider, error) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
constructor, loaded := r.constructors[providerType]
|
||||
if !loaded {
|
||||
return nil, E.New("provider type not found: '" + providerType + "'")
|
||||
}
|
||||
return constructor(ctx, router, logFactory, tag, options)
|
||||
}
|
||||
|
||||
func (r *Registry) register(providerType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
r.optionsType[providerType] = optionsConstructor
|
||||
r.constructors[providerType] = constructor
|
||||
}
|
||||
48
box.go
48
box.go
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/adapter/provider"
|
||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||
"github.com/sagernet/sing-box/common/certificate"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
@@ -44,6 +45,7 @@ type Box struct {
|
||||
endpoint *endpoint.Manager
|
||||
inbound *inbound.Manager
|
||||
outbound *outbound.Manager
|
||||
provider *provider.Manager
|
||||
service *boxService.Manager
|
||||
dnsTransport *dns.TransportManager
|
||||
dnsRouter *dns.Router
|
||||
@@ -64,6 +66,7 @@ func Context(
|
||||
inboundRegistry adapter.InboundRegistry,
|
||||
outboundRegistry adapter.OutboundRegistry,
|
||||
endpointRegistry adapter.EndpointRegistry,
|
||||
providerRegistry adapter.ProviderRegistry,
|
||||
dnsTransportRegistry adapter.DNSTransportRegistry,
|
||||
serviceRegistry adapter.ServiceRegistry,
|
||||
) context.Context {
|
||||
@@ -82,6 +85,11 @@ func Context(
|
||||
ctx = service.ContextWith[option.EndpointOptionsRegistry](ctx, endpointRegistry)
|
||||
ctx = service.ContextWith[adapter.EndpointRegistry](ctx, endpointRegistry)
|
||||
}
|
||||
if service.FromContext[option.ProviderOptionsRegistry](ctx) == nil ||
|
||||
service.FromContext[adapter.ProviderRegistry](ctx) == nil {
|
||||
ctx = service.ContextWith[option.ProviderOptionsRegistry](ctx, providerRegistry)
|
||||
ctx = service.ContextWith[adapter.ProviderRegistry](ctx, providerRegistry)
|
||||
}
|
||||
if service.FromContext[adapter.DNSTransportRegistry](ctx) == nil {
|
||||
ctx = service.ContextWith[option.DNSTransportOptionsRegistry](ctx, dnsTransportRegistry)
|
||||
ctx = service.ContextWith[adapter.DNSTransportRegistry](ctx, dnsTransportRegistry)
|
||||
@@ -104,6 +112,7 @@ func New(options Options) (*Box, error) {
|
||||
endpointRegistry := service.FromContext[adapter.EndpointRegistry](ctx)
|
||||
inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
|
||||
outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
|
||||
providerRegistry := service.FromContext[adapter.ProviderRegistry](ctx)
|
||||
dnsTransportRegistry := service.FromContext[adapter.DNSTransportRegistry](ctx)
|
||||
serviceRegistry := service.FromContext[adapter.ServiceRegistry](ctx)
|
||||
|
||||
@@ -116,6 +125,9 @@ func New(options Options) (*Box, error) {
|
||||
if outboundRegistry == nil {
|
||||
return nil, E.New("missing outbound registry in context")
|
||||
}
|
||||
if providerRegistry == nil {
|
||||
return nil, E.New("missing provider registry in context")
|
||||
}
|
||||
if dnsTransportRegistry == nil {
|
||||
return nil, E.New("missing DNS transport registry in context")
|
||||
}
|
||||
@@ -160,6 +172,7 @@ func New(options Options) (*Box, error) {
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create log factory")
|
||||
}
|
||||
service.MustRegister[log.Factory](ctx, logFactory)
|
||||
|
||||
var internalServices []adapter.LifecycleService
|
||||
certificateOptions := common.PtrValueOrDefault(options.Certificate)
|
||||
@@ -180,11 +193,13 @@ func New(options Options) (*Box, error) {
|
||||
endpointManager := endpoint.NewManager(logFactory.NewLogger("endpoint"), endpointRegistry)
|
||||
inboundManager := inbound.NewManager(logFactory.NewLogger("inbound"), inboundRegistry, endpointManager)
|
||||
outboundManager := outbound.NewManager(logFactory.NewLogger("outbound"), outboundRegistry, endpointManager, routeOptions.Final)
|
||||
providerManager := provider.NewManager(logFactory.NewLogger("provider"), providerRegistry)
|
||||
dnsTransportManager := dns.NewTransportManager(logFactory.NewLogger("dns/transport"), dnsTransportRegistry, outboundManager, dnsOptions.Final)
|
||||
serviceManager := boxService.NewManager(logFactory.NewLogger("service"), serviceRegistry)
|
||||
service.MustRegister[adapter.EndpointManager](ctx, endpointManager)
|
||||
service.MustRegister[adapter.InboundManager](ctx, inboundManager)
|
||||
service.MustRegister[adapter.OutboundManager](ctx, outboundManager)
|
||||
service.MustRegister[adapter.ProviderManager](ctx, providerManager)
|
||||
service.MustRegister[adapter.DNSTransportManager](ctx, dnsTransportManager)
|
||||
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
|
||||
dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions)
|
||||
@@ -275,6 +290,10 @@ func New(options Options) (*Box, error) {
|
||||
return nil, E.Cause(err, "initialize inbound[", i, "]")
|
||||
}
|
||||
}
|
||||
options.Outbounds = append(options.Outbounds, option.Outbound{
|
||||
Tag: "Compatible",
|
||||
Type: C.TypeDirect,
|
||||
})
|
||||
for i, outboundOptions := range options.Outbounds {
|
||||
var tag string
|
||||
if outboundOptions.Tag != "" {
|
||||
@@ -301,6 +320,25 @@ func New(options Options) (*Box, error) {
|
||||
return nil, E.Cause(err, "initialize outbound[", i, "]")
|
||||
}
|
||||
}
|
||||
for i, providerOptions := range options.Providers {
|
||||
var tag string
|
||||
if providerOptions.Tag != "" {
|
||||
tag = providerOptions.Tag
|
||||
} else {
|
||||
tag = F.ToString(i)
|
||||
}
|
||||
err = providerManager.Create(
|
||||
ctx,
|
||||
router,
|
||||
logFactory,
|
||||
tag,
|
||||
providerOptions.Type,
|
||||
providerOptions.Options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize provider[", i, "]")
|
||||
}
|
||||
}
|
||||
for i, serviceOptions := range options.Services {
|
||||
var tag string
|
||||
if serviceOptions.Tag != "" {
|
||||
@@ -391,6 +429,7 @@ func New(options Options) (*Box, error) {
|
||||
endpoint: endpointManager,
|
||||
inbound: inboundManager,
|
||||
outbound: outboundManager,
|
||||
provider: providerManager,
|
||||
dnsTransport: dnsTransportManager,
|
||||
service: serviceManager,
|
||||
dnsRouter: dnsRouter,
|
||||
@@ -454,11 +493,11 @@ func (s *Box) preStart() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(s.logger, adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.provider, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
||||
err = adapter.Start(s.logger, adapter.StartStateStart, s.outbound, s.provider, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -478,7 +517,7 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(s.logger, adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.provider, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -486,7 +525,7 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(s.logger, adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.provider, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -512,6 +551,7 @@ func (s *Box) Close() error {
|
||||
{"service", s.service},
|
||||
{"endpoint", s.endpoint},
|
||||
{"inbound", s.inbound},
|
||||
{"provider", s.provider},
|
||||
{"outbound", s.outbound},
|
||||
{"router", s.router},
|
||||
{"connection", s.connection},
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build with_quic
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type CloudflareApi struct {
|
||||
@@ -25,50 +22,93 @@ func NewCloudflareApi(opts ...CloudflareApiOption) *CloudflareApi {
|
||||
}
|
||||
|
||||
func (api *CloudflareApi) CreateProfile(ctx context.Context, publicKey string) (*CloudflareProfile, error) {
|
||||
request, err := http.NewRequest("POST", "https://api.cloudflareclient.com/v0i1909051800/reg", strings.NewReader(
|
||||
fmt.Sprintf(
|
||||
"{\"install_id\":\"\",\"tos\":\"%s\",\"key\":\"%s\",\"fcm_token\":\"\",\"type\":\"ios\",\"locale\":\"en_US\"}",
|
||||
time.Now().Format("2006-01-02T15:04:05.000Z"),
|
||||
publicKey,
|
||||
),
|
||||
))
|
||||
serial, err := GenerateRandomAndroidSerial()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate serial: %v", err)
|
||||
}
|
||||
data := Registration{
|
||||
Key: publicKey,
|
||||
InstallID: "",
|
||||
FcmToken: "",
|
||||
Tos: TimeAsCfString(time.Now()),
|
||||
Model: "PC",
|
||||
Serial: serial,
|
||||
OsVersion: "",
|
||||
KeyType: KeyTypeWg,
|
||||
TunType: TunTypeWg,
|
||||
Locale: "en-US",
|
||||
}
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal json: %v", err)
|
||||
}
|
||||
request, err := http.NewRequest("POST", ApiUrl+"/"+ApiVersion+"/reg", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range Headers {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
response, err := api.client.Do(request.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("status code is not 200")
|
||||
}
|
||||
content, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to register: %v", response.StatusCode)
|
||||
}
|
||||
profile := new(CloudflareProfile)
|
||||
return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile)
|
||||
return profile, json.NewDecoder(response.Body).Decode(profile)
|
||||
}
|
||||
|
||||
func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) {
|
||||
request, err := http.NewRequest("GET", "https://api.cloudflareclient.com/v0i1909051800/reg/"+id, nil)
|
||||
func (api *CloudflareApi) EnrollKey(ctx context.Context, authToken string, id string, keyType, tunType, publicKey string) (*CloudflareProfile, error) {
|
||||
deviceUpdate := DeviceUpdate{
|
||||
Name: "PC",
|
||||
Key: publicKey,
|
||||
KeyType: keyType,
|
||||
TunType: tunType,
|
||||
}
|
||||
jsonData, err := json.Marshal(deviceUpdate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal json: %v", err)
|
||||
}
|
||||
request, err := http.NewRequest("PATCH", ApiUrl+"/"+ApiVersion+"/reg/"+id, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range Headers {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
request.Header.Set("Authorization", "Bearer "+authToken)
|
||||
response, err := api.client.Do(request.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("status code is not 200")
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to enroll key: %v", response.StatusCode)
|
||||
}
|
||||
content, err := io.ReadAll(response.Body)
|
||||
profile := new(CloudflareProfile)
|
||||
return profile, json.NewDecoder(response.Body).Decode(profile)
|
||||
}
|
||||
|
||||
func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) {
|
||||
request, err := http.NewRequest("GET", ApiUrl+"/"+ApiVersion+"/reg/"+id, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range Headers {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
request.Header.Set("Authorization", "Bearer "+authToken)
|
||||
response, err := api.client.Do(request.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to get profile: %v", response.StatusCode)
|
||||
}
|
||||
profile := new(CloudflareProfile)
|
||||
return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile)
|
||||
return profile, json.NewDecoder(response.Body).Decode(profile)
|
||||
}
|
||||
|
||||
25
common/cloudflare/constant.go
Normal file
25
common/cloudflare/constant.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package cloudflare
|
||||
|
||||
const (
|
||||
ApiUrl = "https://api.cloudflareclient.com"
|
||||
ApiVersion = "v0a4471"
|
||||
ConnectSNI = "consumer-masque.cloudflareclient.com"
|
||||
// unused for now
|
||||
ZeroTierSNI = "zt-masque.cloudflareclient.com"
|
||||
ConnectURI = "https://cloudflareaccess.com"
|
||||
DefaultModel = "PC"
|
||||
KeyTypeWg = "curve25519"
|
||||
TunTypeWg = "wireguard"
|
||||
KeyTypeMasque = "secp256r1"
|
||||
TunTypeMasque = "masque"
|
||||
DefaultLocale = "en_US"
|
||||
DefaultEndpointH2V4 = "162.159.198.2"
|
||||
DefaultEndpointH2V6 = ""
|
||||
)
|
||||
|
||||
var Headers = map[string]string{
|
||||
"User-Agent": "WARP for Android",
|
||||
"CF-Client-Version": "a-6.35-4471",
|
||||
"Content-Type": "application/json; charset=UTF-8",
|
||||
"Connection": "Keep-Alive",
|
||||
}
|
||||
132
common/cloudflare/models.go
Normal file
132
common/cloudflare/models.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Registration struct {
|
||||
Key string `json:"key"`
|
||||
InstallID string `json:"install_id"`
|
||||
FcmToken string `json:"fcm_token"`
|
||||
Tos string `json:"tos"`
|
||||
Model string `json:"model"`
|
||||
Serial string `json:"serial_number"`
|
||||
OsVersion string `json:"os_version"`
|
||||
KeyType string `json:"key_type"`
|
||||
TunType string `json:"tunnel_type"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
type CloudflareProfile struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Model string `json:"model"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
KeyType string `json:"key_type"`
|
||||
TunType string `json:"tunnel_type"`
|
||||
Account Account `json:"account"`
|
||||
Config Config `json:"config"`
|
||||
// WarpEnabled not set for ZeroTier
|
||||
WarpEnabled bool `json:"warp_enabled,omitempty"`
|
||||
// Waitlist not set for ZeroTier
|
||||
Waitlist bool `json:"waitlist_enabled,omitempty"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
// Tos not set for ZeroTier
|
||||
Tos string `json:"tos,omitempty"`
|
||||
// Place not set for ZeroTier
|
||||
Place int `json:"place,omitempty"`
|
||||
Locale string `json:"locale"`
|
||||
// Enabled not set for ZeroTier
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
InstallID string `json:"install_id"`
|
||||
// Token only set for /reg call
|
||||
Token string `json:"token,omitempty"`
|
||||
FcmToken string `json:"fcm_token"`
|
||||
// SerialNumber not set for ZeroTier
|
||||
SerialNumber string `json:"serial_number,omitempty"`
|
||||
Policy Policy `json:"policy"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
ID string `json:"id"`
|
||||
AccountType string `json:"account_type"`
|
||||
// Created not set for ZeroTier
|
||||
Created string `json:"created,omitempty"`
|
||||
// Updated not set for ZeroTier
|
||||
Updated string `json:"updated,omitempty"`
|
||||
// Managed only set for ZeroTier
|
||||
Managed string `json:"managed,omitempty"`
|
||||
// Organization only set for ZeroTier
|
||||
Organization string `json:"organization,omitempty"`
|
||||
// PremiumData not set for ZeroTier
|
||||
PremiumData int `json:"premium_data,omitempty"`
|
||||
// Quota not set for ZeroTier
|
||||
Quota int `json:"quota,omitempty"`
|
||||
// WarpPlus not set for ZeroTier
|
||||
WarpPlus bool `json:"warp_plus,omitempty"`
|
||||
// ReferralCode not set for ZeroTier
|
||||
ReferralCount int `json:"referral_count,omitempty"`
|
||||
// ReferralRenewalCount not set for ZeroTier
|
||||
ReferralRenewalCount int `json:"referral_renewal_countdown,omitempty"`
|
||||
// Role not set for ZeroTier
|
||||
Role string `json:"role,omitempty"`
|
||||
// License not set for ZeroTier
|
||||
License string `json:"license,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ClientID string `json:"client_id"`
|
||||
Peers []Peer `json:"peers"`
|
||||
Interface struct {
|
||||
Addresses struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
} `json:"addresses"`
|
||||
} `json:"interface"`
|
||||
Services struct {
|
||||
HTTPProxy string `json:"http_proxy"`
|
||||
} `json:"services"`
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
Endpoint struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
Host string `json:"host"`
|
||||
Ports []int `json:"ports"`
|
||||
} `json:"endpoint"`
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
TunnelProtocol string `json:"tunnel_protocol"`
|
||||
}
|
||||
|
||||
type DeviceUpdate struct {
|
||||
Key string `json:"key"`
|
||||
KeyType string `json:"key_type"`
|
||||
TunType string `json:"tunnel_type"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type APIError struct {
|
||||
Result interface{} `json:"result"`
|
||||
Success bool `json:"success"`
|
||||
Errors []ErrorInfo `json:"errors"`
|
||||
Messages []string `json:"messages"`
|
||||
}
|
||||
|
||||
type ErrorInfo struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *APIError) Error() string {
|
||||
errors := make([]string, len(e.Errors))
|
||||
for i, err := range e.Errors {
|
||||
errors[i] = err.Message
|
||||
}
|
||||
return strings.Join(errors, ",")
|
||||
}
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CloudflareApiOption func(api *CloudflareApi)
|
||||
|
||||
func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) CloudflareApiOption {
|
||||
return func(api *CloudflareApi) {
|
||||
api.client.Timeout = 30 * time.Second
|
||||
api.client.Transport = &http.Transport{
|
||||
DialContext: dialContext,
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package cloudflare
|
||||
|
||||
import "time"
|
||||
|
||||
type CloudflareProfile struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
Account struct {
|
||||
ID string `json:"id"`
|
||||
AccountType string `json:"account_type"`
|
||||
Created time.Time `json:"created"`
|
||||
Updated time.Time `json:"updated"`
|
||||
PremiumData int `json:"premium_data"`
|
||||
Quota int `json:"quota"`
|
||||
Usage int `json:"usage"`
|
||||
WARPPlus bool `json:"warp_plus"`
|
||||
ReferralCount int `json:"referral_count"`
|
||||
ReferralRenewalCountdown int `json:"referral_renewal_countdown"`
|
||||
Role string `json:"role"`
|
||||
License string `json:"license"`
|
||||
TTL time.Time `json:"ttl"`
|
||||
} `json:"account"`
|
||||
Config struct {
|
||||
ClientID string `json:"client_id"`
|
||||
Interface struct {
|
||||
Addresses struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
} `json:"addresses"`
|
||||
} `json:"interface"`
|
||||
Peers []struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
Endpoint struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
Host string `json:"host"`
|
||||
Ports []int `json:"ports"`
|
||||
} `json:"endpoint"`
|
||||
} `json:"peers"`
|
||||
Services struct {
|
||||
HTTPProxy string `json:"http_proxy"`
|
||||
} `json:"services"`
|
||||
Metrics struct {
|
||||
Ping int `json:"ping"`
|
||||
Report int `json:"report"`
|
||||
} `json:"metrics"`
|
||||
} `json:"config"`
|
||||
Token string `json:"token"`
|
||||
WARPEnabled bool `json:"warp_enabled"`
|
||||
WaitlistEnabled bool `json:"waitlist_enabled"`
|
||||
Created time.Time `json:"created"`
|
||||
Updated time.Time `json:"updated"`
|
||||
Tos time.Time `json:"tos"`
|
||||
Place int `json:"place"`
|
||||
Locale string `json:"locale"`
|
||||
Enabled bool `json:"enabled"`
|
||||
InstallID string `json:"install_id"`
|
||||
FcmToken string `json:"fcm_token"`
|
||||
Policy struct {
|
||||
TunnelProtocol string `json:"tunnel_protocol"`
|
||||
} `json:"policy"`
|
||||
}
|
||||
19
common/cloudflare/utils.go
Normal file
19
common/cloudflare/utils.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateRandomAndroidSerial() (string, error) {
|
||||
serial := make([]byte, 8)
|
||||
if _, err := rand.Read(serial); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(serial), nil
|
||||
}
|
||||
|
||||
func TimeAsCfString(t time.Time) string {
|
||||
return t.Format("2006-01-02T15:04:05.000-07:00")
|
||||
}
|
||||
@@ -11,3 +11,13 @@ func ContextWithIsExternalConnection(ctx context.Context) context.Context {
|
||||
func IsExternalConnectionFromContext(ctx context.Context) bool {
|
||||
return ctx.Value(contextKeyIsExternalConnection{}) != nil
|
||||
}
|
||||
|
||||
type contextKeyIsProviderConnection struct{}
|
||||
|
||||
func ContextWithIsProviderConnection(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, contextKeyIsProviderConnection{}, true)
|
||||
}
|
||||
|
||||
func IsProviderConnectionFromContext(ctx context.Context) bool {
|
||||
return ctx.Value(contextKeyIsProviderConnection{}) != nil
|
||||
}
|
||||
|
||||
@@ -17,30 +17,31 @@ type Group struct {
|
||||
type groupConnItem struct {
|
||||
conn io.Closer
|
||||
isExternal bool
|
||||
isProvider bool
|
||||
}
|
||||
|
||||
func NewGroup() *Group {
|
||||
return &Group{}
|
||||
}
|
||||
|
||||
func (g *Group) NewConn(conn net.Conn, isExternal bool) net.Conn {
|
||||
func (g *Group) NewConn(conn net.Conn, isExternal bool, isProvider bool) net.Conn {
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal, isProvider})
|
||||
return &Conn{Conn: conn, group: g, element: item}
|
||||
}
|
||||
|
||||
func (g *Group) NewPacketConn(conn net.PacketConn, isExternal bool) net.PacketConn {
|
||||
func (g *Group) NewPacketConn(conn net.PacketConn, isExternal bool, isProvider bool) net.PacketConn {
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal, isProvider})
|
||||
return &PacketConn{PacketConn: conn, group: g, element: item}
|
||||
}
|
||||
|
||||
func (g *Group) NewSingPacketConn(conn N.PacketConn, isExternal bool) N.PacketConn {
|
||||
func (g *Group) NewSingPacketConn(conn N.PacketConn, isExternal bool, isProvider bool) N.PacketConn {
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal})
|
||||
item := g.connections.PushBack(&groupConnItem{conn, isExternal, isProvider})
|
||||
return &SingPacketConn{PacketConn: conn, group: g, element: item}
|
||||
}
|
||||
|
||||
|
||||
54
common/kmutex/mutex.go
Normal file
54
common/kmutex/mutex.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package kmutex
|
||||
|
||||
import "sync"
|
||||
|
||||
type Kmutex[T comparable] struct {
|
||||
l sync.Locker
|
||||
s map[T]*klock
|
||||
}
|
||||
|
||||
type klock struct {
|
||||
cond *sync.Cond
|
||||
ref uint64
|
||||
}
|
||||
|
||||
func New[T comparable]() *Kmutex[T] {
|
||||
l := sync.Mutex{}
|
||||
return &Kmutex[T]{
|
||||
l: &l,
|
||||
s: make(map[T]*klock),
|
||||
}
|
||||
}
|
||||
|
||||
func (km *Kmutex[T]) Unlock(key T) {
|
||||
km.l.Lock()
|
||||
defer km.l.Unlock()
|
||||
kl, ok := km.s[key]
|
||||
if !ok || kl.ref == 0 {
|
||||
panic("unlock of unlocked kmutex")
|
||||
}
|
||||
kl.ref--
|
||||
if kl.ref == 0 {
|
||||
delete(km.s, key)
|
||||
return
|
||||
}
|
||||
kl.cond.Signal()
|
||||
}
|
||||
|
||||
func (km *Kmutex[T]) Lock(key T) {
|
||||
km.l.Lock()
|
||||
defer km.l.Unlock()
|
||||
for {
|
||||
kl, ok := km.s[key]
|
||||
if !ok {
|
||||
km.s[key] = &klock{
|
||||
cond: sync.NewCond(km.l),
|
||||
ref: 1,
|
||||
}
|
||||
return
|
||||
}
|
||||
kl.ref++
|
||||
kl.cond.Wait()
|
||||
return
|
||||
}
|
||||
}
|
||||
96
common/kmutex/mutex_test.go
Normal file
96
common/kmutex/mutex_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package kmutex
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Number of unique resources to access
|
||||
const number = 100
|
||||
|
||||
func makeIds(count int) []int {
|
||||
ids := make([]int, count)
|
||||
for i := 0; i < count; i++ {
|
||||
ids[i] = i
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func TestKmutex(t *testing.T) {
|
||||
km := New[int]()
|
||||
ids := makeIds(number)
|
||||
resources := make([]int, number)
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
lc := make(chan int)
|
||||
uc := make(chan int)
|
||||
// Start 10n goroutines accessing n resources 10 times each
|
||||
for i := 0; i < 10*number; i++ {
|
||||
wg.Add(1)
|
||||
go func(k int) {
|
||||
for j := 0; j < 10; j++ {
|
||||
lc <- k
|
||||
km.Lock(ids[k])
|
||||
// read and write resource to check for race
|
||||
resources[k] = resources[k] + 1
|
||||
km.Unlock(ids[k])
|
||||
uc <- k
|
||||
}
|
||||
wg.Done()
|
||||
}(i % len(ids))
|
||||
}
|
||||
|
||||
to := time.After(time.Second)
|
||||
counts := make(map[int]int)
|
||||
var lCount, ulCount int
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case k := <-lc:
|
||||
counts[k] = counts[k] + 1
|
||||
lCount++
|
||||
case k := <-uc:
|
||||
counts[k] = counts[k] - 1
|
||||
ulCount++
|
||||
case <-to:
|
||||
t.Fatal("timed out waiting for results")
|
||||
break loop
|
||||
}
|
||||
expectCount := 100 * number
|
||||
if lCount == expectCount && ulCount == expectCount {
|
||||
// Have all results
|
||||
break
|
||||
}
|
||||
}
|
||||
for k, c := range counts {
|
||||
if c != 0 {
|
||||
t.Errorf("Key %d count != 0: %d\n", k, c)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkKmutex1000(b *testing.B) {
|
||||
km := New[int]()
|
||||
ids := makeIds(number)
|
||||
resources := make([]int, number)
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
// Start 1000 goroutines accessing 100 resources N times each
|
||||
b.ResetTimer()
|
||||
for i := 0; i < 1000; i++ {
|
||||
wg.Add(1)
|
||||
go func(k int) {
|
||||
for j := 0; j < b.N; j++ {
|
||||
km.Lock(ids[k])
|
||||
// read and write resource to check for race
|
||||
resources[k] = resources[k] + 1
|
||||
km.Unlock(ids[k])
|
||||
}
|
||||
wg.Done()
|
||||
}(i % len(ids))
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
98
common/migrate/source/raw.go
Normal file
98
common/migrate/source/raw.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package source
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4/source"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type RawDriver struct {
|
||||
migrations *source.Migrations
|
||||
rawMigrations map[string]string
|
||||
}
|
||||
|
||||
func NewRawDriver(rawMigrations map[string]string) *RawDriver {
|
||||
return &RawDriver{rawMigrations: rawMigrations}
|
||||
}
|
||||
|
||||
func (d *RawDriver) Init() error {
|
||||
ms := source.NewMigrations()
|
||||
for key := range d.rawMigrations {
|
||||
m, err := source.DefaultParse(key)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !ms.Append(m) {
|
||||
return source.ErrDuplicateMigration{
|
||||
Migration: *m,
|
||||
}
|
||||
}
|
||||
}
|
||||
d.migrations = ms
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RawDriver) Open(url string) (source.Driver, error) {
|
||||
return nil, E.New("open() cannot be called")
|
||||
}
|
||||
|
||||
func (d *RawDriver) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RawDriver) First() (version uint, err error) {
|
||||
if version, ok := d.migrations.First(); ok {
|
||||
return version, nil
|
||||
}
|
||||
return 0, &fs.PathError{
|
||||
Op: "first",
|
||||
Err: fs.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *RawDriver) Prev(version uint) (prevVersion uint, err error) {
|
||||
if version, ok := d.migrations.Prev(version); ok {
|
||||
return version, nil
|
||||
}
|
||||
return 0, &fs.PathError{
|
||||
Op: "prev for version " + strconv.FormatUint(uint64(version), 10),
|
||||
Err: fs.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *RawDriver) Next(version uint) (nextVersion uint, err error) {
|
||||
if version, ok := d.migrations.Next(version); ok {
|
||||
return version, nil
|
||||
}
|
||||
return 0, &fs.PathError{
|
||||
Op: "next for version " + strconv.FormatUint(uint64(version), 10),
|
||||
Err: fs.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *RawDriver) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
|
||||
if m, ok := d.migrations.Up(version); ok {
|
||||
body := ioutil.NopCloser(strings.NewReader(d.rawMigrations[m.Raw]))
|
||||
return body, m.Identifier, nil
|
||||
}
|
||||
return nil, "", &fs.PathError{
|
||||
Op: "read up for version " + strconv.FormatUint(uint64(version), 10),
|
||||
Err: fs.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *RawDriver) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
|
||||
if m, ok := d.migrations.Down(version); ok {
|
||||
body := ioutil.NopCloser(strings.NewReader(d.rawMigrations[m.Raw]))
|
||||
return body, m.Identifier, nil
|
||||
}
|
||||
return nil, "", &fs.PathError{
|
||||
Op: "read down for version " + strconv.FormatUint(uint64(version), 10),
|
||||
Err: fs.ErrNotExist,
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,9 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
|
||||
}
|
||||
}
|
||||
service, err := mux.NewService(mux.ServiceOptions{
|
||||
NewConnectionContext: func(ctx context.Context, conn net.Conn) context.Context {
|
||||
return log.ContextWithNewMuxID(ctx)
|
||||
},
|
||||
NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context {
|
||||
return log.ContextWithNewID(ctx)
|
||||
},
|
||||
|
||||
74
common/tls/masque_client.go
Normal file
74
common/tls/masque_client.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
func NewMASQUEClient(ctx context.Context, logger logger.ContextLogger, serverName string, cert [][]byte, privateKey *ecdsa.PrivateKey, peerPublicKey *ecdsa.PublicKey, options option.MASQUEOutboundTLSOptions) (Config, error) {
|
||||
var tlsConfig tls.Config
|
||||
tlsConfig.ServerName = serverName
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
tlsConfig.NextProtos = []string{http3.NextProtoH3}
|
||||
tlsConfig.Certificates = []tls.Certificate{
|
||||
{
|
||||
Certificate: cert,
|
||||
PrivateKey: privateKey,
|
||||
},
|
||||
}
|
||||
if options.CipherSuites != nil {
|
||||
find:
|
||||
for _, cipherSuite := range options.CipherSuites {
|
||||
for _, tlsCipherSuite := range tls.CipherSuites() {
|
||||
if cipherSuite == tlsCipherSuite.Name {
|
||||
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
|
||||
continue find
|
||||
}
|
||||
}
|
||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||
}
|
||||
}
|
||||
for _, curve := range options.CurvePreferences {
|
||||
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curve))
|
||||
}
|
||||
if !options.Insecure {
|
||||
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
if len(rawCerts) == 0 {
|
||||
return nil
|
||||
}
|
||||
cert, err := x509.ParseCertificate(rawCerts[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok {
|
||||
return x509.ErrUnsupportedAlgorithm
|
||||
}
|
||||
if !cert.PublicKey.(*ecdsa.PublicKey).Equal(peerPublicKey) {
|
||||
return x509.CertificateInvalidError{Cert: cert, Reason: 10, Detail: "remote endpoint has a different public key than what we trust in config.json"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||
if options.KernelRx || options.KernelTx {
|
||||
if !C.IsLinux {
|
||||
return nil, E.New("kTLS is only supported on Linux")
|
||||
}
|
||||
config = &KTLSClientConfig{
|
||||
Config: config,
|
||||
logger: logger,
|
||||
kernelTx: options.KernelTx,
|
||||
kernelRx: options.KernelRx,
|
||||
}
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
68
common/utils.go
Normal file
68
common/utils.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
func StringToType[T any](str string) T {
|
||||
var value T
|
||||
v := reflect.ValueOf(&value).Elem()
|
||||
switch any(value).(type) {
|
||||
case badoption.Duration:
|
||||
d, err := time.ParseDuration(str)
|
||||
if err != nil {
|
||||
v.SetInt(StringToType[int64](str))
|
||||
} else {
|
||||
v.Set(reflect.ValueOf(d))
|
||||
}
|
||||
return value
|
||||
case badoption.HTTPHeader:
|
||||
headers := badoption.HTTPHeader{}
|
||||
reg := regexp.MustCompile(`^[ \t]*?(\S+?):[ \t]*?(\S+?)[ \t]*?$`)
|
||||
for _, header := range strings.Split(str, "\n") {
|
||||
result := reg.FindStringSubmatch(header)
|
||||
if result != nil {
|
||||
key := result[1]
|
||||
headers[key] = strings.Split(result[2], ",")
|
||||
}
|
||||
}
|
||||
v.Set(reflect.ValueOf(headers))
|
||||
return value
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i, _ := strconv.ParseInt(str, 10, 64)
|
||||
v.SetInt(i)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
i, _ := strconv.ParseUint(str, 10, 64)
|
||||
v.SetUint(i)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
f, _ := strconv.ParseFloat(str, 64)
|
||||
v.SetFloat(f)
|
||||
case reflect.Bool:
|
||||
b, _ := strconv.ParseBool(str)
|
||||
v.SetBool(b)
|
||||
default:
|
||||
panic("unsupported type")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func DecodeBase64URLSafe(content string) (string, error) {
|
||||
s := strings.ReplaceAll(content, " ", "-")
|
||||
s = strings.ReplaceAll(s, "/", "_")
|
||||
s = strings.ReplaceAll(s, "+", "-")
|
||||
s = strings.ReplaceAll(s, "=", "")
|
||||
result, err := base64.RawURLEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return content, nil
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
25
common/vision/hook.go
Normal file
25
common/vision/hook.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package vision
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Hook func(net.Conn)
|
||||
|
||||
type hookKey struct{}
|
||||
|
||||
func WithHook(ctx context.Context, hook Hook) context.Context {
|
||||
if hook == nil {
|
||||
return ctx
|
||||
}
|
||||
return context.WithValue(ctx, hookKey{}, hook)
|
||||
}
|
||||
|
||||
func HookFromContext(ctx context.Context) (Hook, bool) {
|
||||
if ctx == nil {
|
||||
return nil, false
|
||||
}
|
||||
hook, ok := ctx.Value(hookKey{}).(Hook)
|
||||
return hook, ok
|
||||
}
|
||||
18
common/xray/cpuid/cpuid.go
Normal file
18
common/xray/cpuid/cpuid.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
var (
|
||||
// Keep in sync with crypto/tls/cipher_suites.go.
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasGHASH
|
||||
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
|
||||
|
||||
// HasAESGCM indicates whether the CPU has AES-GCM hardware acceleration.
|
||||
HasAESGCM = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
|
||||
)
|
||||
@@ -71,6 +71,13 @@ func (c *Range) UnmarshalJSON(content []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Range) String() string {
|
||||
if c.From == c.To {
|
||||
return strconv.FormatInt(int64(c.From), 10)
|
||||
}
|
||||
return fmt.Sprintf("%d-%d", c.From, c.To)
|
||||
}
|
||||
|
||||
func (c Range) Rand() int32 {
|
||||
return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
|
||||
}
|
||||
|
||||
20
constant/provider.go
Normal file
20
constant/provider.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
ProviderTypeInline = "inline"
|
||||
ProviderTypeLocal = "local"
|
||||
ProviderTypeRemote = "remote"
|
||||
)
|
||||
|
||||
func ProviderDisplayName(providerType string) string {
|
||||
switch providerType {
|
||||
case ProviderTypeInline:
|
||||
return "Inline"
|
||||
case ProviderTypeLocal:
|
||||
return "Local"
|
||||
case ProviderTypeRemote:
|
||||
return "Remote"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,56 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
TypeTun = "tun"
|
||||
TypeRedirect = "redirect"
|
||||
TypeTProxy = "tproxy"
|
||||
TypeDirect = "direct"
|
||||
TypeBlock = "block"
|
||||
TypeDNS = "dns"
|
||||
TypeSOCKS = "socks"
|
||||
TypeHTTP = "http"
|
||||
TypeMixed = "mixed"
|
||||
TypeShadowsocks = "shadowsocks"
|
||||
TypeVMess = "vmess"
|
||||
TypeTrojan = "trojan"
|
||||
TypeNaive = "naive"
|
||||
TypeWireGuard = "wireguard"
|
||||
TypeWARP = "warp"
|
||||
TypeHysteria = "hysteria"
|
||||
TypeTor = "tor"
|
||||
TypeSSH = "ssh"
|
||||
TypeShadowTLS = "shadowtls"
|
||||
TypeMieru = "mieru"
|
||||
TypeAnyTLS = "anytls"
|
||||
TypeShadowsocksR = "shadowsocksr"
|
||||
TypeVLESS = "vless"
|
||||
TypeTUIC = "tuic"
|
||||
TypeHysteria2 = "hysteria2"
|
||||
TypeTunnelClient = "tunnel_client"
|
||||
TypeTunnelServer = "tunnel_server"
|
||||
TypeTailscale = "tailscale"
|
||||
TypeDERP = "derp"
|
||||
TypeResolved = "resolved"
|
||||
TypeSSMAPI = "ssm-api"
|
||||
TypeCCM = "ccm"
|
||||
TypeOCM = "ocm"
|
||||
TypeOOMKiller = "oom-killer"
|
||||
TypeTun = "tun"
|
||||
TypeRedirect = "redirect"
|
||||
TypeTProxy = "tproxy"
|
||||
TypeDirect = "direct"
|
||||
TypeBlock = "block"
|
||||
TypeDNS = "dns"
|
||||
TypeSOCKS = "socks"
|
||||
TypeHTTP = "http"
|
||||
TypeMixed = "mixed"
|
||||
TypeShadowsocks = "shadowsocks"
|
||||
TypeVMess = "vmess"
|
||||
TypeTrojan = "trojan"
|
||||
TypeNaive = "naive"
|
||||
TypeWireGuard = "wireguard"
|
||||
TypeWARP = "warp"
|
||||
TypeMASQUE = "masque"
|
||||
TypeMTProxy = "mtproxy"
|
||||
TypeParser = "parser"
|
||||
TypeHysteria = "hysteria"
|
||||
TypeTor = "tor"
|
||||
TypeSSH = "ssh"
|
||||
TypeShadowTLS = "shadowtls"
|
||||
TypeMieru = "mieru"
|
||||
TypeAnyTLS = "anytls"
|
||||
TypeShadowsocksR = "shadowsocksr"
|
||||
TypeVLESS = "vless"
|
||||
TypeTUIC = "tuic"
|
||||
TypeHysteria2 = "hysteria2"
|
||||
TypeBond = "bond"
|
||||
TypeVPNServer = "vpn-server"
|
||||
TypeVPNClient = "vpn-client"
|
||||
TypeTailscale = "tailscale"
|
||||
TypeConnectionLimiter = "connection-limiter"
|
||||
TypeBandwidthLimiter = "bandwidth-limiter"
|
||||
TypeTrafficLimiter = "traffic-limiter"
|
||||
TypeAdminPanel = "admin-panel"
|
||||
TypeNodeManagerServer = "node-manager-server"
|
||||
TypeNodeManagerClient = "node-manager-client"
|
||||
TypeDERP = "derp"
|
||||
TypeManager = "manager"
|
||||
TypeNode = "node"
|
||||
TypeResolved = "resolved"
|
||||
TypeSSMAPI = "ssm-api"
|
||||
TypeCCM = "ccm"
|
||||
TypeOCM = "ocm"
|
||||
TypeOOMKiller = "oom-killer"
|
||||
)
|
||||
|
||||
const (
|
||||
TypeFallback = "fallback"
|
||||
TypeSelector = "selector"
|
||||
TypeURLTest = "urltest"
|
||||
)
|
||||
@@ -74,6 +87,12 @@ func ProxyDisplayName(proxyType string) string {
|
||||
return "WireGuard"
|
||||
case TypeWARP:
|
||||
return "WARP"
|
||||
case TypeMASQUE:
|
||||
return "MASQUE"
|
||||
case TypeMTProxy:
|
||||
return "MTProxy"
|
||||
case TypeParser:
|
||||
return "Parser"
|
||||
case TypeHysteria:
|
||||
return "Hysteria"
|
||||
case TypeTor:
|
||||
@@ -90,20 +109,24 @@ func ProxyDisplayName(proxyType string) string {
|
||||
return "TUIC"
|
||||
case TypeHysteria2:
|
||||
return "Hysteria2"
|
||||
case TypeBond:
|
||||
return "Bond"
|
||||
case TypeMieru:
|
||||
return "Mieru"
|
||||
case TypeAnyTLS:
|
||||
return "AnyTLS"
|
||||
case TypeFallback:
|
||||
return "Fallback"
|
||||
case TypeTailscale:
|
||||
return "Tailscale"
|
||||
case TypeSelector:
|
||||
return "Selector"
|
||||
case TypeURLTest:
|
||||
return "URLTest"
|
||||
case TypeTunnelClient:
|
||||
return "Tunnel Client"
|
||||
case TypeTunnelServer:
|
||||
return "Tunnel Server"
|
||||
case TypeVPNClient:
|
||||
return "VPN Client"
|
||||
case TypeVPNServer:
|
||||
return "VPN Server"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ const (
|
||||
V2RayTransportTypeGRPC = "grpc"
|
||||
V2RayTransportTypeHTTPUpgrade = "httpupgrade"
|
||||
V2RayTransportTypeXHTTP = "xhttp"
|
||||
V2RayTransportTypeKCP = "mkcp"
|
||||
)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package constant
|
||||
|
||||
type WARPConfig struct {
|
||||
PrivateKey string `json:"private_key"`
|
||||
Interface struct {
|
||||
Addresses struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
} `json:"addresses"`
|
||||
} `json:"interface"`
|
||||
Peers []struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
Endpoint struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
Host string `json:"host"`
|
||||
Ports []int `json:"ports"`
|
||||
} `json:"endpoint"`
|
||||
} `json:"peers"`
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
#### 1.13.11
|
||||
|
||||
* Fix process searcher failure introduced in 1.13.9
|
||||
@@ -63,7 +61,6 @@ from [SagerNet/go](https://github.com/SagerNet/go).
|
||||
|
||||
See [OCM](/configuration/service/ocm).
|
||||
|
||||
>>>>>>> v1.13.11
|
||||
#### 1.13.2
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
57
examples/bandwidth_limiter/connection.json
Normal file
57
examples/bandwidth_limiter/connection.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"transport": {
|
||||
"type": "http"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "bandwidth-limiter",
|
||||
"tag": "bandwidth-limiter",
|
||||
"strategy": "connection",
|
||||
"mode": "duplex", // download, upload
|
||||
"connection_type": "hwid", // mux, ip
|
||||
"speed": "1MB", // 100KB, 1GB, etc.
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "direct"
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "bandwidth-limiter",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
56
examples/bandwidth_limiter/global.json
Normal file
56
examples/bandwidth_limiter/global.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"transport": {
|
||||
"type": "http"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "bandwidth-limiter",
|
||||
"tag": "bandwidth-limiter",
|
||||
"strategy": "global",
|
||||
"mode": "duplex", // download, upload
|
||||
"speed": "1MB", // 100KB, 1GB, etc.
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "direct"
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "bandwidth-limiter",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
78
examples/bandwidth_limiter/multi.json
Normal file
78
examples/bandwidth_limiter/multi.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"transport": {
|
||||
"type": "http"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "bandwidth-limiter",
|
||||
"tag": "duplex-bandwidth-limiter",
|
||||
"strategy": "global",
|
||||
"mode": "duplex",
|
||||
"speed": "5MB",
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "direct"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "bandwidth-limiter",
|
||||
"tag": "upload-bandwidth-limiter",
|
||||
"strategy": "global",
|
||||
"mode": "upload",
|
||||
"speed": "3MB",
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "duplex-bandwidth-limiter"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "bandwidth-limiter",
|
||||
"tag": "download-bandwidth-limiter",
|
||||
"strategy": "global",
|
||||
"mode": "download",
|
||||
"speed": "3MB",
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "upload-bandwidth-limiter"
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "download-bandwidth-limiter",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
70
examples/bandwidth_limiter/users.json
Normal file
70
examples/bandwidth_limiter/users.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"transport": {
|
||||
"type": "http"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "bandwidth-limiter",
|
||||
"tag": "bandwidth-limiter",
|
||||
"strategy": "users",
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"strategy": "connection", // global
|
||||
"mode": "duplex", // download, upload
|
||||
"connection_type": "hwid", // mux, ip
|
||||
"speed": "5MB", // 100KB, 1GB, etc.
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"strategy": "connection", // global
|
||||
"mode": "duplex", // download, upload
|
||||
"connection_type": "hwid", // mux, ip
|
||||
"speed": "1MB", // 100KB, 1GB, etc.
|
||||
},
|
||||
],
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "direct"
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "bandwidth-limiter",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
61
examples/bond/client.json
Normal file
61
examples/bond/client.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "bond",
|
||||
"tag": "bond-out",
|
||||
"outbounds": [ // sum of download_ratio and upload_ratio must be 100
|
||||
{
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
"server": "0.0.0.0",
|
||||
"server_port": 443,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"network": "tcp",
|
||||
"bind_interface": ""
|
||||
},
|
||||
"download_ratio": 50,
|
||||
"upload_ratio": 50
|
||||
},
|
||||
{
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
"server": "0.0.0.0",
|
||||
"server_port": 444,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"network": "tcp",
|
||||
"bind_interface": ""
|
||||
},
|
||||
"download_ratio": 50,
|
||||
"upload_ratio": 50
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "bond-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
49
examples/bond/client_multi.json
Normal file
49
examples/bond/client_multi.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "bond",
|
||||
"tag": "bond-out",
|
||||
"outbounds": [ // sum of download_ratio and upload_ratio must be 100
|
||||
{
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
"server": "0.0.0.0",
|
||||
"server_port": 443,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"network": "tcp",
|
||||
},
|
||||
"download_ratio": 20,
|
||||
"upload_ratio": 20,
|
||||
"count": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "bond-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
61
examples/bond/client_split.json
Normal file
61
examples/bond/client_split.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "bond",
|
||||
"tag": "bond-out",
|
||||
"outbounds": [ // sum of download_ratio and upload_ratio must be 100
|
||||
{
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
"server": "0.0.0.0",
|
||||
"server_port": 443,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"network": "tcp",
|
||||
"bind_interface": ""
|
||||
},
|
||||
"download_ratio": 100,
|
||||
"upload_ratio": 0
|
||||
},
|
||||
{
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
"server": "0.0.0.0",
|
||||
"server_port": 444,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"network": "tcp",
|
||||
"bind_interface": ""
|
||||
},
|
||||
"download_ratio": 0,
|
||||
"upload_ratio": 100
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "bond-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
54
examples/bond/server.json
Normal file
54
examples/bond/server.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "bond",
|
||||
"tag": "bond-in",
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"users": [
|
||||
{
|
||||
"name": "user",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 444,
|
||||
"users": [
|
||||
{
|
||||
"name": "user",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
],
|
||||
"route": {
|
||||
"final": "direct",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
56
examples/connection_limiter/connection.json
Normal file
56
examples/connection_limiter/connection.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"transport": {
|
||||
"type": "http"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "connection-limiter",
|
||||
"tag": "connection-limiter",
|
||||
"strategy": "connection",
|
||||
"connection_type": "hwid", // mux, ip
|
||||
"count": 5,
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "direct"
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "connection-limiter",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
68
examples/connection_limiter/users.json
Normal file
68
examples/connection_limiter/users.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 5000,
|
||||
"transport": {
|
||||
"type": "http"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"uuid": "6c8c7ffc-a909-4699-af34-e9d9bcb3e6d6"
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "connection-limiter",
|
||||
"tag": "connection-limiter",
|
||||
"strategy": "users",
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"strategy": "connection",
|
||||
"connection_type": "hwid", // mux, ip
|
||||
"count": 5,
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"strategy": "connection",
|
||||
"connection_type": "hwid", // mux, ip
|
||||
"count": 1,
|
||||
},
|
||||
],
|
||||
"route": { // https://sing-box.sagernet.org/configuration/route/#structure
|
||||
"rules": [],
|
||||
"final": "direct"
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "connection-limiter",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
61
examples/fallback/client.json
Normal file
61
examples/fallback/client.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-1-out",
|
||||
"server": "example1.com",
|
||||
"server_port": 443,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-2-out",
|
||||
"server": "example2.com",
|
||||
"server_port": 443,
|
||||
"uuid": "294fd6bc-4f89-43e7-9228-7900aba396af"
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-3-out",
|
||||
"server": "example3.com",
|
||||
"server_port": 443,
|
||||
"uuid": "257f20d0-294a-4f07-9f2c-9efee9a37400"
|
||||
},
|
||||
{
|
||||
"type": "fallback",
|
||||
"tag": "fallback-out",
|
||||
"outbounds": [
|
||||
"vless-1-out",
|
||||
"vless-2-out",
|
||||
"vless-3-out"
|
||||
]
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "fallback-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
62
examples/manager/manager.json
Normal file
62
examples/manager/manager.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"action": "hijack-dns"
|
||||
}
|
||||
],
|
||||
"final": "direct-out"
|
||||
},
|
||||
"services": [
|
||||
{
|
||||
"type": "manager",
|
||||
"tag": "my-manager",
|
||||
"database": {
|
||||
"driver": "postgresql",
|
||||
"dsn": "postgresql://postgres:postgres@localhost:5432/manager?sslmode=disable"
|
||||
}
|
||||
},
|
||||
{ // http://127.0.0.1:8000
|
||||
// Username: admin
|
||||
// Password: admin
|
||||
"type": "admin-panel",
|
||||
"tag": "my-admin-panel",
|
||||
"listen_port": 8000,
|
||||
"manager": "my-manager",
|
||||
"database": {
|
||||
"driver": "postgresql",
|
||||
"dsn": "postgresql://postgres:postgres@localhost:5432/adminpanel?sslmode=disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node-manager-server", // for connecting nodes
|
||||
"listen_port": 7000,
|
||||
"manager": "my-manager",
|
||||
"tls": { // https://sing-box.sagernet.org/configuration/shared/tls/#inbound
|
||||
"enabled": true,
|
||||
"server_name": "example.com",
|
||||
"certificate_path": "/path/to/fullchain.pem",
|
||||
"key_path": "/path/to/privkey.pem"
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
77
examples/manager/node.json
Normal file
77
examples/manager/node.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"transport": {
|
||||
"type": "http"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct-out"
|
||||
},
|
||||
{
|
||||
"type": "bandwidth-limiter",
|
||||
"tag": "bandwidth-limiter",
|
||||
"strategy": "manager",
|
||||
"route": {
|
||||
"final": "direct-out"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "connection-limiter",
|
||||
"tag": "connection-limiter",
|
||||
"strategy": "manager",
|
||||
"route": {
|
||||
"final": "bandwidth-limiter"
|
||||
}
|
||||
},
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"action": "hijack-dns"
|
||||
}
|
||||
],
|
||||
"final": "connection-limiter"
|
||||
},
|
||||
"services": [
|
||||
{
|
||||
"type": "node",
|
||||
"tag": "my-node",
|
||||
"uuid": "e6eceb84-ad66-474b-8641-142499db7c6e",
|
||||
"manager": "node-manager",
|
||||
"inbounds": ["vless-in"],
|
||||
"bandwidth_limiters": ["bandwidth-limiter"],
|
||||
"connection_limiters": ["connection-limiter"],
|
||||
},
|
||||
{
|
||||
"type": "node-manager-client",
|
||||
"tag": "node-manager",
|
||||
"server": "example.com",
|
||||
"server_port": 7000,
|
||||
"tls": { // https://sing-box.sagernet.org/configuration/shared/tls/#outbound
|
||||
"enabled": true,
|
||||
"server_name": "example.com",
|
||||
"alpn": "h2" // h3 for QUIC
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
58
examples/masque/client.json
Normal file
58
examples/masque/client.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "masque",
|
||||
"tag": "masque-out",
|
||||
"use_http2": false,
|
||||
"use_ipv6": false,
|
||||
"profile": {
|
||||
"detour": "direct",
|
||||
// For getting existing MASQUE device profile, else sing-box will create new profile
|
||||
"id": "",
|
||||
"auth_token": ""
|
||||
},
|
||||
"udp_timeout": "5m0s",
|
||||
"udp_keepalive_period": "30s",
|
||||
"udp_initial_packet_size": 0,
|
||||
"reconnect_delay": "5s",
|
||||
"tls": { // https://sing-box.sagernet.org/configuration/shared/tls/#fields
|
||||
"insecure": false,
|
||||
"cipher_suites": [],
|
||||
"curve_preferences": [],
|
||||
"fragment": false,
|
||||
"fragment_fallback_delay": "",
|
||||
"record_fragment": false,
|
||||
"kernel_tx": false,
|
||||
"kernel_rx": false,
|
||||
}
|
||||
// Dial Fields
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "masque-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
43
examples/mkcp/client.json
Normal file
43
examples/mkcp/client.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-out",
|
||||
"server": "example.com",
|
||||
"server_port": 443,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"packet_encoding": "",
|
||||
"transport": {
|
||||
"type": "mkcp",
|
||||
"mtu": 1500
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "vless-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
42
examples/mkcp/server.json
Normal file
42
examples/mkcp/server.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"users": [
|
||||
{
|
||||
"name": "user",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
],
|
||||
"transport": {
|
||||
"type": "mkcp",
|
||||
"mtu": 1500
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "direct",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
83
examples/mtproxy/server.json
Normal file
83
examples/mtproxy/server.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mtproxy",
|
||||
// https://sing-box.sagernet.org/configuration/shared/listen/
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 3128,
|
||||
"users": [
|
||||
{
|
||||
"name": "user1",
|
||||
"secret": "7hBO-dCS4EBzenlKbdLFxyNnb29nbGUuY29t"
|
||||
}
|
||||
],
|
||||
// concurrency is a size of the worker pool for connection management.
|
||||
"concurrency": 8192,
|
||||
// domain_fronting_port is a port we use to connect to a fronting domain.
|
||||
"domain_fronting_port": 443,
|
||||
// domain_fronting_ip is an IP address to use when connecting to the fronting
|
||||
// domain instead of resolving the hostname from the secret via DNS.
|
||||
"domain_fronting_ip": "",
|
||||
// domain_fronting_proxy_protocol is used if communication between upstream
|
||||
// endpoint and sing-box supports proxy protocol.
|
||||
"domain_fronting_proxy_protocol": false,
|
||||
// prefer_ip defines an IP connectivity preference. Valid values are:
|
||||
// 'prefer-ipv4', 'prefer-ipv6', 'only-ipv4', 'only-ipv6'.
|
||||
"prefer_ip": "prefer-ipv4",
|
||||
// auto_update defines if it is required to auto update proxy list from
|
||||
// Telegram instead of relying on a hardcoded list.
|
||||
"auto_update": false,
|
||||
// allow_fallback_on_unknown_dc defines how proxy behaves if unknown DC was
|
||||
// requested. If this setting is set to false, then such connection will be
|
||||
// rejected. Otherwise, proxy will chose any DC.
|
||||
"allow_fallback_on_unknown_dc": false,
|
||||
// tolerate_time_skewness is a time boundary that defines a time range where
|
||||
// faketls timestamp is acceptable.
|
||||
"tolerate_time_skewness": "",
|
||||
// idle_timeout is a timeout for relay when we have to break a stream.
|
||||
"idle_timeout": "5m",
|
||||
// handshake_timeout is a timeout during which all handshake ceremonies must
|
||||
// be completed, otherwise this process will be aborted
|
||||
"handshake_timeout": "10s",
|
||||
// doppelganger_urls is a list of URLs that should be crawled by
|
||||
// sing-box to calculate parameters for statistical distribution of a
|
||||
// traffic for fronting domains.
|
||||
"doppelganger_urls": [],
|
||||
// doppelganger_per_raid defines how many time each URL from
|
||||
// doppelganger_urls list should be crawled per raid.
|
||||
"doppelganger_per_raid": 10,
|
||||
// doppelganger_each defines a time period between each raid. We recommend
|
||||
// to use hours here.
|
||||
"doppelganger_each": "6h",
|
||||
// doppelganger_drs defines if TLS Dynamic Record Sizing is active.
|
||||
"doppelganger_drs": false,
|
||||
// throttle_max_connections is the total connection limit.
|
||||
"throttle_max_connections": 0,
|
||||
// throttle_check_interval is how often the throttle recomputes per-user
|
||||
// caps.
|
||||
"throttle_check_interval": "5s"
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "direct",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
37
examples/parser/client.json
Normal file
37
examples/parser/client.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "parser",
|
||||
"tag": "vless-out",
|
||||
// Supported protocols: hysteria, hysteria2, shadowsocks, trojan, tuic, vless, vmess
|
||||
"link": "vless://b5e41c8c-c437-4689-b863-76208a3efb4b@0.0.0.0:443?..."
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "vless-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_server",
|
||||
"tag": "tunnel",
|
||||
"uuid": "f79f7678-55e7-432d-a15f-6e8ab2b7fe13",
|
||||
"users": [
|
||||
{
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe"
|
||||
},
|
||||
{
|
||||
"uuid": "487f6073-3300-4819-a07d-39652e45fb4d",
|
||||
"key": "3d74d616-2502-4c17-9cc3-92c366550f4f"
|
||||
}
|
||||
],
|
||||
"inbound": {
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 8000,
|
||||
"users": [
|
||||
{
|
||||
"name": "vless",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"tunnel_source": [
|
||||
"9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"487f6073-3300-4819-a07d-39652e45fb4d"
|
||||
],
|
||||
"tunnel_destination": [
|
||||
"9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"487f6073-3300-4819-a07d-39652e45fb4d"
|
||||
],
|
||||
"outbound": "tunnel"
|
||||
}
|
||||
],
|
||||
"final": "direct-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_server",
|
||||
"tag": "tunnel",
|
||||
"uuid": "f79f7678-55e7-432d-a15f-6e8ab2b7fe13",
|
||||
"users": [
|
||||
{
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe"
|
||||
}
|
||||
],
|
||||
"inbound": {
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 8000,
|
||||
"users": [
|
||||
{
|
||||
"name": "vless",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"inbound": "vless-in",
|
||||
"outbound": "tunnel",
|
||||
"override_tunnel_destination": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
],
|
||||
"final": "direct-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
40
examples/vless_encryption/client.json
Normal file
40
examples/vless_encryption/client.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-out",
|
||||
"server": "example.com",
|
||||
"server_port": 443,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"encryption": "", // xray vlessenc
|
||||
"packet_encoding": ""
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "vless-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
39
examples/vless_encryption/server.json
Normal file
39
examples/vless_encryption/server.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 443,
|
||||
"decryption": "", // xray vlessenc
|
||||
"users": [
|
||||
{
|
||||
"name": "user",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "direct",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,9 @@
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_client",
|
||||
"tag": "tunnel",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"type": "vpn-client",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe",
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
@@ -37,17 +37,16 @@
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct-out"
|
||||
},
|
||||
{
|
||||
"type": "dns",
|
||||
"tag": "dns-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"outbound": "tunnel",
|
||||
"override_tunnel_destination": "f79f7678-55e7-432d-a15f-6e8ab2b7fe13"
|
||||
"protocol": "dns",
|
||||
"action": "hijack-dns"
|
||||
},
|
||||
{
|
||||
"outbound": "vpn",
|
||||
}
|
||||
],
|
||||
"final": "direct-out",
|
||||
51
examples/vpn/client-server/server.json
Normal file
51
examples/vpn/client-server/server.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "vpn-server",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.1",
|
||||
"users": [
|
||||
{
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe"
|
||||
}
|
||||
],
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 8000,
|
||||
"users": [
|
||||
{
|
||||
"name": "vless",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"final": "direct-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,9 @@
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_client",
|
||||
"tag": "tunnel",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"type": "vpn-client",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe",
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
@@ -42,8 +42,8 @@
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"outbound": "tunnel",
|
||||
"override_tunnel_destination": "487f6073-3300-4819-a07d-39652e45fb4d"
|
||||
"outbound": "vpn",
|
||||
"override_gateway": "10.0.0.3"
|
||||
}
|
||||
],
|
||||
"final": "direct-out",
|
||||
@@ -12,9 +12,9 @@
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_client",
|
||||
"tag": "tunnel",
|
||||
"uuid": "487f6073-3300-4819-a07d-39652e45fb4d",
|
||||
"type": "vpn-client",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.3",
|
||||
"key": "3d74d616-2502-4c17-9cc3-92c366550f4f",
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
61
examples/vpn/client1-server-client2/server.json
Normal file
61
examples/vpn/client1-server-client2/server.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "vpn-server",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.1",
|
||||
"users": [
|
||||
{
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe"
|
||||
},
|
||||
{
|
||||
"address": "10.0.0.3",
|
||||
"key": "3d74d616-2502-4c17-9cc3-92c366550f4f"
|
||||
}
|
||||
],
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
"listen": "0.0.0.0",
|
||||
"listen_port": 8000,
|
||||
"users": [
|
||||
{
|
||||
"name": "vless",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"source_ip_cidr": "10.0.0.0/24",
|
||||
"outbound": "vpn"
|
||||
}
|
||||
],
|
||||
"final": "direct-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
@@ -29,10 +29,6 @@
|
||||
"server_port": 8000,
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"network": "tcp"
|
||||
},
|
||||
{
|
||||
"type": "dns",
|
||||
"tag": "dns-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
@@ -12,12 +12,12 @@
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_server",
|
||||
"tag": "tunnel",
|
||||
"uuid": "f79f7678-55e7-432d-a15f-6e8ab2b7fe13",
|
||||
"type": "vpn-server",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.1",
|
||||
"users": [
|
||||
{
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe"
|
||||
}
|
||||
],
|
||||
@@ -42,6 +42,13 @@
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"inbound": "vless-in",
|
||||
"outbound": "vpn",
|
||||
"override_gateway": "10.0.0.2"
|
||||
}
|
||||
],
|
||||
"final": "direct-out",
|
||||
"default_domain_resolver": "default",
|
||||
"auto_detect_interface": true
|
||||
@@ -12,9 +12,9 @@
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_client",
|
||||
"tag": "tunnel",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"type": "vpn-client",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe",
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
"level": "info"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
@@ -12,9 +12,9 @@
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_client",
|
||||
"tag": "tunnel",
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"type": "vpn-client",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe",
|
||||
"outbound": {
|
||||
"type": "vless",
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"log": {
|
||||
"level": "error"
|
||||
"level": "info"
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
@@ -12,12 +12,12 @@
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"type": "tunnel_server",
|
||||
"tag": "tunnel",
|
||||
"uuid": "f79f7678-55e7-432d-a15f-6e8ab2b7fe13",
|
||||
"type": "vpn-server",
|
||||
"tag": "vpn",
|
||||
"address": "10.0.0.1",
|
||||
"users": [
|
||||
{
|
||||
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
|
||||
"address": "10.0.0.2",
|
||||
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe"
|
||||
}
|
||||
],
|
||||
@@ -39,7 +39,7 @@
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"listen_port": 7897
|
||||
"listen_port": 10000
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
@@ -51,8 +51,8 @@
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"outbound": "tunnel",
|
||||
"override_tunnel_destination": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
|
||||
"outbound": "vpn",
|
||||
"override_gateway": "10.0.0.2"
|
||||
}
|
||||
],
|
||||
"final": "direct-out",
|
||||
@@ -44,6 +44,7 @@ type CacheFile struct {
|
||||
storeFakeIP bool
|
||||
storeRDRC bool
|
||||
storeWARPConfig bool
|
||||
storeMASQUEConfig bool
|
||||
rdrcTimeout time.Duration
|
||||
DB *bbolt.DB
|
||||
resetAccess sync.Mutex
|
||||
@@ -82,17 +83,18 @@ func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||
}
|
||||
}
|
||||
return &CacheFile{
|
||||
ctx: ctx,
|
||||
path: filemanager.BasePath(ctx, path),
|
||||
cacheID: cacheIDBytes,
|
||||
storeFakeIP: options.StoreFakeIP,
|
||||
storeRDRC: options.StoreRDRC,
|
||||
storeWARPConfig: options.StoreWARPConfig,
|
||||
rdrcTimeout: rdrcTimeout,
|
||||
saveDomain: make(map[netip.Addr]string),
|
||||
saveAddress4: make(map[string]netip.Addr),
|
||||
saveAddress6: make(map[string]netip.Addr),
|
||||
saveRDRC: make(map[saveRDRCCacheKey]bool),
|
||||
ctx: ctx,
|
||||
path: filemanager.BasePath(ctx, path),
|
||||
cacheID: cacheIDBytes,
|
||||
storeFakeIP: options.StoreFakeIP,
|
||||
storeRDRC: options.StoreRDRC,
|
||||
storeWARPConfig: options.StoreWARPConfig,
|
||||
storeMASQUEConfig: options.StoreMASQUEConfig,
|
||||
rdrcTimeout: rdrcTimeout,
|
||||
saveDomain: make(map[netip.Addr]string),
|
||||
saveAddress4: make(map[string]netip.Addr),
|
||||
saveAddress6: make(map[string]netip.Addr),
|
||||
saveRDRC: make(map[saveRDRCCacheKey]bool),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +368,10 @@ func (c *CacheFile) StoreWARPConfig() bool {
|
||||
return c.storeWARPConfig
|
||||
}
|
||||
|
||||
func (c *CacheFile) StoreMASQUEConfig() bool {
|
||||
return c.storeMASQUEConfig
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadWARPConfig(tag string) *adapter.SavedBinary {
|
||||
var savedConfig adapter.SavedBinary
|
||||
err := c.DB.View(func(t *bbolt.Tx) error {
|
||||
@@ -398,3 +404,69 @@ func (c *CacheFile) SaveWARPConfig(tag string, set *adapter.SavedBinary) error {
|
||||
return bucket.Put([]byte(tag), configBinary)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadMASQUEConfig(tag string) *adapter.SavedBinary {
|
||||
var savedConfig adapter.SavedBinary
|
||||
err := c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := c.bucket(t, bucketRuleSet)
|
||||
if bucket == nil {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
configBinary := bucket.Get([]byte(tag))
|
||||
if len(configBinary) == 0 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return savedConfig.UnmarshalBinary(configBinary)
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &savedConfig
|
||||
}
|
||||
|
||||
func (c *CacheFile) SaveMASQUEConfig(tag string, set *adapter.SavedBinary) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
bucket, err := c.createBucket(t, bucketRuleSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configBinary, err := set.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put([]byte(tag), configBinary)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadSubscription(tag string) *adapter.SavedBinary {
|
||||
var savedSet adapter.SavedBinary
|
||||
err := c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := c.bucket(t, bucketRuleSet)
|
||||
if bucket == nil {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
setBinary := bucket.Get([]byte(tag))
|
||||
if len(setBinary) == 0 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return savedSet.UnmarshalBinary(setBinary)
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &savedSet
|
||||
}
|
||||
|
||||
func (c *CacheFile) SaveSubscription(tag string, sub *adapter.SavedBinary) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
bucket, err := c.createBucket(t, bucketRuleSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setBinary, err := sub.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put([]byte(tag), setBinary)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,48 +4,78 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func proxyProviderRouter() http.Handler {
|
||||
func proxyProviderRouter(server *Server) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getProviders)
|
||||
r.Get("/", getProviders(server))
|
||||
|
||||
r.Route("/{name}", func(r chi.Router) {
|
||||
r.Use(parseProviderName, findProviderByName)
|
||||
r.Get("/", getProvider)
|
||||
r.Use(parseProviderName, findProviderByName(server))
|
||||
r.Get("/", getProvider(server))
|
||||
r.Put("/", updateProvider)
|
||||
r.Get("/healthcheck", healthCheckProvider)
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func getProviders(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, render.M{
|
||||
"providers": render.M{},
|
||||
})
|
||||
func getProviders(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
providerMap := make(render.M)
|
||||
for _, provider := range server.provider.Providers() {
|
||||
providerMap[provider.Tag()] = providerInfo(server, provider)
|
||||
}
|
||||
render.JSON(w, r, render.M{
|
||||
"providers": providerMap,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getProvider(w http.ResponseWriter, r *http.Request) {
|
||||
/*provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider)
|
||||
render.JSON(w, r, provider)*/
|
||||
render.NoContent(w, r)
|
||||
func getProvider(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
provider := r.Context().Value(CtxKeyProvider).(adapter.Provider)
|
||||
render.JSON(w, r, providerInfo(server, provider))
|
||||
}
|
||||
}
|
||||
|
||||
func providerInfo(server *Server, p adapter.Provider) *badjson.JSONObject {
|
||||
var info badjson.JSONObject
|
||||
proxies := make([]*badjson.JSONObject, 0)
|
||||
for _, detour := range p.Outbounds() {
|
||||
proxies = append(proxies, proxyInfo(server, detour))
|
||||
}
|
||||
info.Put("type", "Proxy") // Proxy, Rule
|
||||
info.Put("vehicleType", C.ProviderDisplayName(p.Type())) // HTTP, File, Compatible
|
||||
info.Put("name", p.Tag())
|
||||
info.Put("proxies", proxies)
|
||||
info.Put("updatedAt", p.UpdatedAt())
|
||||
if p, ok := p.(adapter.ProviderSubscriptionInfo); ok {
|
||||
info.Put("subscriptionInfo", p.SubscriptionInfo())
|
||||
}
|
||||
return &info
|
||||
}
|
||||
|
||||
func updateProvider(w http.ResponseWriter, r *http.Request) {
|
||||
/*provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider)
|
||||
if err := provider.Update(); err != nil {
|
||||
render.Status(r, http.StatusServiceUnavailable)
|
||||
render.JSON(w, r, newError(err.Error()))
|
||||
return
|
||||
}*/
|
||||
provider := r.Context().Value(CtxKeyProvider).(adapter.Provider)
|
||||
if provider, isUpdater := provider.(adapter.ProviderUpdater); isUpdater {
|
||||
if err := provider.Update(); err != nil {
|
||||
render.Status(r, http.StatusServiceUnavailable)
|
||||
render.JSON(w, r, newError(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
func healthCheckProvider(w http.ResponseWriter, r *http.Request) {
|
||||
/*provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider)
|
||||
provider.HealthCheck()*/
|
||||
provider := r.Context().Value(CtxKeyProvider).(adapter.Provider)
|
||||
provider.HealthCheck(r.Context())
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
@@ -57,18 +87,19 @@ func parseProviderName(next http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func findProviderByName(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
/*name := r.Context().Value(CtxKeyProviderName).(string)
|
||||
providers := tunnel.ProxyProviders()
|
||||
provider, exist := providers[name]
|
||||
if !exist {*/
|
||||
render.Status(r, http.StatusNotFound)
|
||||
render.JSON(w, r, ErrNotFound)
|
||||
//return
|
||||
//}
|
||||
func findProviderByName(server *Server) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
name := r.Context().Value(CtxKeyProviderName).(string)
|
||||
provider, exist := server.provider.Get(name)
|
||||
if !exist {
|
||||
render.Status(r, http.StatusNotFound)
|
||||
render.JSON(w, r, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// ctx := context.WithValue(r.Context(), CtxKeyProvider, provider)
|
||||
// next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
ctx := context.WithValue(r.Context(), CtxKeyProvider, provider)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ type Server struct {
|
||||
dnsRouter adapter.DNSRouter
|
||||
outbound adapter.OutboundManager
|
||||
endpoint adapter.EndpointManager
|
||||
provider adapter.ProviderManager
|
||||
logger log.Logger
|
||||
httpServer *http.Server
|
||||
trafficManager *trafficontrol.Manager
|
||||
@@ -71,6 +72,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
endpoint: service.FromContext[adapter.EndpointManager](ctx),
|
||||
provider: service.FromContext[adapter.ProviderManager](ctx),
|
||||
logger: logFactory.NewLogger("clash-api"),
|
||||
httpServer: &http.Server{
|
||||
Addr: options.ExternalController,
|
||||
@@ -122,7 +124,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
||||
r.Mount("/proxies", proxyRouter(s, s.router))
|
||||
r.Mount("/rules", ruleRouter(s.router))
|
||||
r.Mount("/connections", connectionRouter(s.ctx, s.router, trafficManager))
|
||||
r.Mount("/providers/proxies", proxyProviderRouter())
|
||||
r.Mount("/providers/proxies", proxyProviderRouter(s))
|
||||
r.Mount("/providers/rules", ruleProviderRouter())
|
||||
r.Mount("/script", scriptRouter())
|
||||
r.Mount("/profile", profileRouter())
|
||||
|
||||
@@ -34,7 +34,7 @@ func baseContext(platformInterface PlatformInterface) context.Context {
|
||||
}
|
||||
ctx := context.Background()
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
return box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||
return box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), include.ProviderRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||
}
|
||||
|
||||
func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
|
||||
|
||||
101
go.mod
101
go.mod
@@ -1,8 +1,11 @@
|
||||
module github.com/sagernet/sing-box
|
||||
|
||||
go 1.25.5
|
||||
go 1.26.1
|
||||
|
||||
require (
|
||||
github.com/Diniboy1123/connect-ip-go v0.0.0-20260409225322-8d7bb0a858a2
|
||||
github.com/GoAdminGroup/go-admin v1.2.26
|
||||
github.com/GoAdminGroup/themes v0.0.48
|
||||
github.com/anthropics/anthropic-sdk-go v1.26.0
|
||||
github.com/anytls/sing-anytls v0.0.11
|
||||
github.com/caddyserver/certmagic v0.25.2
|
||||
@@ -10,12 +13,18 @@ require (
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/database64128/tfo-go/v2 v2.3.2
|
||||
github.com/enfein/mieru/v3 v3.17.1
|
||||
github.com/go-chi/chi v1.5.5
|
||||
github.com/go-chi/chi/v5 v5.2.5
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/go-playground/validator/v10 v10.30.1
|
||||
github.com/godbus/dbus/v5 v5.2.2
|
||||
github.com/gofrs/uuid/v5 v5.4.0
|
||||
github.com/golang-migrate/migrate/v4 v4.19.1
|
||||
github.com/huandu/go-sqlbuilder v1.39.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91
|
||||
github.com/jackc/pgx/v5 v5.8.0
|
||||
github.com/keybase/go-keychain v0.0.1
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/libdns/acmedns v0.5.0
|
||||
github.com/libdns/alidns v1.0.6
|
||||
github.com/libdns/cloudflare v0.2.2
|
||||
@@ -25,6 +34,7 @@ require (
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/openai/openai-go/v3 v3.26.0
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/patrickmn/go-cache/v2 v2.0.0-00010101000000-000000000000
|
||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/cors v1.2.1
|
||||
@@ -46,25 +56,40 @@ require (
|
||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7
|
||||
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20260224074747-506b7631853c
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/vishvananda/netns v0.0.5
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2
|
||||
go.uber.org/zap v1.27.1
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93
|
||||
golang.org/x/mod v0.33.0
|
||||
golang.org/x/net v0.50.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.org/x/crypto v0.49.0
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90
|
||||
golang.org/x/mod v0.34.0
|
||||
golang.org/x/net v0.52.0
|
||||
golang.org/x/sys v0.42.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/dunglas/httpsfv v1.1.0 // indirect
|
||||
github.com/panjf2000/ants/v2 v2.12.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/tylertreat/BoomFilters v0.0.0-20251117164519-53813c36cc1b // indirect
|
||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20260408064518-65a410b0d584 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect
|
||||
github.com/AdguardTeam/golibs v0.32.7 // indirect
|
||||
github.com/GoAdminGroup/html v0.0.1 // indirect
|
||||
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/akutz/memconn v0.1.0 // indirect
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
|
||||
@@ -78,16 +103,24 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||
github.com/dolonet/mtg-multi v1.8.0
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/florianl/go-nfqueue/v2 v2.0.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/gaissmai/bart v0.18.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.3.0 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
@@ -95,16 +128,29 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/huandu/go-clone v1.7.3 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/libdns/libdns v1.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/mdlayher/netlink v1.9.0 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||
github.com/pires/go-proxyproto v0.11.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
@@ -140,7 +186,8 @@ require (
|
||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260413092954-cd09eb3e271b // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/sagernet/nftables v0.3.0-mod.2 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
||||
@@ -148,31 +195,49 @@ require (
|
||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
|
||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/term v0.41.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/time v0.15.0
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.4.1 // indirect
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
lukechampine.com/blake3 v1.4.1
|
||||
xorm.io/builder v0.3.7 // indirect
|
||||
xorm.io/xorm v1.0.2 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/wireguard-go => github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.3.4
|
||||
|
||||
replace github.com/sagernet/wireguard-go => github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.4.0
|
||||
|
||||
replace github.com/sagernet/tailscale => github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2
|
||||
|
||||
replace github.com/sagernet/sing-mux => github.com/shtorm-7/sing-mux v0.3.4-extended-1.0.0
|
||||
|
||||
replace github.com/ameshkov/dnscrypt/v2 => github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0
|
||||
|
||||
replace github.com/sagernet/sing-vmess => github.com/starifly/sing-vmess v0.2.7-mod.9
|
||||
|
||||
replace github.com/patrickmn/go-cache/v2 => github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.0.2
|
||||
|
||||
replace github.com/dolonet/mtg-multi => github.com/shtorm-7/mtg-multi v1.8.0-extended-1.0.0
|
||||
|
||||
replace github.com/Diniboy1123/connect-ip-go => github.com/shtorm-7/connect-ip-go v1.0.0-extended-1.0.0
|
||||
|
||||
413
go.sum
413
go.sum
@@ -1,413 +0,0 @@
|
||||
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
|
||||
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdguardTeam/golibs v0.32.7 h1:3dmGlAVgmvquCCwHsvEl58KKcRAK3z1UnjMnwSIeDH4=
|
||||
github.com/AdguardTeam/golibs v0.32.7/go.mod h1:bE8KV1zqTzgZjmjFyBJ9f9O5DEKO717r7e57j1HclJA=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
|
||||
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/anthropics/anthropic-sdk-go v1.26.0 h1:oUTzFaUpAevfuELAP1sjL6CQJ9HHAfT7CoSYSac11PY=
|
||||
github.com/anthropics/anthropic-sdk-go v1.26.0/go.mod h1:qUKmaW+uuPB64iy1l+4kOSvaLqPXnHTTBKH6RVZ7q5Q=
|
||||
github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc=
|
||||
github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
|
||||
github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc=
|
||||
github.com/caddyserver/certmagic v0.25.2/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg=
|
||||
github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE=
|
||||
github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
|
||||
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
|
||||
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
||||
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
|
||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||
github.com/database64128/netx-go v0.1.1 h1:dT5LG7Gs7zFZBthFBbzWE6K8wAHjSNAaK7wCYZT7NzM=
|
||||
github.com/database64128/netx-go v0.1.1/go.mod h1:LNlYVipaYkQArRFDNNJ02VkNV+My9A5XR/IGS7sIBQc=
|
||||
github.com/database64128/tfo-go/v2 v2.3.2 h1:UhZMKiMq3swZGUiETkLBDzQnZBPSAeBMClpJGlnJ5Fw=
|
||||
github.com/database64128/tfo-go/v2 v2.3.2/go.mod h1:GC3uB5oa4beGpCUbRb2ZOWP73bJJFmMyAVgQSO7r724=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/enfein/mieru/v3 v3.17.1 h1:pIKbspsKRYNyUrORVI33t1/yz2syaaUkIanskAbGBHY=
|
||||
github.com/enfein/mieru/v3 v3.17.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
|
||||
github.com/florianl/go-nfqueue/v2 v2.0.2 h1:FL5lQTeetgpCvac1TRwSfgaXUn0YSO7WzGvWNIp3JPE=
|
||||
github.com/florianl/go-nfqueue/v2 v2.0.2/go.mod h1:VA09+iPOT43OMoCKNfXHyzujQUty2xmzyCRkBOlmabc=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gaissmai/bart v0.18.0 h1:jQLBT/RduJu0pv/tLwXE+xKPgtWJejbxuXAR+wLJafo=
|
||||
github.com/gaissmai/bart v0.18.0/go.mod h1:JJzMAhNF5Rjo4SF4jWBrANuJfqY+FvsFhW7t1UZJ+XY=
|
||||
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
|
||||
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
|
||||
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
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-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced h1:Q311OHjMh/u5E2TITc++WlTP5We0xNseRMkHDyvhW7I=
|
||||
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
|
||||
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||
github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
|
||||
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI=
|
||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
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-20260220084031-5adc3eb26f91 h1:u9i04mGE3iliBh0EFuWaKsmcwrLacqGmq1G3XoaM7gY=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91/go.mod h1:qfvBmyDNp+/liLEYWRvqny/PEz9hGe2Dz833eXILSmo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ=
|
||||
github.com/libdns/acmedns v0.5.0 h1:5pRtmUj4Lb/QkNJSl1xgOGBUJTWW7RjpNaIhjpDXjPE=
|
||||
github.com/libdns/acmedns v0.5.0/go.mod h1:X7UAFP1Ep9NpTwWpVlrZzJLR7epynAy0wrIxSPFgKjQ=
|
||||
github.com/libdns/alidns v1.0.6 h1:/Ii428ty6WHFJmE24rZxq2taq++gh7rf9jhgLfp8PmM=
|
||||
github.com/libdns/alidns v1.0.6/go.mod h1:RECwyQ88e9VqQVtSrvX76o1ux3gQUKGzMgxICi+u7Ec=
|
||||
github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI=
|
||||
github.com/libdns/cloudflare v0.2.2/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60=
|
||||
github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
|
||||
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco=
|
||||
github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg=
|
||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
|
||||
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
||||
github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk=
|
||||
github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/openai/openai-go/v3 v3.26.0 h1:bRt6H/ozMNt/dDkN4gobnLqaEGrRGBzmbVs0xxJEnQE=
|
||||
github.com/openai/openai-go/v3 v3.26.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
|
||||
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
||||
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4=
|
||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU=
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
||||
github.com/sagernet/cronet-go v0.0.0-20260413093659-e4926ba205fa h1:7SehNSF1UHbLZa5dk+1rW1aperffJzl5r6TCJIXtAaY=
|
||||
github.com/sagernet/cronet-go v0.0.0-20260413093659-e4926ba205fa/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw=
|
||||
github.com/sagernet/cronet-go/all v0.0.0-20260413093659-e4926ba205fa h1:ijk5v9N/akiMgqu734yMpv7Pk9F4Qmjh8Vfdcb4uJHE=
|
||||
github.com/sagernet/cronet-go/all v0.0.0-20260413093659-e4926ba205fa/go.mod h1:+FENo4+0AOvH9e3oY6/iO7yy7USNt61dgbnI5W0TDZ0=
|
||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260413092954-cd09eb3e271b h1:O+PkYT88ayVWESX5tqxeMeS9OnzC3ZTic8gYiPJNXT8=
|
||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260413092954-cd09eb3e271b h1:o0MsgbsJwYkbqlbfaCvmAwb8/LAXeoSP8NE/aNvR/yY=
|
||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260413092954-cd09eb3e271b h1:JEQnc7cRMUahWJFtWY6n0hs1LE0KgyRv3pD0RWS8Yo8=
|
||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260413092954-cd09eb3e271b h1:69+AKzuUW9hzw2nU79c2DWfuzrIZ3PJm1KAwXh+7xr0=
|
||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260413092954-cd09eb3e271b h1:jp9FHUVTCJQ67Ecw3Inoct6/z1VTFXPtNYpXt47pa4E=
|
||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260413092954-cd09eb3e271b h1:WN3DZoECd2UbhmYQGpOA4jx4QBXiZuN1DvL/35NT61g=
|
||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260413092954-cd09eb3e271b h1:H4RKicwrIa4PwTXZOmXOg85hiCrpeFja4daOlX180pE=
|
||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
|
||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260413092954-cd09eb3e271b h1:Rwi+Cu+Hgwj28F1lh837gGqSqn7oU8+r5i3UJyLPkKc=
|
||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260413092954-cd09eb3e271b h1:v2wcnPX3gt0PngFYXjXYAiarFckwx3pVAP6ETSpbSWE=
|
||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
|
||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260413092954-cd09eb3e271b h1:Bl0zZ3QZq6pPJMbQlYHDhhaGngVefRlFzxWc0p48eHo=
|
||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260413092954-cd09eb3e271b h1:vf+MbGv6RvvmXUNvganykBOnDIVXxy8XgtKOOqOcxtE=
|
||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260413092954-cd09eb3e271b h1:2IAc1bVFYF+B6hof34ChQKVhw7LElBxEEx7S0n+7o78=
|
||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260413092954-cd09eb3e271b h1:NrJaiOS0VLmWTbUHhXDsLTqelmCW4y3xJqptPs4Sx0s=
|
||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260413092954-cd09eb3e271b h1:A+ubSkca1nl2cT8pYUqCo1O7M41suNrKpWhZKCM/aIQ=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260413092954-cd09eb3e271b h1:WrhGH5FDXlCAoXwN6N44yCMvy6EbIurmTmptkz3mmms=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260413092954-cd09eb3e271b h1:kgwB5p5e0gdVX5iYRE7VbZS/On4qnb4UKonkGPwhkDI=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260413092954-cd09eb3e271b h1:Z3dOeFlRIOeQhSh+mCYDHui1yR3S/Uw8eupczzBvxqw=
|
||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
||||
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260413092954-cd09eb3e271b h1:LPi6jz1k11Q67hm3Pw6aaPJ/Z6e3VtNhzrRjr5/5AQo=
|
||||
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk=
|
||||
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260413092954-cd09eb3e271b h1:55sqihyfXWN7y7p7gOEgtUz9cm1mV3SDQ90/v6ROFaA=
|
||||
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E=
|
||||
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260413092954-cd09eb3e271b h1:OTA1cbv5YIDVsYA8AAXHC4NgEc7b6pDiY+edujLWfJU=
|
||||
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8=
|
||||
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260413092954-cd09eb3e271b h1:B/rdD/1A+RgqUYUZcoGhLeMqijnBd1mUt8+5LhOH7j8=
|
||||
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w=
|
||||
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260413092954-cd09eb3e271b h1:QFRWi6FucrODS4xQ8e9GYIzGSeMFO/DAMtTCVeJiCvM=
|
||||
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0=
|
||||
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260413092954-cd09eb3e271b h1:2WJjPKZHLNIB4D17c3o9S+SP9kb3Qh0D26oWlun1+pE=
|
||||
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs=
|
||||
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260413092954-cd09eb3e271b h1:cUNTe4gNncRpYL28jzQf6qcJej40zzGQsH0o6CLUGws=
|
||||
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc=
|
||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260413092954-cd09eb3e271b h1:+sc1LJF0FjU2hVO5xBqqT+8qzoU08J2uHwxSle2m/Hw=
|
||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
|
||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260413092954-cd09eb3e271b h1:+D/uhFxllI/KTLpeNEl8dwF3omPGmUFbrqt5tJkAyp0=
|
||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
|
||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260413092954-cd09eb3e271b h1:nSUzzTUAZdqjGGckayk64sz+F0TGJPHvauTiAn27UKk=
|
||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
|
||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260413092954-cd09eb3e271b h1:PE/fYBiHzB52gnQMg0soBfQyJCzmWHti48kCe2TBt9w=
|
||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260413092954-cd09eb3e271b h1:hy/3lPV11pKAAojDFnb95l9NpwOym6kME7FxS9p8sXs=
|
||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260413092954-cd09eb3e271b/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
|
||||
github.com/sagernet/fswatch v0.1.2 h1:/TT7k4mkce1qFPxamLO842WjqBgbTBiXP2mlUjp9PFk=
|
||||
github.com/sagernet/fswatch v0.1.2/go.mod h1:5BpGmpUQVd3Mc5r313HRpvADHRg3/rKn5QbwFteB880=
|
||||
github.com/sagernet/gomobile v0.1.12 h1:XwzjZaclFF96deLqwAgK8gU3w0M2A8qxgDmhV+A0wjg=
|
||||
github.com/sagernet/gomobile v0.1.12/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
|
||||
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1 h1:AzCE2RhBjLJ4WIWc/GejpNh+z30d5H1hwaB0nD9eY3o=
|
||||
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1/go.mod h1:NJKBtm9nVEK3iyOYWsUlrDQuoGh4zJ4KOPhSYVidvQ4=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/nftables v0.3.0-mod.2 h1:ck2KMU02OxL1eDFgGaWYglMDpoOZ7OHzxje+vW5Q0OQ=
|
||||
github.com/sagernet/nftables v0.3.0-mod.2/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
|
||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o=
|
||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
||||
github.com/sagernet/sing v0.8.9 h1:iX8FyMrWNl/divVgTe7cLT9n36v6bfzfnCYlcM1cLaU=
|
||||
github.com/sagernet/sing v0.8.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
|
||||
github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
||||
github.com/sagernet/sing-quic v0.6.1 h1:lx0tcm99wIA1RkyvILNzRSsMy1k7TTQYIhx71E/WBlw=
|
||||
github.com/sagernet/sing-quic v0.6.1/go.mod h1:K5bWvITOm4vE10fwLfrWpw27bCoVJ+tfQ79tOWg+Ko8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||
github.com/sagernet/sing-tun v0.8.9 h1:ixFKKUGdVcJl4wb0xbL36hobiw9l6DIH497EQf5ILpM=
|
||||
github.com/sagernet/sing-tun v0.8.9/go.mod h1:QvarqUtHfj1ULaRR+6kZOS/OoCE+pYGq67A5tyIy+dQ=
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||
github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0 h1:e5s7RKBd2rIPR0StbvZ2vTVtJ5jDTsTk5wtIIapZTRg=
|
||||
github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI=
|
||||
github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2 h1:hSMjh97OszszOd8HrzpaYUQH9dWRRBluJCbwQyz8ZOk=
|
||||
github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2/go.mod h1:TYIIqO5sZpWq873rLIeO2usszSMUpR3h6WdqVVs65ug=
|
||||
github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.3.4 h1:t/2ZxRo8cwvydImFaKuUSDrcZYhX753JiXGe7411krI=
|
||||
github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.3.4/go.mod h1:Me2JlCDYHxnd0mnuX7L5LXAeDHCltI7vSKq3eTE6SVE=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
|
||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
|
||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
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.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek=
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
|
||||
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/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=
|
||||
gvisor.dev/gvisor v0.0.0-20260309223511-c9735035b4c6 h1:q2oSL6CrUQDUOT8D70ImK10gGRTIZjhR7fgSU//5kc0=
|
||||
gvisor.dev/gvisor v0.0.0-20260309223511-c9735035b4c6/go.mod h1:xQ2PWgHmWJA/Ph4i1q1jBm39BKhc3W0DXqWoDSyuBOY=
|
||||
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
||||
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
||||
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
|
||||
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
||||
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||
12
include/masque.go
Normal file
12
include/masque.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build with_masque
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/protocol/masque"
|
||||
)
|
||||
|
||||
func registerMASQUEOutbound(registry *outbound.Registry) {
|
||||
masque.RegisterOutbound(registry)
|
||||
}
|
||||
20
include/masque_stub.go
Normal file
20
include/masque_stub.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build !with_masque
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func registerMASQUEOutbound(registry *outbound.Registry) {
|
||||
outbound.Register[option.MASQUEOutboundOptions](registry, C.TypeMASQUE, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MASQUEOutboundOptions) (adapter.Outbound, error) {
|
||||
return nil, E.New(`MASQUE outbound is not included in this build, rebuild with -tags with_masque`)
|
||||
})
|
||||
}
|
||||
12
include/mtproxy.go
Normal file
12
include/mtproxy.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build with_mtproxy
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/protocol/mtproxy"
|
||||
)
|
||||
|
||||
func registerMTProxyInbound(registry *inbound.Registry) {
|
||||
mtproxy.RegisterInbound(registry)
|
||||
}
|
||||
20
include/mtproxy_stub.go
Normal file
20
include/mtproxy_stub.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build !with_mtproxy
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func registerMTProxyInbound(registry *inbound.Registry) {
|
||||
inbound.Register[option.MTProxyInboundOptions](registry, C.TypeMTProxy, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MTProxyInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, E.New(`MTProxy is not included in this build, rebuild with -tags with_mtproxy`)
|
||||
})
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/adapter/provider"
|
||||
"github.com/sagernet/sing-box/adapter/service"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
@@ -19,12 +20,16 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/anytls"
|
||||
"github.com/sagernet/sing-box/protocol/block"
|
||||
"github.com/sagernet/sing-box/protocol/bond"
|
||||
"github.com/sagernet/sing-box/protocol/direct"
|
||||
"github.com/sagernet/sing-box/protocol/group"
|
||||
"github.com/sagernet/sing-box/protocol/http"
|
||||
"github.com/sagernet/sing-box/protocol/limiter/bandwidth"
|
||||
"github.com/sagernet/sing-box/protocol/limiter/connection"
|
||||
"github.com/sagernet/sing-box/protocol/mieru"
|
||||
"github.com/sagernet/sing-box/protocol/mixed"
|
||||
"github.com/sagernet/sing-box/protocol/naive"
|
||||
"github.com/sagernet/sing-box/protocol/parser"
|
||||
"github.com/sagernet/sing-box/protocol/redirect"
|
||||
"github.com/sagernet/sing-box/protocol/shadowsocks"
|
||||
"github.com/sagernet/sing-box/protocol/shadowtls"
|
||||
@@ -33,16 +38,23 @@ import (
|
||||
"github.com/sagernet/sing-box/protocol/tor"
|
||||
"github.com/sagernet/sing-box/protocol/trojan"
|
||||
"github.com/sagernet/sing-box/protocol/tun"
|
||||
"github.com/sagernet/sing-box/protocol/tunnel"
|
||||
"github.com/sagernet/sing-box/protocol/vless"
|
||||
"github.com/sagernet/sing-box/protocol/vmess"
|
||||
"github.com/sagernet/sing-box/protocol/vpn"
|
||||
localProvider "github.com/sagernet/sing-box/provider/local"
|
||||
remoteProvider "github.com/sagernet/sing-box/provider/remote"
|
||||
"github.com/sagernet/sing-box/service/admin_panel"
|
||||
"github.com/sagernet/sing-box/service/manager"
|
||||
"github.com/sagernet/sing-box/service/node"
|
||||
nodeManagerClient "github.com/sagernet/sing-box/service/node_manager/client"
|
||||
nodeManagerServer "github.com/sagernet/sing-box/service/node_manager/server"
|
||||
"github.com/sagernet/sing-box/service/resolved"
|
||||
"github.com/sagernet/sing-box/service/ssmapi"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func Context(ctx context.Context) context.Context {
|
||||
return box.Context(ctx, InboundRegistry(), OutboundRegistry(), EndpointRegistry(), DNSTransportRegistry(), ServiceRegistry())
|
||||
return box.Context(ctx, InboundRegistry(), OutboundRegistry(), EndpointRegistry(), ProviderRegistry(), DNSTransportRegistry(), ServiceRegistry())
|
||||
}
|
||||
|
||||
func InboundRegistry() *inbound.Registry {
|
||||
@@ -65,8 +77,11 @@ func InboundRegistry() *inbound.Registry {
|
||||
vless.RegisterInbound(registry)
|
||||
anytls.RegisterInbound(registry)
|
||||
|
||||
bond.RegisterInbound(registry)
|
||||
|
||||
registerQUICInbounds(registry)
|
||||
registerStubForRemovedInbounds(registry)
|
||||
registerMTProxyInbound(registry)
|
||||
|
||||
return registry
|
||||
}
|
||||
@@ -78,6 +93,7 @@ func OutboundRegistry() *outbound.Registry {
|
||||
|
||||
block.RegisterOutbound(registry)
|
||||
|
||||
group.RegisterFallback(registry)
|
||||
group.RegisterSelector(registry)
|
||||
group.RegisterURLTest(registry)
|
||||
|
||||
@@ -93,6 +109,14 @@ func OutboundRegistry() *outbound.Registry {
|
||||
vless.RegisterOutbound(registry)
|
||||
mieru.RegisterOutbound(registry)
|
||||
anytls.RegisterOutbound(registry)
|
||||
registerMASQUEOutbound(registry)
|
||||
|
||||
bond.RegisterOutbound(registry)
|
||||
|
||||
bandwidth.RegisterOutbound(registry)
|
||||
connection.RegisterOutbound(registry)
|
||||
|
||||
parser.RegisterOutbound(registry)
|
||||
|
||||
registerQUICOutbounds(registry)
|
||||
registerStubForRemovedOutbounds(registry)
|
||||
@@ -103,8 +127,8 @@ func OutboundRegistry() *outbound.Registry {
|
||||
func EndpointRegistry() *endpoint.Registry {
|
||||
registry := endpoint.NewRegistry()
|
||||
|
||||
tunnel.RegisterServerEndpoint(registry)
|
||||
tunnel.RegisterClientEndpoint(registry)
|
||||
vpn.RegisterServerEndpoint(registry)
|
||||
vpn.RegisterClientEndpoint(registry)
|
||||
|
||||
registerWireGuardEndpoint(registry)
|
||||
registerTailscaleEndpoint(registry)
|
||||
@@ -112,6 +136,16 @@ func EndpointRegistry() *endpoint.Registry {
|
||||
return registry
|
||||
}
|
||||
|
||||
func ProviderRegistry() *provider.Registry {
|
||||
registry := provider.NewRegistry()
|
||||
|
||||
localProvider.RegisterProviderInline(registry)
|
||||
localProvider.RegisterProviderLocal(registry)
|
||||
remoteProvider.RegisterProvider(registry)
|
||||
|
||||
return registry
|
||||
}
|
||||
|
||||
func DNSTransportRegistry() *dns.TransportRegistry {
|
||||
registry := dns.NewTransportRegistry()
|
||||
|
||||
@@ -135,6 +169,11 @@ func DNSTransportRegistry() *dns.TransportRegistry {
|
||||
func ServiceRegistry() *service.Registry {
|
||||
registry := service.NewRegistry()
|
||||
|
||||
admin_panel.RegisterService(registry)
|
||||
manager.RegisterService(registry)
|
||||
node.RegisterService(registry)
|
||||
nodeManagerClient.RegisterService(registry)
|
||||
nodeManagerServer.RegisterService(registry)
|
||||
resolved.RegisterService(registry)
|
||||
ssmapi.RegisterService(registry)
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@ package include
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
||||
"github.com/sagernet/sing-box/protocol/warp"
|
||||
"github.com/sagernet/sing-box/protocol/wireguard"
|
||||
)
|
||||
|
||||
func registerWireGuardEndpoint(registry *endpoint.Registry) {
|
||||
wireguard.RegisterEndpoint(registry)
|
||||
wireguard.RegisterWARPEndpoint(registry)
|
||||
warp.RegisterEndpoint(registry)
|
||||
}
|
||||
|
||||
27
log/id.go
27
log/id.go
@@ -13,6 +13,8 @@ func init() {
|
||||
}
|
||||
|
||||
type idKey struct{}
|
||||
type muxIdKey struct{}
|
||||
type hwidKey struct{}
|
||||
|
||||
type ID struct {
|
||||
ID uint32
|
||||
@@ -34,3 +36,28 @@ func IDFromContext(ctx context.Context) (ID, bool) {
|
||||
id, loaded := ctx.Value((*idKey)(nil)).(ID)
|
||||
return id, loaded
|
||||
}
|
||||
|
||||
func ContextWithNewMuxID(ctx context.Context) context.Context {
|
||||
return ContextWithMuxID(ctx, ID{
|
||||
ID: rand.Uint32(),
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
func ContextWithMuxID(ctx context.Context, id ID) context.Context {
|
||||
return context.WithValue(ctx, (*muxIdKey)(nil), id)
|
||||
}
|
||||
|
||||
func MuxIDFromContext(ctx context.Context) (ID, bool) {
|
||||
id, loaded := ctx.Value((*muxIdKey)(nil)).(ID)
|
||||
return id, loaded
|
||||
}
|
||||
|
||||
func ContextWithHWID(ctx context.Context, id ID) context.Context {
|
||||
return context.WithValue(ctx, (*hwidKey)(nil), id)
|
||||
}
|
||||
|
||||
func HWIDFromContext(ctx context.Context) (ID, bool) {
|
||||
id, loaded := ctx.Value((*hwidKey)(nil)).(ID)
|
||||
return id, loaded
|
||||
}
|
||||
|
||||
13
option/admin_panel.go
Normal file
13
option/admin_panel.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package option
|
||||
|
||||
type AdminPanelServiceOptions struct {
|
||||
ListenOptions
|
||||
Manager string `json:"manager"`
|
||||
Database AdminPanelServiceDatabase `json:"database"`
|
||||
InboundTLSOptionsContainer
|
||||
}
|
||||
|
||||
type AdminPanelServiceDatabase struct {
|
||||
Driver string `json:"driver"`
|
||||
DSN string `json:"dsn"`
|
||||
}
|
||||
16
option/bond.go
Normal file
16
option/bond.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package option
|
||||
|
||||
type BondInboundOptions struct {
|
||||
Inbounds []Inbound `json:"inbounds"`
|
||||
}
|
||||
|
||||
type BondOutboundOptions struct {
|
||||
Outbounds []BondOutbound `json:"outbounds"`
|
||||
}
|
||||
|
||||
type BondOutbound struct {
|
||||
Outbound Outbound `json:"outbound"`
|
||||
DownloadRatio uint8 `json:"download_ratio"`
|
||||
UploadRatio uint8 `json:"upload_ratio"`
|
||||
Count uint8 `json:"count"`
|
||||
}
|
||||
9
option/cloudflare.go
Normal file
9
option/cloudflare.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package option
|
||||
|
||||
type CloudflareProfile struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
AuthToken string `json:"auth_token,omitempty"`
|
||||
PrivateKey string `json:"private_key,omitempty"`
|
||||
Recreate bool `json:"recreate,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
}
|
||||
@@ -11,13 +11,14 @@ type ExperimentalOptions struct {
|
||||
}
|
||||
|
||||
type CacheFileOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
CacheID string `json:"cache_id,omitempty"`
|
||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||
StoreRDRC bool `json:"store_rdrc,omitempty"`
|
||||
StoreWARPConfig bool `json:"store_warp_config,omitempty"`
|
||||
RDRCTimeout badoption.Duration `json:"rdrc_timeout,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
CacheID string `json:"cache_id,omitempty"`
|
||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||
StoreRDRC bool `json:"store_rdrc,omitempty"`
|
||||
StoreWARPConfig bool `json:"store_warp_config,omitempty"`
|
||||
StoreMASQUEConfig bool `json:"store_masque_config,omitempty"`
|
||||
RDRCTimeout badoption.Duration `json:"rdrc_timeout,omitempty"`
|
||||
}
|
||||
|
||||
type ClashAPIOptions struct {
|
||||
|
||||
9
option/failover.go
Normal file
9
option/failover.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package option
|
||||
|
||||
type FailoverInboundOptions struct {
|
||||
Inbounds []Inbound `json:"inbounds"`
|
||||
}
|
||||
|
||||
type FailoverOutboundOptions struct {
|
||||
Outbounds []Outbound `json:"outbounds"`
|
||||
}
|
||||
@@ -3,16 +3,28 @@ package option
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type SelectorOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Default string `json:"default,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
GroupCommonOption
|
||||
Default string `json:"default,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
}
|
||||
|
||||
type URLTestOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
GroupCommonOption
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval badoption.Duration `json:"interval,omitempty"`
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||
}
|
||||
|
||||
type FallbackOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
}
|
||||
|
||||
type GroupCommonOption struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Providers []string `json:"providers"`
|
||||
Exclude *badoption.Regexp `json:"exclude,omitempty"`
|
||||
Include *badoption.Regexp `json:"include,omitempty"`
|
||||
UseAllProviders bool `json:"use_all_providers,omitempty"`
|
||||
}
|
||||
|
||||
37
option/limiter.go
Normal file
37
option/limiter.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing/common/byteformats"
|
||||
)
|
||||
|
||||
type BandwidthLimiterOutboundOptions struct {
|
||||
Strategy string `json:"strategy"`
|
||||
Mode string `json:"mode"`
|
||||
ConnectionType string `json:"connection_type,omitempty"`
|
||||
Speed *byteformats.NetworkBytesCompat `json:"speed"`
|
||||
Users []BandwidthLimiterUser `json:"users,omitempty"`
|
||||
Route RouteOptions `json:"route"`
|
||||
}
|
||||
|
||||
type BandwidthLimiterUser struct {
|
||||
Name string `json:"name"`
|
||||
Strategy string `json:"strategy"`
|
||||
Mode string `json:"mode"`
|
||||
ConnectionType string `json:"connection_type,omitempty"`
|
||||
Speed *byteformats.NetworkBytesCompat `json:"speed"`
|
||||
}
|
||||
|
||||
type ConnectionLimiterOutboundOptions struct {
|
||||
Strategy string `json:"strategy"`
|
||||
ConnectionType string `json:"connection_type,omitempty"`
|
||||
Count uint32 `json:"count"`
|
||||
Users []ConnectionLimiterUser `json:"users,omitempty"`
|
||||
Route RouteOptions `json:"route"`
|
||||
}
|
||||
|
||||
type ConnectionLimiterUser struct {
|
||||
Name string `json:"name"`
|
||||
Strategy string `json:"strategy"`
|
||||
ConnectionType string `json:"connection_type,omitempty"`
|
||||
Count uint32 `json:"count"`
|
||||
}
|
||||
11
option/manager.go
Normal file
11
option/manager.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package option
|
||||
|
||||
type ManagerServiceDatabase struct {
|
||||
Driver string `json:"driver"`
|
||||
DSN string `json:"dsn"`
|
||||
}
|
||||
|
||||
type ManagerServiceOptions struct {
|
||||
Inbounds []string `json:"inbounds"`
|
||||
Database ManagerServiceDatabase `json:"database"`
|
||||
}
|
||||
32
option/masque.go
Normal file
32
option/masque.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type MASQUEOutboundOptions struct {
|
||||
UseHTTP2 bool `json:"use_http2,omitempty"`
|
||||
UseIPv6 bool `json:"use_ipv6,omitempty"`
|
||||
Profile CloudflareProfile `json:"profile,omitempty"`
|
||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||
UDPKeepalivePeriod badoption.Duration `json:"udp_keepalive_period,omitempty"`
|
||||
UDPInitialPacketSize uint16 `json:"udp_initial_packet_size,omitempty"`
|
||||
ReconnectDelay badoption.Duration `json:"reconnect_delay,omitempty"`
|
||||
MASQUEOutboundTLSOptions
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
type MASQUEOutboundTLSOptions struct {
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
|
||||
CurvePreferences badoption.Listable[CurvePreference] `json:"curve_preferences,omitempty"`
|
||||
Fragment bool `json:"fragment,omitempty"`
|
||||
FragmentFallbackDelay badoption.Duration `json:"fragment_fallback_delay,omitempty"`
|
||||
RecordFragment bool `json:"record_fragment,omitempty"`
|
||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
||||
}
|
||||
|
||||
type MASQUEOutboundTLSOptionsContainer struct {
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
}
|
||||
89
option/mtproxy.go
Normal file
89
option/mtproxy.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
type MTProxyInboundOptions struct {
|
||||
ListenOptions
|
||||
Users []MTProxyUser `json:"users,omitempty"`
|
||||
Concurrency uint `json:"concurrency,omitempty"`
|
||||
DomainFrontingPort uint `json:"domain_fronting_port,omitempty"`
|
||||
DomainFrontingIP string `json:"domain_fronting_ip,omitempty"`
|
||||
DomainFrontingProxyProtocol bool `json:"domain_fronting_proxy_protocol,omitempty"`
|
||||
PreferIP string `json:"prefer_ip,omitempty"`
|
||||
AutoUpdate bool `json:"auto_update,omitempty"`
|
||||
AllowFallbackOnUnknownDC bool `json:"allow_fallback_on_unknown_dc,omitempty"`
|
||||
TolerateTimeSkewness badoption.Duration `json:"tolerate_time_skewness,omitempty"`
|
||||
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
||||
HandshakeTimeout badoption.Duration `json:"handshake_timeout,omitempty"`
|
||||
DoppelGangerURLs []string `json:"doppelganger_urls,omitempty"`
|
||||
DoppelGangerPerRaid uint `json:"doppelganger_per_raid,omitempty"`
|
||||
DoppelGangerEach badoption.Duration `json:"doppelganger_each,omitempty"`
|
||||
DoppelGangerDRS bool `json:"doppelganger_drs,omitempty"`
|
||||
ThrottleMaxConnections uint `json:"throttle_max_connections,omitempty"`
|
||||
ThrottleCheckInterval badoption.Duration `json:"throttle_check_interval,omitempty"`
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetConcurrency() uint {
|
||||
if o.Concurrency == 0 {
|
||||
return 8192
|
||||
}
|
||||
return o.Concurrency
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetDomainFrontingPort() uint {
|
||||
if o.DomainFrontingPort == 0 {
|
||||
return 443
|
||||
}
|
||||
return o.DomainFrontingPort
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetPreferIP() string {
|
||||
if o.PreferIP == "" {
|
||||
return "prefer-ipv4"
|
||||
}
|
||||
return o.PreferIP
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetIdleTimeout() time.Duration {
|
||||
if o.IdleTimeout == 0 {
|
||||
return 5 * time.Minute
|
||||
}
|
||||
return o.IdleTimeout.Build()
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetHandshakeTimeout() time.Duration {
|
||||
if o.HandshakeTimeout == 0 {
|
||||
return 10 * time.Second
|
||||
}
|
||||
return o.HandshakeTimeout.Build()
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetDoppelGangerPerRaid() uint {
|
||||
if o.DoppelGangerPerRaid == 0 {
|
||||
return 10
|
||||
}
|
||||
return o.DoppelGangerPerRaid
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetDoppelGangerEach() time.Duration {
|
||||
if o.HandshakeTimeout == 0 {
|
||||
return 6 * time.Hour
|
||||
}
|
||||
return o.DoppelGangerEach.Build()
|
||||
}
|
||||
|
||||
func (o *MTProxyInboundOptions) GetThrottleCheckInterval() time.Duration {
|
||||
if o.ThrottleCheckInterval == 0 {
|
||||
return 5 * time.Second
|
||||
}
|
||||
return o.ThrottleCheckInterval.Build()
|
||||
}
|
||||
|
||||
type MTProxyUser struct {
|
||||
Name string `json:"name"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
9
option/node.go
Normal file
9
option/node.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package option
|
||||
|
||||
type NodeServiceOptions struct {
|
||||
UUID string
|
||||
Inbounds []string `json:"inbounds"`
|
||||
ConnectionLimiters []string `json:"connection_limiters"`
|
||||
BandwidthLimiters []string `json:"bandwidth_limiters"`
|
||||
Manager string `json:"manager"`
|
||||
}
|
||||
13
option/node_manager.go
Normal file
13
option/node_manager.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package option
|
||||
|
||||
type NodeManagerServerServiceOptions struct {
|
||||
ListenOptions
|
||||
InboundTLSOptionsContainer
|
||||
Manager string `json:"manager"`
|
||||
}
|
||||
|
||||
type NodeManagerClientServiceOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
OutboundTLSOptionsContainer
|
||||
}
|
||||
@@ -19,6 +19,7 @@ type _Options struct {
|
||||
Endpoints []Endpoint `json:"endpoints,omitempty"`
|
||||
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||
Providers []Provider `json:"providers,omitempty"`
|
||||
Route *RouteOptions `json:"route,omitempty"`
|
||||
Services []Service `json:"services,omitempty"`
|
||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||
|
||||
6
option/parser.go
Normal file
6
option/parser.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package option
|
||||
|
||||
type ParserOutboundOptions struct {
|
||||
DialerOptions
|
||||
Link string `json:"link"`
|
||||
}
|
||||
75
option/provider.go
Normal file
75
option/provider.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
type ProviderOptionsRegistry interface {
|
||||
CreateOptions(providerType string) (any, bool)
|
||||
}
|
||||
type _Provider struct {
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Options any `json:"-"`
|
||||
}
|
||||
|
||||
type Provider _Provider
|
||||
|
||||
func (h *Provider) MarshalJSONContext(ctx context.Context) ([]byte, error) {
|
||||
return badjson.MarshallObjectsContext(ctx, (*_Provider)(h), h.Options)
|
||||
}
|
||||
|
||||
func (h *Provider) UnmarshalJSONContext(ctx context.Context, content []byte) error {
|
||||
err := json.UnmarshalContext(ctx, content, (*_Provider)(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registry := service.FromContext[ProviderOptionsRegistry](ctx)
|
||||
if registry == nil {
|
||||
return E.New("missing provider options registry in context")
|
||||
}
|
||||
options, loaded := registry.CreateOptions(h.Type)
|
||||
if !loaded {
|
||||
return E.New("unknown provider type: ", h.Type)
|
||||
}
|
||||
err = badjson.UnmarshallExcludedContext(ctx, content, (*_Provider)(h), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.Options = options
|
||||
return nil
|
||||
}
|
||||
|
||||
type ProviderLocalOptions struct {
|
||||
Path string `json:"path"`
|
||||
HealthCheck ProviderHealthCheckOptions `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderRemoteOptions struct {
|
||||
URL string `json:"url"`
|
||||
UserAgent string `json:"user_agent,omitempty"`
|
||||
DownloadDetour string `json:"download_detour,omitempty"`
|
||||
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
|
||||
|
||||
Exclude *badoption.Regexp `json:"exclude,omitempty"`
|
||||
Include *badoption.Regexp `json:"include,omitempty"`
|
||||
HealthCheck ProviderHealthCheckOptions `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderInlineOptions struct {
|
||||
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||
HealthCheck ProviderHealthCheckOptions `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderHealthCheckOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval badoption.Duration `json:"interval,omitempty"`
|
||||
Timeout badoption.Duration `json:"timeout,omitempty"`
|
||||
}
|
||||
@@ -88,8 +88,6 @@ type RawDefaultRule struct {
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
TunnelSource badoption.Listable[string] `json:"tunnel_source,omitempty"`
|
||||
TunnelDestination badoption.Listable[string] `json:"tunnel_destination,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
|
||||
@@ -155,9 +155,10 @@ type RouteActionOptions struct {
|
||||
}
|
||||
|
||||
type RawRouteOptionsActionOptions struct {
|
||||
OverrideAddress string `json:"override_address,omitempty"`
|
||||
OverridePort uint16 `json:"override_port,omitempty"`
|
||||
OverrideTunnelDestination string `json:"override_tunnel_destination,omitempty"`
|
||||
OverrideAddress string `json:"override_address,omitempty"`
|
||||
OverridePort uint16 `json:"override_port,omitempty"`
|
||||
|
||||
OverrideGateway string `json:"override_gateway,omitempty"`
|
||||
|
||||
NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"`
|
||||
FallbackDelay uint32 `json:"fallback_delay,omitempty"`
|
||||
|
||||
@@ -90,8 +90,6 @@ type RawDefaultDNSRule struct {
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
TunnelSource badoption.Listable[string] `json:"tunnel_source,omitempty"`
|
||||
TunnelDestination badoption.Listable[string] `json:"tunnel_destination,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user