Compare commits

..

16 Commits

Author SHA1 Message Date
世界
3105b8c920 Bump version 2022-09-25 22:27:23 +08:00
世界
4c67ab1a54 Fix read source address from grpc-go 2022-09-25 22:27:23 +08:00
世界
84783c5359 Fix fqdn socks5 outbound connection 2022-09-25 14:44:39 +08:00
世界
22b16f82bd Fix missing source address from transport connection 2022-09-25 14:44:33 +08:00
世界
d2add33723 Bump version 2022-09-15 13:12:18 +08:00
世界
ab0daf31c1 Fix clash api proxy type 2022-09-15 13:11:52 +08:00
世界
3d94b948dd Fix port rule match logic 2022-09-15 13:11:20 +08:00
世界
1659ae5d79 Fix close grpc conn 2022-09-15 13:10:18 +08:00
世界
7279855b08 Bump version 2022-09-13 11:25:38 +08:00
世界
925fbca363 Fix concurrent write 2022-09-13 10:36:37 +08:00
世界
a5163e3e3c Fix hysteria inbound 2022-09-13 10:32:14 +08:00
世界
62859e0c6b Fix socks4 client 2022-09-13 10:32:12 +08:00
世界
a37cab48d2 Bump version 2022-09-10 23:13:58 +08:00
世界
c586c8f361 Fix socks4 request 2022-09-10 22:53:06 +08:00
世界
e68fa3e12d Fix processing empty dns result 2022-09-10 22:52:54 +08:00
世界
7f5b9e0e3b Run build on main branch 2022-09-10 22:52:54 +08:00
273 changed files with 1733 additions and 25646 deletions

View File

@@ -1,34 +0,0 @@
name: Test build
on:
pull_request:
branches:
- main
- dev
- dev-next
jobs:
build:
name: Debug build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get latest go version
id: version
run: |
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ steps.version.outputs.go_version }}
- name: Cache go module
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
- name: Run Test
run: make test

3
.gitignore vendored
View File

@@ -4,5 +4,4 @@
/*.db
/site/
/bin/
/dist/
/sing-box
/dist/

View File

@@ -7,10 +7,6 @@ linters:
- staticcheck
- paralleltest
run:
skip-dirs:
- transport/cloudflaretls
linters-settings:
# gci:
# sections:

View File

@@ -9,9 +9,8 @@ builds:
gcflags:
- all=-trimpath={{.Env.GOPATH}}
ldflags:
- -s -w -buildid=
- -X github.com/sagernet/sing-box/constant.Commit={{ .ShortCommit }} -s -w -buildid=
tags:
- with_gvisor
- with_quic
- with_wireguard
- with_clash_api

View File

@@ -8,9 +8,9 @@ ENV CGO_ENABLED=0
RUN set -ex \
&& apk add git build-base \
&& export COMMIT=$(git rev-parse --short HEAD) \
&& go build -v -trimpath -tags with_quic,with_wireguard,with_acme \
&& go build -v -trimpath -tags 'no_gvisor,with_quic,with_wireguard,with_acme' \
-o /go/bin/sing-box \
-ldflags "-s -w -buildid=" \
-ldflags "-X github.com/sagernet/sing-box/constant.Commit=${COMMIT} -w -s -buildid=" \
./cmd/sing-box
FROM alpine AS dist
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"

View File

@@ -1,8 +1,9 @@
NAME = sing-box
COMMIT = $(shell git rev-parse --short HEAD)
TAGS ?= with_gvisor,with_quic,with_wireguard,with_clash_api
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
TAGS ?= with_quic,with_wireguard,with_clash_api
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \
'-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \
-w -s -buildid='
MAIN = ./cmd/sing-box
.PHONY: test release
@@ -60,19 +61,14 @@ release_install:
go install -v github.com/tcnksm/ghr@latest
test:
@go test -v ./... && \
cd test && \
@go test -v . && \
pushd test && \
go mod tidy && \
go test -v -tags "$(TAGS_TEST)" .
test_stdio:
@go test -v ./... && \
cd test && \
go mod tidy && \
go test -v -tags "$(TAGS_TEST),force_stdio" .
go test -v -tags with_quic,with_wireguard,with_grpc . && \
popd
clean:
rm -rf bin dist sing-box
rm -rf bin dist
rm -f $(shell go env GOPATH)/sing-box
update:

View File

@@ -4,29 +4,23 @@ import (
"context"
"net"
"github.com/sagernet/sing-box/common/urltest"
N "github.com/sagernet/sing/common/network"
)
type ClashServer interface {
Service
Mode() string
StoreSelected() bool
CacheFile() ClashCacheFile
HistoryStorage() *urltest.HistoryStorage
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
}
type ClashCacheFile interface {
LoadSelected(group string) string
StoreSelected(group string, selected string) error
TrafficController
}
type Tracker interface {
Leave()
}
type TrafficController interface {
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
}
type OutboundGroup interface {
Now() string
All() []string
@@ -38,13 +32,3 @@ func OutboundTag(detour Outbound) string {
}
return detour.Tag()
}
type V2RayServer interface {
Service
StatsService() V2RayStatsService
}
type V2RayStatsService interface {
RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn
RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn
}

View File

@@ -11,7 +11,7 @@ import (
"github.com/sagernet/sing/common/control"
N "github.com/sagernet/sing/common/network"
mdns "github.com/miekg/dns"
"golang.org/x/net/dns/dnsmessage"
)
type Router interface {
@@ -27,11 +27,11 @@ type Router interface {
GeoIPReader() *geoip.Reader
LoadGeosite(code string) (Rule, error)
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error)
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
InterfaceFinder() control.InterfaceFinder
InterfaceBindManager() control.BindManager
DefaultInterface() string
AutoDetectInterface() bool
DefaultMark() int
@@ -39,12 +39,7 @@ type Router interface {
InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager
Rules() []Rule
ClashServer() ClashServer
SetClashServer(server ClashServer)
V2RayServer() V2RayServer
SetV2RayServer(server V2RayServer)
SetTrafficController(controller TrafficController)
}
type Rule interface {

35
box.go
View File

@@ -31,7 +31,6 @@ type Box struct {
logger log.ContextLogger
logFile *os.File
clashServer adapter.ClashServer
v2rayServer adapter.V2RayServer
done chan struct{}
}
@@ -40,14 +39,8 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
logOptions := common.PtrValueOrDefault(options.Log)
var needClashAPI bool
var needV2RayAPI bool
if options.Experimental != nil {
if options.Experimental.ClashAPI != nil && options.Experimental.ClashAPI.ExternalController != "" {
needClashAPI = true
}
if options.Experimental.V2RayAPI != nil && options.Experimental.V2RayAPI.Listen != "" {
needV2RayAPI = true
}
if options.Experimental != nil && options.Experimental.ClashAPI != nil && options.Experimental.ClashAPI.ExternalController != "" {
needClashAPI = true
}
var logFactory log.Factory
@@ -156,20 +149,12 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
}
var clashServer adapter.ClashServer
var v2rayServer adapter.V2RayServer
if needClashAPI {
clashServer, err = experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI))
if err != nil {
return nil, E.Cause(err, "create clash api server")
}
router.SetClashServer(clashServer)
}
if needV2RayAPI {
v2rayServer, err = experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(options.Experimental.V2RayAPI))
if err != nil {
return nil, E.Cause(err, "create v2ray api server")
}
router.SetV2RayServer(v2rayServer)
router.SetTrafficController(clashServer)
}
return &Box{
router: router,
@@ -177,10 +162,9 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
outbounds: outbounds,
createdAt: createdAt,
logFactory: logFactory,
logger: logFactory.Logger(),
logger: logFactory.NewLogger(""),
logFile: logFile,
clashServer: clashServer,
v2rayServer: v2rayServer,
done: make(chan struct{}),
}, nil
}
@@ -239,12 +223,6 @@ func (s *Box) start() error {
return E.Cause(err, "start clash api server")
}
}
if s.v2rayServer != nil {
err = s.v2rayServer.Start()
if err != nil {
return E.Cause(err, "start v2ray api server")
}
}
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
return nil
}
@@ -266,11 +244,6 @@ func (s *Box) Close() error {
s.router,
s.logFactory,
s.clashServer,
s.v2rayServer,
common.PtrOrNil(s.logFile),
)
}
func (s *Box) Router() adapter.Router {
return s.router
}

View File

@@ -3,9 +3,9 @@ package main
import (
"os"
"runtime"
"runtime/debug"
C "github.com/sagernet/sing-box/constant"
F "github.com/sagernet/sing/common/format"
"github.com/spf13/cobra"
)
@@ -25,40 +25,30 @@ func init() {
}
func printVersion(cmd *cobra.Command, args []string) {
if nameOnly {
os.Stdout.WriteString(C.Version + "\n")
return
var version string
if !nameOnly {
version = "sing-box "
}
version := "sing-box version " + C.Version + "\n\n"
version += "Environment: " + runtime.Version() + " " + runtime.GOOS + "/" + runtime.GOARCH + "\n"
var tags string
var revision string
debugInfo, loaded := debug.ReadBuildInfo()
if loaded {
for _, setting := range debugInfo.Settings {
switch setting.Key {
case "-tags":
tags = setting.Value
case "vcs.revision":
revision = setting.Value
}
version += F.ToString(C.Version)
if C.Commit != "" {
version += "." + C.Commit
}
if !nameOnly {
version += " ("
version += runtime.Version()
version += ", "
version += runtime.GOOS
version += "/"
version += runtime.GOARCH
version += ", "
version += "CGO "
if C.CGO_ENABLED {
version += "enabled"
} else {
version += "disabled"
}
version += ")"
}
if tags != "" {
version += "Tags: " + tags + "\n"
}
if revision != "" {
version += "Revision: " + revision + "\n"
}
if C.CGO_ENABLED {
version += "CGO: enabled\n"
} else {
version += "CGO: disabled\n"
}
version += "\n"
os.Stdout.WriteString(version)
}

View File

@@ -3,7 +3,6 @@ package main
import (
"os"
_ "github.com/sagernet/sing-box/include"
"github.com/sagernet/sing-box/log"
"github.com/spf13/cobra"

View File

@@ -26,7 +26,7 @@ func WrapH2(err error) error {
if err == io.ErrUnexpectedEOF {
return io.EOF
}
if Contains(err, "client disconnected", "body closed by handler", "response body closed", "; CANCEL") {
if Contains(err, "client disconnected", "body closed by handler") {
return net.ErrClosed
}
return err

View File

@@ -1,210 +0,0 @@
//go:build go1.19 && !go1.20
package badtls
import (
"crypto/cipher"
"crypto/rand"
"crypto/tls"
"encoding/binary"
"io"
"net"
"reflect"
"sync"
"sync/atomic"
"unsafe"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
)
type Conn struct {
*tls.Conn
writer N.ExtendedWriter
activeCall *int32
closeNotifySent *bool
version *uint16
rand io.Reader
halfAccess *sync.Mutex
halfError *error
cipher cipher.AEAD
explicitNonceLen int
halfPtr uintptr
halfSeq []byte
halfScratchBuf []byte
}
func Create(conn *tls.Conn) (TLSConn, error) {
if !handshakeComplete(conn) {
return nil, E.New("handshake not finished")
}
rawConn := reflect.Indirect(reflect.ValueOf(conn))
rawActiveCall := rawConn.FieldByName("activeCall")
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Int32 {
return nil, E.New("badtls: invalid active call")
}
activeCall := (*int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr()))
rawHalfConn := rawConn.FieldByName("out")
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid half conn")
}
rawVersion := rawConn.FieldByName("vers")
if !rawVersion.IsValid() || rawVersion.Kind() != reflect.Uint16 {
return nil, E.New("badtls: invalid version")
}
version := (*uint16)(unsafe.Pointer(rawVersion.UnsafeAddr()))
rawCloseNotifySent := rawConn.FieldByName("closeNotifySent")
if !rawCloseNotifySent.IsValid() || rawCloseNotifySent.Kind() != reflect.Bool {
return nil, E.New("badtls: invalid notify")
}
closeNotifySent := (*bool)(unsafe.Pointer(rawCloseNotifySent.UnsafeAddr()))
rawConfig := reflect.Indirect(rawConn.FieldByName("config"))
if !rawConfig.IsValid() || rawConfig.Kind() != reflect.Struct {
return nil, E.New("badtls: bad config")
}
config := (*tls.Config)(unsafe.Pointer(rawConfig.UnsafeAddr()))
randReader := config.Rand
if randReader == nil {
randReader = rand.Reader
}
rawHalfMutex := rawHalfConn.FieldByName("Mutex")
if !rawHalfMutex.IsValid() || rawHalfMutex.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid half mutex")
}
halfAccess := (*sync.Mutex)(unsafe.Pointer(rawHalfMutex.UnsafeAddr()))
rawHalfError := rawHalfConn.FieldByName("err")
if !rawHalfError.IsValid() || rawHalfError.Kind() != reflect.Interface {
return nil, E.New("badtls: invalid half error")
}
halfError := (*error)(unsafe.Pointer(rawHalfError.UnsafeAddr()))
rawHalfCipherInterface := rawHalfConn.FieldByName("cipher")
if !rawHalfCipherInterface.IsValid() || rawHalfCipherInterface.Kind() != reflect.Interface {
return nil, E.New("badtls: invalid cipher interface")
}
rawHalfCipher := rawHalfCipherInterface.Elem()
aeadCipher, loaded := valueInterface(rawHalfCipher, false).(cipher.AEAD)
if !loaded {
return nil, E.New("badtls: invalid AEAD cipher")
}
var explicitNonceLen int
switch cipherName := reflect.Indirect(rawHalfCipher).Type().String(); cipherName {
case "tls.prefixNonceAEAD":
explicitNonceLen = aeadCipher.NonceSize()
case "tls.xorNonceAEAD":
default:
return nil, E.New("badtls: unknown cipher type: ", cipherName)
}
rawHalfSeq := rawHalfConn.FieldByName("seq")
if !rawHalfSeq.IsValid() || rawHalfSeq.Kind() != reflect.Array {
return nil, E.New("badtls: invalid seq")
}
halfSeq := rawHalfSeq.Bytes()
rawHalfScratchBuf := rawHalfConn.FieldByName("scratchBuf")
if !rawHalfScratchBuf.IsValid() || rawHalfScratchBuf.Kind() != reflect.Array {
return nil, E.New("badtls: invalid scratchBuf")
}
halfScratchBuf := rawHalfScratchBuf.Bytes()
return &Conn{
Conn: conn,
writer: bufio.NewExtendedWriter(conn.NetConn()),
activeCall: activeCall,
closeNotifySent: closeNotifySent,
version: version,
halfAccess: halfAccess,
halfError: halfError,
cipher: aeadCipher,
explicitNonceLen: explicitNonceLen,
rand: randReader,
halfPtr: rawHalfConn.UnsafeAddr(),
halfSeq: halfSeq,
halfScratchBuf: halfScratchBuf,
}, nil
}
func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
if buffer.Len() > maxPlaintext {
defer buffer.Release()
return common.Error(c.Write(buffer.Bytes()))
}
for {
x := atomic.LoadInt32(c.activeCall)
if x&1 != 0 {
return net.ErrClosed
}
if atomic.CompareAndSwapInt32(c.activeCall, x, x+2) {
break
}
}
defer atomic.AddInt32(c.activeCall, -2)
c.halfAccess.Lock()
defer c.halfAccess.Unlock()
if err := *c.halfError; err != nil {
return err
}
if *c.closeNotifySent {
return errShutdown
}
dataLen := buffer.Len()
dataBytes := buffer.Bytes()
outBuf := buffer.ExtendHeader(recordHeaderLen + c.explicitNonceLen)
outBuf[0] = 23
version := *c.version
if version == 0 {
version = tls.VersionTLS10
} else if version == tls.VersionTLS13 {
version = tls.VersionTLS12
}
binary.BigEndian.PutUint16(outBuf[1:], version)
var nonce []byte
if c.explicitNonceLen > 0 {
nonce = outBuf[5 : 5+c.explicitNonceLen]
if c.explicitNonceLen < 16 {
copy(nonce, c.halfSeq)
} else {
if _, err := io.ReadFull(c.rand, nonce); err != nil {
return err
}
}
}
if len(nonce) == 0 {
nonce = c.halfSeq
}
if *c.version == tls.VersionTLS13 {
buffer.FreeBytes()[0] = 23
binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+1+c.cipher.Overhead()))
c.cipher.Seal(outBuf, nonce, outBuf[recordHeaderLen:recordHeaderLen+c.explicitNonceLen+dataLen+1], outBuf[:recordHeaderLen])
buffer.Extend(1 + c.cipher.Overhead())
} else {
binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen))
additionalData := append(c.halfScratchBuf[:0], c.halfSeq...)
additionalData = append(additionalData, outBuf[:recordHeaderLen]...)
c.cipher.Seal(outBuf, nonce, dataBytes, additionalData)
buffer.Extend(c.cipher.Overhead())
binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+c.explicitNonceLen+c.cipher.Overhead()))
}
incSeq(c.halfPtr)
return c.writer.WriteBuffer(buffer)
}
func (c *Conn) FrontHeadroom() int {
return recordHeaderLen + c.explicitNonceLen
}
func (c *Conn) RearHeadroom() int {
return 1 + c.cipher.Overhead()
}
func (c *Conn) WriterMTU() int {
return maxPlaintext
}
func (c *Conn) Upstream() any {
return c.Conn
}
func (c *Conn) UpstreamWriter() any {
return c.NetConn()
}

View File

@@ -1,12 +0,0 @@
//go:build !go1.19 || go1.20
package badtls
import (
"crypto/tls"
"os"
)
func Create(conn *tls.Conn) (TLSConn, error) {
return nil, os.ErrInvalid
}

View File

@@ -1,13 +0,0 @@
package badtls
import (
"context"
"crypto/tls"
"net"
)
type TLSConn interface {
net.Conn
HandshakeContext(ctx context.Context) error
ConnectionState() tls.ConnectionState
}

View File

@@ -1,26 +0,0 @@
//go:build go1.19 && !go.1.20
package badtls
import (
"crypto/tls"
"reflect"
_ "unsafe"
)
const (
maxPlaintext = 16384 // maximum plaintext payload length
recordHeaderLen = 5 // record header length
)
//go:linkname errShutdown crypto/tls.errShutdown
var errShutdown error
//go:linkname handshakeComplete crypto/tls.(*Conn).handshakeComplete
func handshakeComplete(conn *tls.Conn) bool
//go:linkname incSeq crypto/tls.(*halfConn).incSeq
func incSeq(conn uintptr)
//go:linkname valueInterface reflect.valueInterface
func valueInterface(v reflect.Value, safe bool) any

View File

@@ -15,7 +15,7 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/database64128/tfo-go/v2"
"github.com/database64128/tfo-go"
)
var warnBindInterfaceOnUnsupportedPlatform = warning.New(
@@ -65,23 +65,25 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
var listener net.ListenConfig
if options.BindInterface != "" {
warnBindInterfaceOnUnsupportedPlatform.Check()
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else if router.AutoDetectInterface() {
const useInterfaceName = C.IsLinux
bindFunc := control.BindToInterfaceFunc(router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
remoteAddr := M.ParseSocksaddr(address).Addr
if C.IsLinux {
return router.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
} else {
return "", router.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
}
})
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
if C.IsWindows {
bindFunc := control.BindToInterfaceIndexFunc(func(network, address string) int {
return router.InterfaceMonitor().DefaultInterfaceIndex(M.ParseSocksaddr(address).Addr)
})
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
} else {
bindFunc := control.BindToInterfaceFunc(router.InterfaceBindManager(), func(network, address string) string {
return router.InterfaceMonitor().DefaultInterfaceName(M.ParseSocksaddr(address).Addr)
})
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
}
} else if router.DefaultInterface() != "" {
bindFunc := control.BindToInterface(router.InterfaceFinder(), router.DefaultInterface(), -1)
bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())
dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc)
}
@@ -110,16 +112,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
if options.TCPFastOpen {
warnTFOOnUnsupportedPlatform.Check()
}
var udpFragment bool
if options.UDPFragment != nil {
udpFragment = *options.UDPFragment
} else {
udpFragment = options.UDPFragmentDefault
}
if !udpFragment {
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
}
var bindUDPAddr string
udpDialer := dialer
var bindAddress netip.Addr
@@ -146,7 +138,7 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
case N.NetworkUDP:
return d.udpDialer.DialContext(ctx, network, address.String())
}
return DialSlowContext(&d.dialer, ctx, network, address)
return d.dialer.DialContext(ctx, network, address.Unwrap().String())
}
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {

View File

@@ -1,135 +0,0 @@
package dialer
import (
"context"
"io"
"net"
"os"
"time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/database64128/tfo-go/v2"
)
type slowOpenConn struct {
dialer *tfo.Dialer
ctx context.Context
network string
destination M.Socksaddr
conn net.Conn
create chan struct{}
err error
}
func DialSlowContext(dialer *tfo.Dialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if dialer.DisableTFO || N.NetworkName(network) != N.NetworkTCP {
return dialer.DialContext(ctx, network, destination.String(), nil)
}
return &slowOpenConn{
dialer: dialer,
ctx: ctx,
network: network,
destination: destination,
create: make(chan struct{}),
}, nil
}
func (c *slowOpenConn) Read(b []byte) (n int, err error) {
if c.conn == nil {
select {
case <-c.create:
if c.err != nil {
return 0, c.err
}
case <-c.ctx.Done():
return 0, c.ctx.Err()
}
}
return c.conn.Read(b)
}
func (c *slowOpenConn) Write(b []byte) (n int, err error) {
if c.conn == nil {
c.conn, err = c.dialer.DialContext(c.ctx, c.network, c.destination.String(), b)
c.err = err
close(c.create)
return
}
return c.conn.Write(b)
}
func (c *slowOpenConn) Close() error {
return common.Close(c.conn)
}
func (c *slowOpenConn) LocalAddr() net.Addr {
if c.conn == nil {
return M.Socksaddr{}
}
return c.conn.LocalAddr()
}
func (c *slowOpenConn) RemoteAddr() net.Addr {
if c.conn == nil {
return M.Socksaddr{}
}
return c.conn.RemoteAddr()
}
func (c *slowOpenConn) SetDeadline(t time.Time) error {
if c.conn == nil {
return os.ErrInvalid
}
return c.conn.SetDeadline(t)
}
func (c *slowOpenConn) SetReadDeadline(t time.Time) error {
if c.conn == nil {
return os.ErrInvalid
}
return c.conn.SetReadDeadline(t)
}
func (c *slowOpenConn) SetWriteDeadline(t time.Time) error {
if c.conn == nil {
return os.ErrInvalid
}
return c.conn.SetWriteDeadline(t)
}
func (c *slowOpenConn) Upstream() any {
return c.conn
}
func (c *slowOpenConn) ReaderReplaceable() bool {
return c.conn != nil
}
func (c *slowOpenConn) WriterReplaceable() bool {
return c.conn != nil
}
func (c *slowOpenConn) ReadFrom(r io.Reader) (n int64, err error) {
if c.conn != nil {
return bufio.Copy(c.conn, r)
}
return bufio.ReadFrom0(c, r)
}
func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
if c.conn == nil {
select {
case <-c.create:
if c.err != nil {
return 0, c.err
}
case <-c.ctx.Done():
return 0, c.ctx.Err()
}
}
return bufio.Copy(w, c.conn)
}

View File

@@ -1,26 +1,34 @@
package tls
package dialer
import (
"context"
"crypto/tls"
"crypto/x509"
"net"
"net/netip"
"os"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type stdClientConfig struct {
type TLSDialer struct {
dialer N.Dialer
config *tls.Config
}
func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) {
func TLSConfig(serverAddress string, options option.OutboundTLSOptions) (*tls.Config, error) {
if !options.Enabled {
return nil, nil
}
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
if _, err := netip.ParseAddr(serverName); err != nil {
if _, err := netip.ParseAddr(serverName); err == nil {
serverName = serverAddress
}
}
@@ -54,14 +62,14 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
minVersion, err := option.ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
@@ -96,21 +104,42 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
}
tlsConfig.RootCAs = certPool
}
return &stdClientConfig{&tlsConfig}, nil
return &tlsConfig, nil
}
func (s *stdClientConfig) NextProtos() []string {
return s.config.NextProtos
func NewTLS(dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
if !options.Enabled {
return dialer, nil
}
tlsConfig, err := TLSConfig(serverAddress, options)
if err != nil {
return nil, err
}
return &TLSDialer{
dialer: dialer,
config: tlsConfig,
}, nil
}
func (s *stdClientConfig) SetNextProtos(nextProto []string) {
s.config.NextProtos = nextProto
func (d *TLSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if network != N.NetworkTCP {
return nil, os.ErrInvalid
}
conn, err := d.dialer.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
return TLSClient(ctx, conn, d.config)
}
func (s *stdClientConfig) Config() (*STDConfig, error) {
return s.config, nil
func (d *TLSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid
}
func (s *stdClientConfig) Client(conn net.Conn) Conn {
return tls.Client(conn, s.config)
func TLSClient(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) (*tls.Conn, error) {
tlsConn := tls.Client(conn, tlsConfig)
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel()
err := tlsConn.HandshakeContext(ctx)
return tlsConn, err
}

View File

@@ -1,128 +0,0 @@
package json
import (
"bufio"
"io"
)
// kanged from v2ray
type commentFilterState = byte
const (
commentFilterStateContent commentFilterState = iota
commentFilterStateEscape
commentFilterStateDoubleQuote
commentFilterStateDoubleQuoteEscape
commentFilterStateSingleQuote
commentFilterStateSingleQuoteEscape
commentFilterStateComment
commentFilterStateSlash
commentFilterStateMultilineComment
commentFilterStateMultilineCommentStar
)
type CommentFilter struct {
br *bufio.Reader
state commentFilterState
}
func NewCommentFilter(reader io.Reader) io.Reader {
return &CommentFilter{br: bufio.NewReader(reader)}
}
func (v *CommentFilter) Read(b []byte) (int, error) {
p := b[:0]
for len(p) < len(b)-2 {
x, err := v.br.ReadByte()
if err != nil {
if len(p) == 0 {
return 0, err
}
return len(p), nil
}
switch v.state {
case commentFilterStateContent:
switch x {
case '"':
v.state = commentFilterStateDoubleQuote
p = append(p, x)
case '\'':
v.state = commentFilterStateSingleQuote
p = append(p, x)
case '\\':
v.state = commentFilterStateEscape
case '#':
v.state = commentFilterStateComment
case '/':
v.state = commentFilterStateSlash
default:
p = append(p, x)
}
case commentFilterStateEscape:
p = append(p, '\\', x)
v.state = commentFilterStateContent
case commentFilterStateDoubleQuote:
switch x {
case '"':
v.state = commentFilterStateContent
p = append(p, x)
case '\\':
v.state = commentFilterStateDoubleQuoteEscape
default:
p = append(p, x)
}
case commentFilterStateDoubleQuoteEscape:
p = append(p, '\\', x)
v.state = commentFilterStateDoubleQuote
case commentFilterStateSingleQuote:
switch x {
case '\'':
v.state = commentFilterStateContent
p = append(p, x)
case '\\':
v.state = commentFilterStateSingleQuoteEscape
default:
p = append(p, x)
}
case commentFilterStateSingleQuoteEscape:
p = append(p, '\\', x)
v.state = commentFilterStateSingleQuote
case commentFilterStateComment:
if x == '\n' {
v.state = commentFilterStateContent
p = append(p, '\n')
}
case commentFilterStateSlash:
switch x {
case '/':
v.state = commentFilterStateComment
case '*':
v.state = commentFilterStateMultilineComment
default:
p = append(p, '/', x)
}
case commentFilterStateMultilineComment:
switch x {
case '*':
v.state = commentFilterStateMultilineCommentStar
case '\n':
p = append(p, '\n')
}
case commentFilterStateMultilineCommentStar:
switch x {
case '/':
v.state = commentFilterStateContent
case '*':
// Stay
case '\n':
p = append(p, '\n')
default:
v.state = commentFilterStateMultilineComment
}
default:
panic("Unknown state.")
}
}
return len(p), nil
}

View File

@@ -466,7 +466,10 @@ func (c *ClientPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
if err != nil {
return
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
if buffer.FreeLen() < int(length) {
return destination, io.ErrShortBuffer
}
_, err = io.ReadFull(c.ExtendedConn, buffer.Extend(int(length)))
return
}

View File

@@ -3,6 +3,7 @@ package mux
import (
"context"
"encoding/binary"
"io"
"net"
"github.com/sagernet/sing-box/adapter"
@@ -14,7 +15,6 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/common/task"
)
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
@@ -26,21 +26,14 @@ func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
if err != nil {
return err
}
var group task.Group
group.Append0(func(ctx context.Context) error {
var stream net.Conn
for {
stream, err = session.Accept()
if err != nil {
return err
}
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
var stream net.Conn
for {
stream, err = session.Accept()
if err != nil {
return err
}
})
group.Cleanup(func() {
session.Close()
})
return group.Run(ctx)
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
}
}
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
@@ -165,6 +158,9 @@ func (c *ServerPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksad
if err != nil {
return
}
if buffer.FreeLen() < int(length) {
return destination, io.ErrShortBuffer
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
if err != nil {
return
@@ -227,6 +223,9 @@ func (c *ServerPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
if err != nil {
return
}
if buffer.FreeLen() < int(length) {
return destination, io.ErrShortBuffer
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
if err != nil {
return

View File

@@ -28,5 +28,11 @@ type Info struct {
}
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
return findProcessInfo(searcher, ctx, network, source, destination)
info, err := findProcessInfo(searcher, ctx, network, source, destination)
if err != nil {
if source.Addr().Is4In6() {
info, err = findProcessInfo(searcher, ctx, network, netip.AddrPortFrom(netip.AddrFrom4(source.Addr().As4()), source.Port()), destination)
}
}
return info, err
}

View File

@@ -13,7 +13,6 @@ import (
type Listener struct {
net.Listener
AcceptNoHeader bool
}
func (l *Listener) Accept() (net.Conn, error) {
@@ -23,7 +22,7 @@ func (l *Listener) Accept() (net.Conn, error) {
}
bufReader := std_bufio.NewReader(conn)
header, err := proxyproto.Read(bufReader)
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
if err != nil {
return nil, err
}
if bufReader.Buffered() > 0 {
@@ -34,11 +33,8 @@ func (l *Listener) Accept() (net.Conn, error) {
}
conn = bufio.NewCachedConn(conn, cache)
}
if header != nil {
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
Source: M.SocksaddrFromNet(header.SourceAddr).Unwrap(),
Destination: M.SocksaddrFromNet(header.DestinationAddr).Unwrap(),
}}, nil
}
return conn, nil
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
Source: M.SocksaddrFromNet(header.SourceAddr),
Destination: M.SocksaddrFromNet(header.DestinationAddr),
}}, nil
}

View File

@@ -2,11 +2,14 @@ package redir
import (
"encoding/binary"
"net"
"net/netip"
"os"
"strconv"
"syscall"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
"golang.org/x/sys/unix"
@@ -29,18 +32,6 @@ func TProxy(fd uintptr, isIPv6 bool) error {
return err
}
func TProxyWriteBack() control.Func {
return func(network, address string, conn syscall.RawConn) error {
return control.Raw(conn, func(fd uintptr) error {
if M.ParseSocksaddr(address).Addr.Is6() {
return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
} else {
return syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
}
})
}
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
controlMessages, err := unix.ParseSocketControlMessage(oob)
if err != nil {
@@ -55,3 +46,79 @@ func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
}
return netip.AddrPort{}, E.New("not found")
}
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
rSockAddr, err := udpAddrToSockAddr(rAddr)
if err != nil {
return nil, err
}
lSockAddr, err := udpAddrToSockAddr(lAddr)
if err != nil {
return nil, err
}
fd, err := syscall.Socket(udpAddrFamily(lAddr, rAddr), syscall.SOCK_DGRAM, 0)
if err != nil {
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Bind(fd, lSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Connect(fd, rSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr))
defer fdFile.Close()
c, err := net.FileConn(fdFile)
if err != nil {
syscall.Close(fd)
return nil, err
}
return c.(*net.UDPConn), nil
}
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
switch {
case addr.IP.To4() != nil:
ip := [4]byte{}
copy(ip[:], addr.IP.To4())
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
default:
ip := [16]byte{}
copy(ip[:], addr.IP.To16())
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
if err != nil {
zoneID = 0
}
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
}
}
func udpAddrFamily(lAddr, rAddr *net.UDPAddr) int {
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
return syscall.AF_INET
}
return syscall.AF_INET6
}

View File

@@ -3,20 +3,19 @@
package redir
import (
"net"
"net/netip"
"os"
"github.com/sagernet/sing/common/control"
)
func TProxy(fd uintptr, isIPv6 bool) error {
return os.ErrInvalid
}
func TProxyWriteBack() control.Func {
return nil
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, os.ErrInvalid
}
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
return nil, os.ErrInvalid
}

View File

@@ -20,7 +20,7 @@ type systemProxy struct {
isMixed bool
}
func (p *systemProxy) update(event int) error {
func (p *systemProxy) update() error {
newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
if p.interfaceName == newInterfaceName {
return nil
@@ -88,7 +88,7 @@ func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() er
port: port,
isMixed: isMixed,
}
err := proxy.update(tun.EventInterfaceUpdate)
err := proxy.update()
if err != nil {
return nil, err
}

View File

@@ -11,10 +11,9 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/task"
mDNS "github.com/miekg/dns"
"golang.org/x/net/dns/dnsmessage"
)
func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.InboundContext, error) {
@@ -45,13 +44,18 @@ func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.
}
func DomainNameQuery(ctx context.Context, packet []byte) (*adapter.InboundContext, error) {
var msg mDNS.Msg
err := msg.Unpack(packet)
var parser dnsmessage.Parser
_, err := parser.Start(packet)
if err != nil {
return nil, err
}
if len(msg.Question) == 0 || msg.Question[0].Qclass != mDNS.ClassINET || !M.IsDomainName(msg.Question[0].Name) {
question, err := parser.Question()
if err != nil {
return nil, os.ErrInvalid
}
return &adapter.InboundContext{Protocol: C.ProtocolDNS}, nil
domain := question.Name.String()
if question.Class == dnsmessage.ClassINET && IsDomainName(domain) {
return &adapter.InboundContext{Protocol: C.ProtocolDNS /*, Domain: domain*/}, nil
}
return nil, os.ErrInvalid
}

6
common/sniff/domain.go Normal file
View File

@@ -0,0 +1,6 @@
package sniff
import _ "unsafe" // for linkname
//go:linkname IsDomainName net.isDomainName
func IsDomainName(domain string) bool

View File

@@ -1,75 +0,0 @@
package tls
import (
"context"
"crypto/tls"
"net"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/badtls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
config, err := NewClient(router, serverAddress, options)
if err != nil {
return nil, err
}
return NewDialer(dialer, config), nil
}
func NewClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
if options.ECH != nil && options.ECH.Enabled {
return newECHClient(router, serverAddress, options)
} else if options.UTLS != nil && options.UTLS.Enabled {
return newUTLSClient(router, serverAddress, options)
} else {
return newStdClient(serverAddress, options)
}
}
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
tlsConn := config.Client(conn)
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel()
err := tlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err
}
if stdConn, isSTD := tlsConn.(*tls.Conn); isSTD {
var badConn badtls.TLSConn
badConn, err = badtls.Create(stdConn)
if err == nil {
return badConn, nil
}
}
return tlsConn, nil
}
type Dialer struct {
dialer N.Dialer
config Config
}
func NewDialer(dialer N.Dialer, config Config) N.Dialer {
return &Dialer{dialer, config}
}
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if network != N.NetworkTCP {
return nil, os.ErrInvalid
}
conn, err := d.dialer.DialContext(ctx, network, destination)
if err != nil {
return nil, err
}
return ClientHandshake(ctx, conn, d.config)
}
func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid
}

View File

@@ -1,12 +0,0 @@
package tls
const (
VersionTLS10 = 0x0301
VersionTLS11 = 0x0302
VersionTLS12 = 0x0303
VersionTLS13 = 0x0304
// Deprecated: SSLv3 is cryptographically broken, and is no longer
// supported by this package. See golang.org/issue/32716.
VersionSSL30 = 0x0300
)

View File

@@ -1,49 +0,0 @@
package tls
import (
"context"
"crypto/tls"
"net"
"github.com/sagernet/sing-box/adapter"
E "github.com/sagernet/sing/common/exceptions"
)
type (
STDConfig = tls.Config
STDConn = tls.Conn
)
type Config interface {
NextProtos() []string
SetNextProtos(nextProto []string)
Config() (*STDConfig, error)
Client(conn net.Conn) Conn
}
type ServerConfig interface {
Config
adapter.Service
Server(conn net.Conn) Conn
}
type Conn interface {
net.Conn
HandshakeContext(ctx context.Context) error
ConnectionState() tls.ConnectionState
}
func ParseTLSVersion(version string) (uint16, error) {
switch version {
case "1.0":
return tls.VersionTLS10, nil
case "1.1":
return tls.VersionTLS11, nil
case "1.2":
return tls.VersionTLS12, nil
case "1.3":
return tls.VersionTLS13, nil
default:
return 0, E.New("unknown tls version:", version)
}
}

View File

@@ -1,207 +0,0 @@
//go:build with_ech
package tls
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"net"
"net/netip"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
cftls "github.com/sagernet/sing-box/transport/cloudflaretls"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
mDNS "github.com/miekg/dns"
)
type echClientConfig struct {
config *cftls.Config
}
func (e *echClientConfig) NextProtos() []string {
return e.config.NextProtos
}
func (e *echClientConfig) SetNextProtos(nextProto []string) {
e.config.NextProtos = nextProto
}
func (e *echClientConfig) Config() (*STDConfig, error) {
return nil, E.New("unsupported usage for ECH")
}
func (e *echClientConfig) Client(conn net.Conn) Conn {
return &echConnWrapper{cftls.Client(conn, e.config)}
}
type echConnWrapper struct {
*cftls.Conn
}
func (c *echConnWrapper) ConnectionState() tls.ConnectionState {
state := c.Conn.ConnectionState()
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,
DidResume: state.DidResume,
CipherSuite: state.CipherSuite,
NegotiatedProtocol: state.NegotiatedProtocol,
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
ServerName: state.ServerName,
PeerCertificates: state.PeerCertificates,
VerifiedChains: state.VerifiedChains,
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
OCSPResponse: state.OCSPResponse,
TLSUnique: state.TLSUnique,
}
}
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
if _, err := netip.ParseAddr(serverName); err != nil {
serverName = serverAddress
}
}
if serverName == "" && !options.Insecure {
return nil, E.New("missing server_name or insecure=true")
}
var tlsConfig cftls.Config
if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1"
} else {
tlsConfig.ServerName = serverName
}
if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI {
tlsConfig.InsecureSkipVerify = true
tlsConfig.VerifyConnection = func(state cftls.ConnectionState) error {
verifyOptions := x509.VerifyOptions{
DNSName: serverName,
Intermediates: x509.NewCertPool(),
}
for _, cert := range state.PeerCertificates[1:] {
verifyOptions.Intermediates.AddCert(cert)
}
_, err := state.PeerCertificates[0].Verify(verifyOptions)
return err
}
}
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
tlsConfig.MaxVersion = maxVersion
}
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range cftls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
var certificate []byte
if options.Certificate != "" {
certificate = []byte(options.Certificate)
} else if options.CertificatePath != "" {
content, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate")
}
certificate = content
}
if len(certificate) > 0 {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certificate) {
return nil, E.New("failed to parse certificate:\n\n", certificate)
}
tlsConfig.RootCAs = certPool
}
// ECH Config
tlsConfig.ECHEnabled = true
tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled
tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled
if options.ECH.Config != "" {
clientConfigContent, err := base64.StdEncoding.DecodeString(options.ECH.Config)
if err != nil {
return nil, err
}
clientConfig, err := cftls.UnmarshalECHConfigs(clientConfigContent)
if err != nil {
return nil, err
}
tlsConfig.ClientECHConfigs = clientConfig
} else {
tlsConfig.GetClientECHConfigs = fetchECHClientConfig(router)
}
return &echClientConfig{&tlsConfig}, nil
}
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
message := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{
RecursionDesired: true,
},
Question: []mDNS.Question{
{
Name: serverName + ".",
Qtype: mDNS.TypeHTTPS,
Qclass: mDNS.ClassINET,
},
},
}
response, err := router.Exchange(ctx, message)
if err != nil {
return nil, err
}
if response.Rcode != mDNS.RcodeSuccess {
return nil, dns.RCodeError(response.Rcode)
}
for _, rr := range response.Answer {
switch resource := rr.(type) {
case *mDNS.HTTPS:
for _, value := range resource.Value {
if value.Key().String() == "ech" {
echConfig, err := base64.StdEncoding.DecodeString(value.String())
if err != nil {
return nil, E.Cause(err, "decode ECH config")
}
return cftls.UnmarshalECHConfigs(echConfig)
}
}
default:
return nil, E.New("unknown resource record type: ", resource.Header().Rrtype)
}
}
return nil, E.New("no ECH config found")
}
}

View File

@@ -1,13 +0,0 @@
//go:build !with_ech
package tls
import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
)
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
return nil, E.New(`ECH is not included in this build, rebuild with -tags with_ech`)
}

View File

@@ -1,50 +0,0 @@
package tls
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"time"
)
func GenerateKeyPair(serverName string) (*tls.Certificate, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, err
}
template := &x509.Certificate{
SerialNumber: serialNumber,
NotBefore: time.Now().Add(time.Hour * -1),
NotAfter: time.Now().Add(time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
Subject: pkix.Name{
CommonName: serverName,
},
DNSNames: []string{serverName},
}
publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
if err != nil {
return nil, err
}
privateDer, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
publicPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer})
privPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDer})
keyPair, err := tls.X509KeyPair(publicPem, privPem)
if err != nil {
return nil, err
}
return &keyPair, err
}

View File

@@ -1,34 +0,0 @@
package tls
import (
"context"
"crypto/tls"
"net"
"github.com/sagernet/sing-box/common/badtls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
)
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
return newSTDServer(ctx, logger, options)
}
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
tlsConn := config.Server(conn)
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel()
err := tlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err
}
if stdConn, isSTD := tlsConn.(*tls.Conn); isSTD {
var badConn badtls.TLSConn
badConn, err = badtls.Create(stdConn)
if err == nil {
return badConn, nil
}
}
return tlsConn, nil
}

View File

@@ -1,151 +0,0 @@
//go:build with_utls
package tls
import (
"context"
"crypto/tls"
"crypto/x509"
"net"
"net/netip"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
utls "github.com/refraction-networking/utls"
)
type utlsClientConfig struct {
config *utls.Config
id utls.ClientHelloID
}
func (e *utlsClientConfig) NextProtos() []string {
return e.config.NextProtos
}
func (e *utlsClientConfig) SetNextProtos(nextProto []string) {
e.config.NextProtos = nextProto
}
func (e *utlsClientConfig) Config() (*STDConfig, error) {
return nil, E.New("unsupported usage for uTLS")
}
func (e *utlsClientConfig) Client(conn net.Conn) Conn {
return &utlsConnWrapper{utls.UClient(conn, e.config, e.id)}
}
type utlsConnWrapper struct {
*utls.UConn
}
func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error {
return c.Conn.Handshake()
}
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
state := c.Conn.ConnectionState()
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,
DidResume: state.DidResume,
CipherSuite: state.CipherSuite,
NegotiatedProtocol: state.NegotiatedProtocol,
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
ServerName: state.ServerName,
PeerCertificates: state.PeerCertificates,
VerifiedChains: state.VerifiedChains,
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
OCSPResponse: state.OCSPResponse,
TLSUnique: state.TLSUnique,
}
}
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
if _, err := netip.ParseAddr(serverName); err != nil {
serverName = serverAddress
}
}
if serverName == "" && !options.Insecure {
return nil, E.New("missing server_name or insecure=true")
}
var tlsConfig utls.Config
if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1"
} else {
tlsConfig.ServerName = serverName
}
if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI {
return nil, E.New("disable_sni is unsupported in uTLS")
}
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
tlsConfig.MaxVersion = maxVersion
}
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)
}
}
var certificate []byte
if options.Certificate != "" {
certificate = []byte(options.Certificate)
} else if options.CertificatePath != "" {
content, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate")
}
certificate = content
}
if len(certificate) > 0 {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certificate) {
return nil, E.New("failed to parse certificate:\n\n", certificate)
}
tlsConfig.RootCAs = certPool
}
var id utls.ClientHelloID
switch options.UTLS.Fingerprint {
case "chrome", "":
id = utls.HelloChrome_Auto
case "firefox":
id = utls.HelloFirefox_Auto
case "ios":
id = utls.HelloIOS_Auto
case "android":
id = utls.HelloAndroid_11_OkHttp
case "random":
id = utls.HelloRandomized
}
return &utlsClientConfig{&tlsConfig, id}, nil
}

View File

@@ -1,13 +0,0 @@
//go:build !with_utls
package tls
import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
)
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
}

View File

@@ -3,5 +3,3 @@ package constant
import E "github.com/sagernet/sing/common/exceptions"
var ErrTLSRequired = E.New("TLS required")
var ErrQUICNotIncluded = E.New(`QUIC is not included in this build, rebuild with -tags with_quic`)

View File

@@ -1,29 +1,26 @@
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"
TypeHysteria = "hysteria"
TypeTor = "tor"
TypeSSH = "ssh"
TypeShadowTLS = "shadowtls"
TypeShadowsocksR = "shadowsocksr"
TypeVLESS = "vless"
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"
TypeHysteria = "hysteria"
TypeTor = "tor"
TypeSSH = "ssh"
TypeShadowTLS = "shadowtls"
)
const (
TypeSelector = "selector"
TypeURLTest = "urltest"
)

5
constant/quic.go Normal file
View File

@@ -0,0 +1,5 @@
//go:build with_quic
package constant
const QUIC_AVAILABLE = true

9
constant/quic_stub.go Normal file
View File

@@ -0,0 +1,9 @@
//go:build !with_quic
package constant
import E "github.com/sagernet/sing/common/exceptions"
const QUIC_AVAILABLE = false
var ErrQUICNotIncluded = E.New(`QUIC is not included in this build, rebuild with -tags with_quic`)

View File

@@ -3,11 +3,10 @@ package constant
import "time"
const (
TCPTimeout = 5 * time.Second
ReadPayloadTimeout = 300 * time.Millisecond
DNSTimeout = 10 * time.Second
QUICTimeout = 30 * time.Second
STUNTimeout = 15 * time.Second
UDPTimeout = 5 * time.Minute
DefaultURLTestInterval = 1 * time.Minute
TCPTimeout = 5 * time.Second
ReadPayloadTimeout = 300 * time.Millisecond
DNSTimeout = 10 * time.Second
QUICTimeout = 30 * time.Second
STUNTimeout = 15 * time.Second
UDPTimeout = 5 * time.Minute
)

View File

@@ -1,3 +1,6 @@
package constant
var Version = "1.1-beta9"
var (
Version = "1.0.5"
Commit = ""
)

View File

@@ -1,164 +1,3 @@
#### 1.1-beta9
* Fix windows route **1**
* Add [v2ray statistics api](/configuration/experimental#v2ray-api-fields)
* Add ShadowTLS v2 support **2**
* Fixes and improvements
**1**:
* Fix DNS leak caused by
Windows' [ordinary multihomed DNS resolution behavior](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29)
* Flush Windows DNS cache when start/close
**2**:
See [ShadowTLS inbound](/configuration/inbound/shadowtls#version) and [ShadowTLS outbound](/configuration/outbound/shadowtls#version)
#### 1.1-beta8
* Fix leaks on close
* Improve websocket writer
* Refine tproxy write back
* Refine 4in6 processing
* Fix shadowsocks plugins
* Fix missing source address from transport connection
* Fix fqdn socks5 outbound connection
* Fix read source address from grpc-go
#### 1.0.5
* Fix missing source address from transport connection
* Fix fqdn socks5 outbound connection
* Fix read source address from grpc-go
#### 1.1-beta7
* Add v2ray mux and XUDP support for VMess inbound
* Add XUDP support for VMess outbound
* Disable DF on direct outbound by default
* Fix bugs in 1.1-beta6
#### 1.1-beta6
* Add [URLTest outbound](/configuration/outbound/urltest)
* Fix bugs in 1.1-beta5
#### 1.1-beta5
* Print tags in version command
* Redirect clash hello to external ui
* Move shadowsocksr implementation to clash
* Make gVisor optional **1**
* Refactor to miekg/dns
* Refactor bind control
* Fix build on go1.18
* Fix clash store-selected
* Fix close grpc conn
* Fix port rule match logic
* Fix clash api proxy type
*1*:
The build tag `no_gvisor` is replaced by `with_gvisor`.
The default tun stack is changed to system.
#### 1.0.4
* Fix close grpc conn
* Fix port rule match logic
* Fix clash api proxy type
#### 1.1-beta4
* Add internal simple-obfs and v2ray-plugin [Shadowsocks plugins](/configuration/outbound/shadowsocks#plugin)
* Add [ShadowsocksR outbound](/configuration/outbound/shadowsocksr)
* Add [VLESS outbound and XUDP](/configuration/outbound/vless)
* Skip wait for hysteria tcp handshake response
* Fix socks4 client
* Fix hysteria inbound
* Fix concurrent write
#### 1.0.3
* Fix socks4 client
* Fix hysteria inbound
* Fix concurrent write
#### 1.1-beta3
* Fix using custom TLS client in http2 client
* Fix bugs in 1.1-beta2
#### 1.1-beta2
* Add Clash mode and persistence support **1**
* Add TLS ECH and uTLS support for outbound TLS options **2**
* Fix socks4 request
* Fix processing empty dns result
*1*:
Switching modes using the Clash API, and `store-selected` are now supported,
see [Experimental](/configuration/experimental).
*2*:
ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello
message, see [TLS#ECH](/configuration/shared/tls#ech).
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance,
see [TLS#uTLS](/configuration/shared/tls#utls).
#### 1.0.2
* Fix socks4 request
* Fix processing empty dns result
#### 1.1-beta1
* Add support for use with android VPNService **1**
* Add tun support for WireGuard outbound **2**
* Add system tun stack **3**
* Add comment filter for config **4**
* Add option for allow optional proxy protocol header
* Add half close for smux
* Set UDP DF by default **5**
* Set default tun mtu to 9000
* Update gVisor to 20220905.0
*1*:
In previous versions, Android VPN would not work with tun enabled.
The usage of tun over VPN and VPN over tun is now supported, see [Tun Inbound](/configuration/inbound/tun#auto_route).
*2*:
In previous releases, WireGuard outbound support was backed by the lower performance gVisor virtual interface.
It achieves the same performance as wireguard-go by providing automatic system interface support.
*3*:
It does not depend on gVisor and has better performance in some cases.
It is less compatible and may not be available in some environments.
*4*:
Annotated json configuration files are now supported.
*5*:
UDP fragmentation is now blocked by default.
Including shadowsocks-libev, shadowsocks-rust and quic-go all disable segmentation by default.
See [Dial Fields](/configuration/shared/dial#udp_fragment)
and [Listen Fields](/configuration/shared/listen#udp_fragment).
#### 1.0.1
* Fix match 4in6 address in ip_cidr

View File

@@ -73,7 +73,6 @@
"user_id": [
1000
],
"clash_mode": "direct",
"invert": false,
"outbound": [
"direct"
@@ -104,10 +103,8 @@
The default rule uses the following matching logic:
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
(`port` || `port_range`) &&
(`source_geoip` || `source_ip_cidr`) &&
(`source_port` || `source_port_range`) &&
`other fields`
`other fields`
#### inbound
@@ -211,10 +208,6 @@ Match user name.
Match user id.
#### clash_mode
Match Clash mode.
#### invert
Invert match result.

View File

@@ -72,7 +72,6 @@
"user_id": [
1000
],
"clash_mode": "direct",
"invert": false,
"outbound": [
"direct"
@@ -103,10 +102,8 @@
默认规则使用以下匹配逻辑:
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
(`port` || `port_range`) &&
(`source_geoip` || `source_ip_cidr`) &&
(`source_port` || `source_port_range`) &&
`other fields`
`other fields`
#### inbound
@@ -210,10 +207,6 @@
匹配用户 ID。
#### clash_mode
匹配 Clash 模式。
#### invert
反选匹配结果。

View File

@@ -8,43 +8,25 @@
"clash_api": {
"external_controller": "127.0.0.1:9090",
"external_ui": "folder",
"secret": "",
"direct_io": false,
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
},
"v2ray_api": {
"listen": "127.0.0.1:8080",
"stats": {
"enabled": true,
"direct_io": false,
"inbounds": [
"socks-in"
],
"outbounds": [
"proxy",
"direct"
]
}
"secret": ""
}
}
}
```
!!! note ""
Traffic statistics and connection management can degrade performance.
### Clash API Fields
!!! error ""
Clash API is not included by default, see [Installation](/#installation).
!!! note ""
Traffic statistics and connection management will disable TCP splice in linux and reduce performance, use at your own risk.
#### external_controller
RESTful web API listening address. Clash API will be disabled if empty.
RESTful web API listening address. Disabled if empty.
#### external_ui
@@ -56,56 +38,4 @@ serve it at `http://{{external-controller}}/ui`.
Secret for the RESTful API (optional)
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
ALWAYS set a secret if RESTful API is listening on 0.0.0.0
#### direct_io
Allows lossless relays like splice without real-time traffic reporting.
#### default_mode
Default mode in clash, `rule` will be used if empty.
This setting has no direct effect, but can be used in routing and DNS rules via the `clash_mode` rule item.
#### store_selected
!!! note ""
The tag must be set for target outbounds.
Store selected outbound for the `Selector` outbound in cache file.
#### cache_file
Cache file path, `cache.db` will be used if empty.
### V2Ray API Fields
!!! error ""
V2Ray API is not included by default, see [Installation](/#installation).
#### listen
gRPC API listening address. V2Ray API will be disabled if empty.
#### stats
Traffic statistics service settings.
#### stats.enabled
Enable statistics service.
#### stats.direct_io
Allows lossless relays like splice without real-time traffic reporting.
#### stats.inbounds
Inbound list to count traffic.
#### stats.outbounds
Outbound list to count traffic.
ALWAYS set a secret if RESTful API is listening on 0.0.0.0

View File

@@ -8,43 +8,25 @@
"clash_api": {
"external_controller": "127.0.0.1:9090",
"external_ui": "folder",
"secret": "",
"direct_io": false,
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
},
"v2ray_api": {
"listen": "127.0.0.1:8080",
"stats": {
"enabled": true,
"direct_io": false,
"inbounds": [
"socks-in"
],
"outbounds": [
"proxy",
"direct"
]
}
"secret": ""
}
}
}
```
!!! note ""
流量统计和连接管理会降低性能。
### Clash API 字段
!!! error ""
默认安装不包含 Clash API参阅 [安装](/zh/#_2)。
!!! note ""
流量统计和连接管理将禁用 Linux 中的 TCP splice 并降低性能,使用风险自负。
#### external_controller
RESTful web API 监听地址。如果为空,则禁用 Clash API。
RESTful web API 监听地址。
#### external_ui
@@ -54,56 +36,4 @@ RESTful web API 监听地址。如果为空,则禁用 Clash API。
RESTful API 的密钥(可选)
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
#### direct_io
允许像 splice 这样的没有实时流量报告的无损中继。
#### default_mode
Clash 中的默认模式,默认使用 `rule`
此设置没有直接影响,但可以通过 `clash_mode` 规则项在路由和 DNS 规则中使用。
#### store_selected
!!! note ""
必须为目标出站设置标签。
`Selector` 中出站的选定的目标出站存储在缓存文件中。
#### cache_file
缓存文件路径,默认使用`cache.db`
### V2Ray API 字段
!!! error ""
默认安装不包含 V2Ray API参阅 [安装](/zh/#_2)。
#### listen
gRPC API 监听地址。如果为空,则禁用 V2Ray API。
#### stats
流量统计服务设置。
#### stats.enabled
启用统计服务。
#### stats.direct_io
允许像 splice 这样的没有实时流量报告的无损中继。
#### stats.inbounds
统计流量的入站列表。
#### stats.outbounds
统计流量的出站列表。
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。

View File

@@ -7,8 +7,6 @@
... // Listen Fields
"version": 2,
"password": "fuck me till the daylight",
"handshake": {
"server": "google.com",
"server_port": 443,
@@ -22,25 +20,12 @@
See [Listen Fields](/configuration/shared/listen) for details.
### Fields
#### version
ShadowTLS protocol version.
| Value | Protocol Version |
|---------------|-----------------------------------------------------------------------------------------|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
#### password
Set password.
Only available in the ShadowTLS v2 protocol.
#### handshake
==Required==
Handshake server address and [Dial options](/configuration/shared/dial).
Handshake server address and [dial options](/configuration/shared/dial).

View File

@@ -7,8 +7,6 @@
... // 监听字段
"version": 2,
"password": "fuck me till the daylight",
"handshake": {
"server": "google.com",
"server_port": 443,
@@ -24,21 +22,6 @@
### 字段
#### version
ShadowTLS 协议版本。
| 值 | 协议版本 |
|---------------|-----------------------------------------------------------------------------------------|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
#### password
设置密码。
仅在 ShadowTLS v2 协议中可用。
#### handshake
==必填==

View File

@@ -8,14 +8,15 @@
{
"type": "tun",
"tag": "tun-in",
"interface_name": "tun0",
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"mtu": 9000,
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
"stack": "system",
"stack": "gvisor",
"include_uid": [
0
],
@@ -38,8 +39,8 @@
"exclude_package": [
"com.android.captiveportallogin"
],
...
// Listen Fields
... // Listen Fields
}
```
@@ -79,15 +80,9 @@ Set the default route to the Tun.
To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface`
!!! note "Use with Android VPN"
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
#### strict_route
*In Linux*:
Enforce strict routing rules when `auto_route` is enabled:
Enforce strict routing rules in Linux when `auto_route` is enabled:
* Let unsupported network unreachable
* Route all connections to tun
@@ -95,16 +90,8 @@ Enforce strict routing rules when `auto_route` is enabled:
It prevents address leaks and makes DNS hijacking work on Android and Linux with systemd-resolved, but your device will
not be accessible by others.
*In Windows*:
Use segmented `auto_route` routing settings, which may help if you're using a dial-up network.
#### endpoint_independent_nat
!!! info ""
This item is only available on the gvisor stack, other stacks are endpoint-independent NAT by default.
Enable endpoint-independent NAT.
Performance may degrade slightly, so it is not recommended to enable on when it is not needed.
@@ -117,15 +104,14 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
TCP/IP stack.
| Stack | Description | Status |
|------------------|----------------------------------------------------------------------------------|-------------------|
| system (default) | Sometimes better performance | recommended |
| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended |
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
| Stack | Upstream | Status |
|------------------|-----------------------------------------------------------------------|-------------------|
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | recommended |
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
!!! warning ""
gVisor and LWIP stacks is not included by default, see [Installation](/#installation).
The LWIP stack is not included by default, see [Installation](/#installation).
#### include_uid

View File

@@ -11,12 +11,12 @@
"interface_name": "tun0",
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"mtu": 9000,
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
"stack": "system",
"stack": "gvisor",
"include_uid": [
0
],
@@ -80,25 +80,15 @@ tun 接口的 IPv6 前缀。
为避免流量环回,请设置 `route.auto_detect_interface``route.default_interface``outbound.bind_interface`
!!! note "与 Android VPN 一起使用"
VPN 默认优先于 tun。要使 tun 经过 VPN启用 `route.override_android_vpn`
#### strict_route
*在 Linux 中*:
启用 `auto_route` 时执行严格的路由规则。
在 Linux 中启用 `auto_route` 时执行严格的路由规则。
* 让不支持的网络无法到达
* 将所有连接路由到 tun
它可以防止地址泄漏,并使 DNS 劫持在 Android 和使用 systemd-resolved 的 Linux 上工作,但你的设备将无法其他设备被访问。
*在 Windows 中*:
使用分段的 `auto_route` 路由设置,如果您使用的是拨号网络,这可能会有所帮助。
#### endpoint_independent_nat
启用独立于端点的 NAT。
@@ -113,15 +103,14 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
TCP/IP 栈。
| 栈 | 描述 | 状态 |
|-------------|--------------------------------------------------------------------------|-------|
| system (默认) | 有时性能更好 | 推荐 |
| gVisor | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
| 栈 | 上游 | 状态 |
|------------------|-----------------------------------------------------------------------|-------|
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | 推荐 |
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
!!! warning ""
默认安装不包含 gVisor 和 LWIP 栈,请参阅 [安装](/zh/#_2)。
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
#### include_uid
@@ -151,10 +140,10 @@ TCP/IP 栈。
限制被路由的 Android 用户。
| 常用用户 | ID |
| 常用用户 | ID |
|--|-----|
| 您 | 0 |
| 工作资料 | 10 |
| 您 | 0 |
| 工作资料 | 10 |
#### include_package

View File

@@ -15,24 +15,21 @@
### Fields
| Type | Format |
|----------------|--------------------------------|
| `direct` | [Direct](./direct) |
| `block` | [Block](./block) |
| `socks` | [SOCKS](./socks) |
| `http` | [HTTP](./http) |
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
| `vmess` | [VMess](./vmess) |
| `trojan` | [Trojan](./trojan) |
| `wireguard` | [Wireguard](./wireguard) |
| `hysteria` | [Hysteria](./hysteria) |
| `shadowsocksr` | [ShadowsocksR](./shadowsocksr) |
| `vless` | [VLESS](./vless) |
| `tor` | [Tor](./tor) |
| `ssh` | [SSH](./ssh) |
| `dns` | [DNS](./dns) |
| `selector` | [Selector](./selector) |
| `urltest` | [URLTest](./urltest) |
| Type | Format |
|---------------|------------------------------|
| `direct` | [Direct](./direct) |
| `block` | [Block](./block) |
| `socks` | [SOCKS](./socks) |
| `http` | [HTTP](./http) |
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
| `vmess` | [VMess](./vmess) |
| `trojan` | [Trojan](./trojan) |
| `wireguard` | [Wireguard](./wireguard) |
| `hysteria` | [Hysteria](./hysteria) |
| `tor` | [Tor](./tor) |
| `ssh` | [SSH](./ssh) |
| `dns` | [DNS](./dns) |
| `selector` | [Selector](./selector) |
#### tag

View File

@@ -15,24 +15,21 @@
### 字段
| 类型 | 格式 |
|----------------|--------------------------------|
| `direct` | [Direct](./direct) |
| `block` | [Block](./block) |
| `socks` | [SOCKS](./socks) |
| `http` | [HTTP](./http) |
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
| `vmess` | [VMess](./vmess) |
| `trojan` | [Trojan](./trojan) |
| `wireguard` | [Wireguard](./wireguard) |
| `hysteria` | [Hysteria](./hysteria) |
| `shadowsocksr` | [ShadowsocksR](./shadowsocksr) |
| `vless` | [VLESS](./vless) |
| `tor` | [Tor](./tor) |
| `ssh` | [SSH](./ssh) |
| `dns` | [DNS](./dns) |
| `selector` | [Selector](./selector) |
| `urltest` | [URLTest](./urltest) |
| 类型 | 格式 |
|---------------|------------------------------|
| `direct` | [Direct](./direct) |
| `block` | [Block](./block) |
| `socks` | [SOCKS](./socks) |
| `http` | [HTTP](./http) |
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
| `vmess` | [VMess](./vmess) |
| `trojan` | [Trojan](./trojan) |
| `wireguard` | [Wireguard](./wireguard) |
| `hysteria` | [Hysteria](./hysteria) |
| `tor` | [Tor](./tor) |
| `ssh` | [SSH](./ssh) |
| `dns` | [DNS](./dns) |
| `selector` | [Selector](./selector) |
#### tag

View File

@@ -9,8 +9,6 @@
"server_port": 1080,
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"plugin": "",
"plugin_opts": "",
"network": "udp",
"udp_over_tcp": false,
"multiplex": {},
@@ -67,16 +65,6 @@ Legacy encryption methods:
The shadowsocks password.
#### plugin
Shadowsocks SIP003 plugin, implemented in internal.
Only `obfs-local` and `v2ray-plugin` are supported.
#### plugin_opts
Shadowsocks SIP003 plugin options.
#### network
Enabled network

View File

@@ -9,8 +9,6 @@
"server_port": 1080,
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"plugin": "",
"plugin_opts": "",
"network": "udp",
"udp_over_tcp": false,
"multiplex": {},
@@ -67,16 +65,6 @@
Shadowsocks 密码。
#### plugin
Shadowsocks SIP003 插件,由内部实现。
仅支持 `obfs-local``v2ray-plugin`
#### plugin_opts
Shadowsocks SIP003 插件参数。
#### network
启用的网络协议

View File

@@ -1,106 +0,0 @@
### Structure
```json
{
"type": "shadowsocksr",
"tag": "ssr-out",
"server": "127.0.0.1",
"server_port": 1080,
"method": "aes-128-cfb",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"obfs": "plain",
"obfs_param": "",
"protocol": "origin",
"protocol_param": "",
"network": "udp",
... // Dial Fields
}
```
!!! warning ""
The ShadowsocksR protocol is obsolete and unmaintained. This outbound is provided for compatibility only.
!!! warning ""
ShadowsocksR is not included by default, see [Installation](/#installation).
### Fields
#### server
==Required==
The server address.
#### server_port
==Required==
The server port.
#### method
==Required==
Encryption methods:
* `aes-128-ctr`
* `aes-192-ctr`
* `aes-256-ctr`
* `aes-128-cfb`
* `aes-192-cfb`
* `aes-256-cfb`
* `rc4-md5`
* `chacha20-ietf`
* `xchacha20`
#### password
==Required==
The shadowsocks password.
#### obfs
The ShadowsocksR obfuscate.
* plain
* http_simple
* http_post
* random_head
* tls1.2_ticket_auth
#### obfs_param
The ShadowsocksR obfuscate parameter.
#### protocol
The ShadowsocksR protocol.
* origin
* verify_sha1
* auth_sha1_v4
* auth_aes128_md5
* auth_aes128_sha1
* auth_chain_a
* auth_chain_b
#### protocol_param
The ShadowsocksR protocol parameter.
#### network
Enabled network
One of `tcp` `udp`.
Both is enabled by default.
### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details.

View File

@@ -1,106 +0,0 @@
### 结构
```json
{
"type": "shadowsocksr",
"tag": "ssr-out",
"server": "127.0.0.1",
"server_port": 1080,
"method": "aes-128-cfb",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"obfs": "plain",
"obfs_param": "",
"protocol": "origin",
"protocol_param": "",
"network": "udp",
... // 拨号字段
}
```
!!! warning ""
ShadowsocksR 协议已过时且无人维护。 提供此出站仅出于兼容性目的。
!!! warning ""
默认安装不包含被 ShadowsocksR参阅 [安装](/zh/#_2)。
### 字段
#### server
==必填==
服务器地址。
#### server_port
==必填==
服务器端口。
#### method
==必填==
加密方法:
* `aes-128-ctr`
* `aes-192-ctr`
* `aes-256-ctr`
* `aes-128-cfb`
* `aes-192-cfb`
* `aes-256-cfb`
* `rc4-md5`
* `chacha20-ietf`
* `xchacha20`
#### password
==必填==
Shadowsocks 密码。
#### obfs
ShadowsocksR 混淆。
* plain
* http_simple
* http_post
* random_head
* tls1.2_ticket_auth
#### obfs_param
ShadowsocksR 混淆参数。
#### protocol
ShadowsocksR 协议。
* origin
* verify_sha1
* auth_sha1_v4
* auth_aes128_md5
* auth_aes128_sha1
* auth_chain_a
* auth_chain_b
#### protocol_param
ShadowsocksR 协议参数。
#### network
启用的网络协议
`tcp``udp`
默认所有。
### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。

View File

@@ -7,8 +7,6 @@
"server": "127.0.0.1",
"server_port": 1080,
"version": 2,
"password": "fuck me till the daylight",
"tls": {},
... // Dial Fields
@@ -29,21 +27,6 @@ The server address.
The server port.
#### version
ShadowTLS protocol version.
| Value | Protocol Version |
|---------------|-----------------------------------------------------------------------------------------|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
#### password
Set password.
Only available in the ShadowTLS v2 protocol.
#### tls
==Required==

View File

@@ -7,8 +7,6 @@
"server": "127.0.0.1",
"server_port": 1080,
"version": 2,
"password": "fuck me till the daylight",
"tls": {},
... // 拨号字段
@@ -29,21 +27,6 @@
服务器端口。
#### version
ShadowTLS 协议版本。
| 值 | 协议版本 |
|---------------|-----------------------------------------------------------------------------------------|
| `1` (default) | [ShadowTLS v1](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v1) |
| `2` | [ShadowTLS v2](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-en.md#v2) |
#### password
设置密码。
仅在 ShadowTLS v2 协议中可用。
#### tls
==必填==

View File

@@ -1,37 +0,0 @@
### Structure
```json
{
"type": "urltest",
"tag": "auto",
"outbounds": [
"proxy-a",
"proxy-b",
"proxy-c"
],
"url": "http://www.gstatic.com/generate_204",
"interval": "1m",
"tolerance": 50
}
```
### Fields
#### outbounds
==Required==
List of outbound tags to test.
#### url
The URL to test. `http://www.gstatic.com/generate_204` will be used if empty.
#### interval
The test interval. `1m` will be used if empty.
#### tolerance
The test tolerance in milliseconds. `50` will be used if empty.

View File

@@ -1,37 +0,0 @@
### 结构
```json
{
"type": "urltest",
"tag": "auto",
"outbounds": [
"proxy-a",
"proxy-b",
"proxy-c"
],
"url": "http://www.gstatic.com/generate_204",
"interval": "1m",
"tolerance": 50
}
```
### 字段
#### outbounds
==必填==
用于测试的出站标签列表。
#### url
用于测试的链接。默认使用 `http://www.gstatic.com/generate_204`
#### interval
测试间隔。 默认使用 `1m`
#### tolerance
以毫秒为单位的测试容差。 默认使用 `50`

View File

@@ -1,70 +0,0 @@
### Structure
```json
{
"type": "vless",
"tag": "vless-out",
"server": "127.0.0.1",
"server_port": 1080,
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
"network": "tcp",
"tls": {},
"packet_encoding": "",
"transport": {},
... // Dial Fields
}
```
!!! warning ""
The VLESS protocol is architecturally coupled to v2ray and is unmaintained. This outbound is provided for compatibility purposes only.
### Fields
#### server
==Required==
The server address.
#### server_port
==Required==
The server port.
#### uuid
==Required==
The VLESS user id.
#### network
Enabled network
One of `tcp` `udp`.
Both is enabled by default.
#### tls
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
#### packet_encoding
| Encoding | Description |
|------------|-----------------------|
| (none) | Disabled |
| packetaddr | Supported by v2ray 5+ |
| xudp | Supported by xray |
#### transport
V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport).
### Dial Fields
See [Dial Fields](/configuration/shared/dial) for details.

View File

@@ -1,70 +0,0 @@
### 结构
```json
{
"type": "vless",
"tag": "vless-out",
"server": "127.0.0.1",
"server_port": 1080,
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
"network": "tcp",
"tls": {},
"packet_encoding": "",
"transport": {},
... // 拨号字段
}
```
!!! warning ""
VLESS 协议与 v2ray 架构耦合且无人维护。 提供此出站仅出于兼容性目的。
### 字段
#### server
==必填==
服务器地址。
#### server_port
==必填==
服务器端口。
#### uuid
==必填==
VLESS 用户 ID。
#### network
启用的网络协议。
`tcp``udp`
默认所有。
#### tls
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
#### packet_encoding
| 编码 | 描述 |
|------------|---------------|
| (空) | 禁用 |
| packetaddr | 由 v2ray 5+ 支持 |
| xudp | 由 xray 支持 |
#### transport
V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。
### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。

View File

@@ -14,7 +14,7 @@
"authenticated_length": true,
"network": "tcp",
"tls": {},
"packet_encoding": "",
"packet_addr": false,
"multiplex": {},
"transport": {},
@@ -84,13 +84,9 @@ Both is enabled by default.
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
#### packet_encoding
#### packet_addr
| Encoding | Description |
|------------|-----------------------|
| (none) | Disabled |
| packetaddr | Supported by v2ray 5+ |
| xudp | Supported by xray |
Enable packetaddr support.
#### multiplex

View File

@@ -14,7 +14,7 @@
"authenticated_length": true,
"network": "tcp",
"tls": {},
"packet_encoding": "",
"packet_addr": false,
"multiplex": {},
"transport": {},
@@ -84,13 +84,9 @@ VMess 用户 ID。
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
#### packet_encoding
#### packet_addr
| 编码 | 描述 |
|------------|---------------|
| (空) | 禁用 |
| packetaddr | 由 v2ray 5+ 支持 |
| xudp | 由 xray 支持 |
启用 packetaddr 支持。
#### multiplex

View File

@@ -7,9 +7,8 @@
"server": "127.0.0.1",
"server_port": 1080,
"system_interface": false,
"interface_name": "wg0",
"local_address": [
"10.0.0.1",
"10.0.0.2/32"
],
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
@@ -26,10 +25,6 @@
WireGuard is not included by default, see [Installation](/#installation).
!!! warning ""
gVisor, which is required by the unprivileged WireGuard is not included by default, see [Installation](/#installation).
### Fields
#### server
@@ -44,23 +39,11 @@ The server address.
The server port.
#### system_interface
Use system tun support.
Requires privilege and cannot conflict with system interfaces.
Forced if gVisor not included in the build.
#### interface_name
Custom device name when `system_interface` enabled.
#### local_address
==Required==
List of IP (v4 or v6) address prefixes to be assigned to the interface.
List of IP (v4 or v6) addresses (optionally with CIDR masks) to be assigned to the interface.
#### private_key

View File

@@ -7,9 +7,8 @@
"server": "127.0.0.1",
"server_port": 1080,
"system_interface": false,
"interface_name": "wg0",
"local_address": [
"10.0.0.1",
"10.0.0.2/32"
],
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
@@ -26,10 +25,6 @@
默认安装不包含 WireGuard, 参阅 [安装](/zh/#_2)。
!!! warning ""
默认安装不包含被非特权 WireGuard 需要的 gVisor, 参阅 [安装](/zh/#_2)。
### 字段
#### server
@@ -44,25 +39,13 @@
服务器端口。
#### system_interface
使用系统 tun 支持。
需要特权且不能与系统接口冲突。
如果 gVisor 未包含在构建中,则强制执行。
#### interface_name
启用 `system_interface` 时的自定义设备名称。
#### local_address
==必填==
接口的 IPv4/IPv6 地址或地址段的列表您。
要分配给接口的 IPv4 或 v6地址列表。
要分配给接口的 IPv4 或 v6地址列表(可以选择带有 CIDR 掩码)
#### private_key

View File

@@ -10,7 +10,6 @@
"rules": [],
"final": "",
"auto_detect_interface": false,
"override_android_vpn": false,
"default_interface": "en0",
"default_mark": 233
}
@@ -35,25 +34,17 @@ Default outbound tag. the first outbound will be used if empty.
Only supported on Linux, Windows and macOS.
Bind outbound connections to the default NIC by default to prevent routing loops under tun.
Bind outbound connections to the default NIC by default to prevent routing loops under Tun.
Takes no effect if `outbound.bind_interface` is set.
#### override_android_vpn
!!! error ""
Only supported on Android.
Accept Android VPN as upstream NIC when `auto_detect_interface` enabled.
#### default_interface
!!! error ""
Only supported on Linux, Windows and macOS.
Bind outbound connections to the specified NIC by default to prevent routing loops under tun.
Bind outbound connections to the specified NIC by default to prevent routing loops under Tun.
Takes no effect if `auto_detect_interface` is set.

View File

@@ -10,7 +10,6 @@
"rules": [],
"final": "",
"auto_detect_interface": false,
"override_android_vpn": false,
"default_interface": "en0",
"default_mark": 233
}
@@ -35,25 +34,17 @@
仅支持 Linux、Windows 和 macOS。
默认将出站连接绑定到默认网卡,以防止在 tun 下出现路由环路。
默认将出站连接绑定到默认网卡,以防止在 Tun 下出现路由环路。
如果设置了 `outbound.bind_interface` 设置,则不生效。
#### override_android_vpn
!!! error ""
仅支持 Android。
启用 `auto_detect_interface` 时接受 Android VPN 作为上游网卡。
#### default_interface
!!! error ""
仅支持 Linux、Windows 和 macOS。
默认将出站连接绑定到指定网卡,以防止在 tun 下出现路由环路。
默认将出站连接绑定到指定网卡,以防止在 Tun 下出现路由环路。
如果设置了 `auto_detect_interface` 设置,则不生效。

View File

@@ -80,7 +80,6 @@
"user_id": [
1000
],
"clash_mode": "direct",
"invert": false,
"outbound": "direct"
},
@@ -107,10 +106,8 @@
The default rule uses the following matching logic:
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
(`port` || `port_range`) &&
(`source_geoip` || `source_ip_cidr`) &&
(`source_port` || `source_port_range`) &&
`other fields`
`other fields`
#### inbound
@@ -222,10 +219,6 @@ Match user name.
Match user id.
#### clash_mode
Match Clash mode.
#### invert
Invert match result.

View File

@@ -78,7 +78,6 @@
"user_id": [
1000
],
"clash_mode": "direct",
"invert": false,
"outbound": "direct"
},
@@ -105,10 +104,8 @@
默认规则使用以下匹配逻辑:
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
(`port` || `port_range`) &&
(`source_geoip` || `source_ip_cidr`) &&
(`source_port` || `source_port_range`) &&
`other fields`
`other fields`
#### inbound
@@ -220,10 +217,6 @@
匹配用户 ID。
#### clash_mode
匹配 Clash 模式。
#### invert
反选匹配结果。

View File

@@ -9,7 +9,6 @@
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"udp_fragment": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
@@ -17,9 +16,9 @@
### Fields
| Field | Available Context |
|---------------------------------------------------------------------------------------------------------------------|-------------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set |
| Field | Available Context |
|-----------------------------------------------------------------------------------|-------------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` /`connect_timeout` | `detour` not set |
#### detour
@@ -45,14 +44,6 @@ Set netfilter routing mark.
Reuse listener address.
#### tcp_fast_open
Enable TCP Fast Open.
#### udp_fragment
Enable UDP fragmentation.
#### connect_timeout
Connect timeout, in golang's Duration format.

View File

@@ -9,7 +9,6 @@
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"udp_fragment": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
@@ -17,11 +16,6 @@
### 字段
| 字段 | 可用上下文 |
|---------------------------------------------------------------------------------------------------------------------|--------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` 未设置 |
#### detour
上游出站的标签。
@@ -48,14 +42,6 @@
重用监听地址。
#### tcp_fast_open
启用 TCP Fast Open。
#### udp_fragment
启用 UDP 分段。
#### connect_timeout
连接超时,采用 golang 的 Duration 格式。

View File

@@ -5,27 +5,24 @@
"listen": "::",
"listen_port": 5353,
"tcp_fast_open": false,
"udp_fragment": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"proxy_protocol": false,
"proxy_protocol_accept_no_header": false,
"detour": "another-in"
}
```
### Fields
| Field | Available Context |
|-----------------------------------|-------------------------------------------------------------------|
| `listen` | Needs to listen on TCP or UDP. |
| `listen_port` | Needs to listen on TCP or UDP. |
| `tcp_fast_open` | Needs to listen on TCP. |
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
| `proxy_protocol` | Needs to listen on TCP. |
| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled |
| Field | Available Context |
|------------------|-------------------------------------------------------------------|
| `listen` | Needs to listen on TCP or UDP. |
| `listen_port` | Needs to listen on TCP or UDP. |
| `tcp_fast_open` | Needs to listen on TCP. |
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
| `proxy_protocol` | Needs to listen on TCP. |
#### listen
@@ -39,11 +36,7 @@ Listen port.
#### tcp_fast_open
Enable TCP Fast Open.
#### udp_fragment
Enable UDP fragmentation.
Enable tcp fast open for listener.
#### sniff
@@ -73,10 +66,6 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.
#### proxy_protocol_accept_no_header
Accept connections without Proxy Protocol header.
#### detour
If set, connections will be forwarded to the specified inbound.

View File

@@ -5,26 +5,21 @@
"listen": "::",
"listen_port": 5353,
"tcp_fast_open": false,
"udp_fragment": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"proxy_protocol": false,
"proxy_protocol_accept_no_header": false,
"detour": "another-in"
}
```
| 字段 | 可用上下文 |
|-----------------------------------|-------------------------------------|
| `listen` | 需要监听 TCP 或 UDP。 |
| `listen_port` | 需要监听 TCP 或 UDP。 |
| `tcp_fast_open` | 需要监听 TCP。 |
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
| `proxy_protocol` | 需要监听 TCP。 |
| `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 |
| 字段 | 可用上下文 |
|------------------|-------------------------------------|
| `listen` | 需要监听 TCP 或 UDP。 |
| `listen_port` | 需要监听 TCP 或 UDP。 |
| `tcp_fast_open` | 需要监听 TCP |
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
| `proxy_protocol` | 需要监听 TCP。 |
### 字段
@@ -40,11 +35,7 @@
#### tcp_fast_open
启用 TCP Fast Open
#### udp_fragment
启用 UDP 分段。
为监听器启用 TCP 快速打开
#### sniff
@@ -74,10 +65,6 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。
#### proxy_protocol_accept_no_header
接受没有代理协议标头的连接。
#### detour
如果设置,连接将被转发到指定的入站。

View File

@@ -30,6 +30,10 @@
}
```
!!! warning ""
ACME is not included by default, see [Installation](/#installation).
### Outbound
```json
@@ -43,17 +47,7 @@
"max_version": "",
"cipher_suites": [],
"certificate": "",
"certificate_path": "",
"ech": {
"enabled": false,
"pq_signature_schemes_enabled": false,
"dynamic_record_sizing_disabled": false,
"config": ""
},
"utls": {
"enabled": false,
"fingerprint": ""
}
"certificate_path": ""
}
```
@@ -161,48 +155,8 @@ The server private key, in PEM format.
The path to the server private key, in PEM format.
#### ech
==Client only==
!!! warning ""
ECH is not included by default, see [Installation](/#installation).
ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello
message.
If you don't know how to fill in the other configuration, just set `enabled`.
#### utls
==Client only==
!!! warning ""
uTLS is not included by default, see [Installation](/#installation).
!!! note ""
uTLS is poorly maintained and the effect may be unproven, use at your own risk.
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance.
Available fingerprint values:
* chrome
* firefox
* ios
* android
* random
### ACME Fields
!!! warning ""
ACME is not included by default, see [Installation](/#installation).
#### domain
List of domain.
@@ -251,6 +205,10 @@ listener for the HTTP challenge.
The alternate port to use for the ACME TLS-ALPN challenge; the system must forward 443 to this port for challenge to
succeed.
### Reload
For server configuration, certificate and key will be automatically reloaded if modified.
#### external_account
EAB (External Account Binding) contains information necessary to bind or map an ACME account to some other account known
@@ -268,8 +226,4 @@ The key identifier.
#### external_account.mac_key
The MAC key.
### Reload
For server configuration, certificate and key will be automatically reloaded if modified.
The MAC key.

View File

@@ -30,6 +30,10 @@
}
```
!!! warning ""
默认安装不包含 ACME参阅 [安装](/zh/#_2)。
### 出站
```json
@@ -43,17 +47,7 @@
"max_version": "",
"cipher_suites": [],
"certificate": "",
"certificate_path": "",
"ech": {
"enabled": false,
"pq_signature_schemes_enabled": false,
"dynamic_record_sizing_disabled": false,
"config": ""
},
"utls": {
"enabled": false,
"fingerprint": ""
}
"certificate_path": ""
}
```
@@ -161,47 +155,8 @@ TLS 版本值:
服务器 PEM 私钥路径。
#### ech
==仅客户端==
!!! warning ""
默认安装不包含 ECH, 参阅 [安装](/zh/#_2)。
ECH (Encrypted Client Hello) 是一个 TLS 扩展,它允许客户端加密其 ClientHello 的第一部分
信息。
如果您不知道如何填写其他配置,只需设置 `enabled` 即可。
#### utls
==仅客户端==
!!! warning ""
默认安装不包含 uTLS, 参阅 [安装](/zh/#_2)。
!!! note ""
uTLS 维护不善且其效果可能未经证实,使用风险自负。
uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻力。
可用的指纹值:
* chrome
* firefox
* ios
* android
* random
### ACME 字段
!!! warning ""
默认安装不包含 ACME参阅 [安装](/zh/#_2)。
#### domain
一组域名。
@@ -248,6 +203,10 @@ ACME 数据目录。
用于 ACME TLS-ALPN 质询的备用端口; 系统必须将 443 转发到此端口以使质询成功。
### Reload
对于服务器配置,如果修改,证书和密钥将自动重新加载。
#### external_account
EAB外部帐户绑定包含将 ACME 帐户绑定或映射到其他已知帐户所需的信息由 CA。
@@ -263,8 +222,4 @@ EAB外部帐户绑定包含将 ACME 帐户绑定或映射到其他已知
#### external_account.mac_key
MAC 密钥。
### 重载
对于服务器配置,如果修改,证书和密钥将自动重新加载。
MAC 密钥。

View File

@@ -53,6 +53,17 @@ we need to do this.
The library needs to be updated with the upstream.
#### certmagic
Link: [GitHub repository](https://github.com/SagerNet/certmagic)
Fork of `caddyserver/certmagic`
Since upstream uses `miekg/dns` and we use `x/net/dnsmessage`, we need to replace its DNS part with our own
implementation.
The library needs to be updated with the upstream.
#### smux
Link: [GitHub repository](https://github.com/SagerNet/smux)

View File

@@ -95,9 +95,7 @@
| cn | 17.8M | 140.3M |
| cn (Loyalsoldier) | 74.3M | 246.7M |
#### Benchmark
##### Shadowsocks
#### Shadowsocks benchmark
| / | none | aes-128-gcm | 2022-blake3-aes-128-gcm |
|------------------------------------|:---------:|:-----------:|:-----------------------:|
@@ -105,13 +103,6 @@
| shadowsocks-rust (v1.15.0-alpha.5) | 10.7 Gbps | / | 9.36 Gbps |
| sing-box | 29.0 Gbps | / | 11.8 Gbps |
##### VMess
| / | TCP | HTTP | H2 TLS | WebSocket TLS | gRPC TLS |
|--------------------|:---------:|:---------:|:---------:|:-------------:|:---------:|
| v2ray-core (5.1.0) | 7.86 GBps | 2.86 Gbps | 1.83 Gbps | 2.36 Gbps | 2.43 Gbps |
| sing-box | 7.96 Gbps | 8.09 Gbps | 6.11 Gbps | 8.02 Gbps | 6.35 Gbps |
#### License
| / | License |

View File

@@ -27,13 +27,9 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 dns transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
| `with_shadowsocksr` | Build with ShadowsocksR support, see [ShadowsocksR outbound](./configuration/outbound/shadowsocksr). |
| `with_ech` | Build with TLS ECH extension support for TLS outbound, see [TLS](./configuration/shared/tls#ech). |
| `with_utls` | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](./configuration/shared/tls#utls). |
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | Build with V2Ray API support, see [Experimental](./configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | Build with gVisor support, see [Tun inbound](./configuration/inbound/tun#stack) and [WireGuard outbound](./configuration/outbound/wireguard#system_interface). |
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
@@ -46,6 +42,10 @@ sing-box version
It is also recommended to use systemd to manage sing-box service,
see [Linux server installation example](./examples/linux-server-installation).
## Contributors
[![](https://opencollective.com/sagernet/contributors.svg?width=740&button=false)](https://github.com/sagernet/sing-box/graphs/contributors)
## License
```

View File

@@ -27,13 +27,9 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server)[Naive 入站](./configuration/inbound/naive)[Hysteria 入站](./configuration/inbound/hysteria)[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
| `with_utls` | 启用 uTLS 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
| `with_clash_api` | 启用 Clash API 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `with_v2ray_api` | V2Rat API 支持,参阅 [实验性](./configuration/experimental#v2ray-api-fields)。 |
| `with_gvisor` | 启用 gVisor 支持,参阅 [Tun 入站](./configuration/inbound/tun#stack) 和 [WireGuard 出站](./configuration/outbound/wireguard#system_interface)。 |
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `no_gvisor` | gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
@@ -43,9 +39,13 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
sing-box version
```
同时推荐使用 systemd 来管理 sing-box 服务器实例。
同时推荐使用 Systemd 来管理 sing-box 服务器实例。
参阅 [Linux 服务器安装示例](./examples/linux-server-installation)。
## 贡献者
[![](https://opencollective.com/sagernet/contributors.svg?width=740&button=false)](https://github.com/sagernet/sing-box/graphs/contributors)
## 授权
```

View File

@@ -1,24 +1,14 @@
//go:build with_clash_api
package experimental
import (
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/clashapi"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
)
type ClashServerConstructor = func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
var clashServerConstructor ClashServerConstructor
func RegisterClashServerConstructor(constructor ClashServerConstructor) {
clashServerConstructor = constructor
}
func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
if clashServerConstructor == nil {
return nil, os.ErrInvalid
}
return clashServerConstructor(router, logFactory, options)
return clashapi.NewServer(router, logFactory, options), nil
}

View File

@@ -1,61 +0,0 @@
package cachefile
import (
"os"
"time"
"github.com/sagernet/sing-box/adapter"
"go.etcd.io/bbolt"
)
var bucketSelected = []byte("selected")
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
type CacheFile struct {
DB *bbolt.DB
}
func Open(path string) (*CacheFile, error) {
const fileMode = 0o666
options := bbolt.Options{Timeout: time.Second}
db, err := bbolt.Open(path, fileMode, &options)
switch err {
case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch:
if err = os.Remove(path); err != nil {
break
}
db, err = bbolt.Open(path, 0o666, &options)
}
if err != nil {
return nil, err
}
return &CacheFile{db}, nil
}
func (c *CacheFile) LoadSelected(group string) string {
var selected string
c.DB.View(func(t *bbolt.Tx) error {
bucket := t.Bucket(bucketSelected)
if bucket == nil {
return nil
}
selectedBytes := bucket.Get([]byte(group))
if len(selectedBytes) > 0 {
selected = string(selectedBytes)
}
return nil
})
return selected
}
func (c *CacheFile) StoreSelected(group, selected string) error {
return c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := t.CreateBucketIfNotExists(bucketSelected)
if err != nil {
return err
}
return bucket.Put([]byte(group), []byte(selected))
})
}

View File

@@ -2,7 +2,6 @@ package clashapi
import (
"net/http"
"strings"
"github.com/sagernet/sing-box/log"
@@ -10,11 +9,11 @@ import (
"github.com/go-chi/render"
)
func configRouter(server *Server, logFactory log.Factory, logger log.Logger) http.Handler {
func configRouter(logFactory log.Factory) http.Handler {
r := chi.NewRouter()
r.Get("/", getConfigs(server, logFactory))
r.Get("/", getConfigs(logFactory))
r.Put("/", updateConfigs)
r.Patch("/", patchConfigs(server, logger))
r.Patch("/", patchConfigs)
return r
}
@@ -32,7 +31,7 @@ type configSchema struct {
Tun map[string]any `json:"tun"`
}
func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
func getConfigs(logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
logLevel := logFactory.Level()
if logLevel == log.LevelTrace {
@@ -41,31 +40,15 @@ func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWrit
logLevel = log.LevelError
}
render.JSON(w, r, &configSchema{
Mode: server.mode,
Mode: "rule",
BindAddress: "*",
LogLevel: log.FormatLevel(logLevel),
})
}
}
func patchConfigs(server *Server, logger log.Logger) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var newConfig configSchema
err := render.DecodeJSON(r.Body, &newConfig)
if err != nil {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest)
return
}
if newConfig.Mode != "" {
mode := strings.ToLower(newConfig.Mode)
if server.mode != mode {
server.mode = mode
logger.Info("updated mode: ", mode)
}
}
render.NoContent(w, r)
}
func patchConfigs(w http.ResponseWriter, r *http.Request) {
render.NoContent(w, r)
}
func updateConfigs(w http.ResponseWriter, r *http.Request) {

View File

@@ -61,6 +61,7 @@ func findProxyByName(router adapter.Router) func(next http.Handler) http.Handler
func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
var info badjson.JSONObject
var clashType string
var isGroup bool
switch detour.Type() {
case C.TypeDirect:
clashType = "Direct"
@@ -80,18 +81,13 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
clashType = "Hysteria"
case C.TypeWireGuard:
clashType = "WireGuard"
case C.TypeShadowsocksR:
clashType = "ShadowsocksR"
case C.TypeVLESS:
clashType = "VLESS"
case C.TypeTor:
clashType = "Tor"
case C.TypeSSH:
clashType = "SSH"
case C.TypeSelector:
clashType = "Selector"
case C.TypeURLTest:
clashType = "URLTest"
isGroup = true
default:
clashType = "Direct"
}
@@ -104,9 +100,10 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
} else {
info.Put("history", []*urltest.History{})
}
if group, isGroup := detour.(adapter.OutboundGroup); isGroup {
info.Put("now", group.Now())
info.Put("all", group.All())
if isGroup {
selector := detour.(adapter.OutboundGroup)
info.Put("now", selector.Now())
info.Put("all", selector.All())
}
return &info
}

View File

@@ -14,8 +14,6 @@ import (
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/common/urltest"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/experimental/clashapi/cachefile"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
@@ -30,10 +28,6 @@ import (
"github.com/go-chi/render"
)
func init() {
experimental.RegisterClashServerConstructor(NewServer)
}
var _ adapter.ClashServer = (*Server)(nil)
type Server struct {
@@ -43,13 +37,9 @@ type Server struct {
trafficManager *trafficontrol.Manager
urlTestHistory *urltest.HistoryStorage
tcpListener net.Listener
directIO bool
mode string
storeSelected bool
cacheFile adapter.ClashCacheFile
}
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) *Server {
trafficManager := trafficontrol.NewManager()
chiRouter := chi.NewRouter()
server := &Server{
@@ -61,23 +51,6 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
},
trafficManager: trafficManager,
urlTestHistory: urltest.NewHistoryStorage(),
directIO: options.DirectIO,
mode: strings.ToLower(options.DefaultMode),
}
if server.mode == "" {
server.mode = "rule"
}
if options.StoreSelected {
server.storeSelected = true
cachePath := os.ExpandEnv(options.CacheFile)
if cachePath == "" {
cachePath = "cache.db"
}
cacheFile, err := cachefile.Open(cachePath)
if err != nil {
return nil, E.Cause(err, "open cache file")
}
server.cacheFile = cacheFile
}
cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
@@ -88,11 +61,11 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
chiRouter.Use(cors.Handler)
chiRouter.Group(func(r chi.Router) {
r.Use(authentication(options.Secret))
r.Get("/", hello(options.ExternalUI != ""))
r.Get("/", hello)
r.Get("/logs", getLogs(logFactory))
r.Get("/traffic", traffic(trafficManager))
r.Get("/version", version)
r.Mount("/configs", configRouter(server, logFactory, server.logger))
r.Mount("/configs", configRouter(logFactory))
r.Mount("/proxies", proxyRouter(server, router))
r.Mount("/rules", ruleRouter(router))
r.Mount("/connections", connectionRouter(trafficManager))
@@ -111,7 +84,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
})
})
}
return server, nil
return server
}
func (s *Server) Start() error {
@@ -135,28 +108,11 @@ func (s *Server) Close() error {
common.PtrOrNil(s.httpServer),
s.tcpListener,
s.trafficManager,
s.cacheFile,
)
}
func (s *Server) Mode() string {
return s.mode
}
func (s *Server) StoreSelected() bool {
return s.storeSelected
}
func (s *Server) CacheFile() adapter.ClashCacheFile {
return s.cacheFile
}
func (s *Server) HistoryStorage() *urltest.HistoryStorage {
return s.urlTestHistory
}
func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) {
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule, s.directIO)
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule)
return tracker, tracker
}
@@ -244,14 +200,8 @@ func authentication(serverSecret string) func(next http.Handler) http.Handler {
}
}
func hello(redirect bool) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if redirect {
http.Redirect(w, r, "/ui/", http.StatusTemporaryRedirect)
} else {
render.JSON(w, r, render.M{"hello": "clash"})
}
}
func hello(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, render.M{"hello": "clash"})
}
var upgrader = websocket.Upgrader{

View File

@@ -6,8 +6,9 @@ import (
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/trackerconn"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/gofrs/uuid"
@@ -44,7 +45,7 @@ type trackerInfo struct {
}
type tcpTracker struct {
N.ExtendedConn `json:"-"`
net.Conn `json:"-"`
*trackerInfo
manager *Manager
}
@@ -53,9 +54,25 @@ func (tt *tcpTracker) ID() string {
return tt.UUID.String()
}
func (tt *tcpTracker) Read(b []byte) (int, error) {
n, err := tt.Conn.Read(b)
upload := int64(n)
tt.manager.PushUploaded(upload)
tt.UploadTotal.Add(upload)
return n, err
}
func (tt *tcpTracker) Write(b []byte) (int, error) {
n, err := tt.Conn.Write(b)
download := int64(n)
tt.manager.PushDownloaded(download)
tt.DownloadTotal.Add(download)
return n, err
}
func (tt *tcpTracker) Close() error {
tt.manager.Leave(tt)
return tt.ExtendedConn.Close()
return tt.Conn.Close()
}
func (tt *tcpTracker) Leave() {
@@ -63,18 +80,10 @@ func (tt *tcpTracker) Leave() {
}
func (tt *tcpTracker) Upstream() any {
return tt.ExtendedConn
return tt.Conn
}
func (tt *tcpTracker) ReaderReplaceable() bool {
return true
}
func (tt *tcpTracker) WriterReplaceable() bool {
return true
}
func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule, directIO bool) *tcpTracker {
func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *tcpTracker {
uuid, _ := uuid.NewV4()
var chain []string
@@ -97,17 +106,8 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
next = group.Now()
}
upload := atomic.NewInt64(0)
download := atomic.NewInt64(0)
t := &tcpTracker{
ExtendedConn: trackerconn.NewHook(conn, func(n int64) {
upload.Add(n)
manager.PushUploaded(n)
}, func(n int64) {
download.Add(n)
manager.PushDownloaded(n)
}, directIO),
Conn: conn,
manager: manager,
trackerInfo: &trackerInfo{
UUID: uuid,
@@ -115,8 +115,8 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
Metadata: metadata,
Chain: common.Reverse(chain),
Rule: "",
UploadTotal: upload,
DownloadTotal: download,
UploadTotal: atomic.NewInt64(0),
DownloadTotal: atomic.NewInt64(0),
},
}
@@ -140,6 +140,27 @@ func (ut *udpTracker) ID() string {
return ut.UUID.String()
}
func (ut *udpTracker) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = ut.PacketConn.ReadPacket(buffer)
if err == nil {
upload := int64(buffer.Len())
ut.manager.PushUploaded(upload)
ut.UploadTotal.Add(upload)
}
return
}
func (ut *udpTracker) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
download := int64(buffer.Len())
err := ut.PacketConn.WritePacket(buffer, destination)
if err != nil {
return err
}
ut.manager.PushDownloaded(download)
ut.DownloadTotal.Add(download)
return nil
}
func (ut *udpTracker) Close() error {
ut.manager.Leave(ut)
return ut.PacketConn.Close()
@@ -153,14 +174,6 @@ func (ut *udpTracker) Upstream() any {
return ut.PacketConn
}
func (ut *udpTracker) ReaderReplaceable() bool {
return true
}
func (ut *udpTracker) WriterReplaceable() bool {
return true
}
func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *udpTracker {
uuid, _ := uuid.NewV4()
@@ -184,26 +197,17 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, route
next = group.Now()
}
upload := atomic.NewInt64(0)
download := atomic.NewInt64(0)
ut := &udpTracker{
PacketConn: trackerconn.NewHookPacket(conn, func(n int64) {
upload.Add(n)
manager.PushUploaded(n)
}, func(n int64) {
download.Add(n)
manager.PushDownloaded(n)
}),
manager: manager,
PacketConn: conn,
manager: manager,
trackerInfo: &trackerInfo{
UUID: uuid,
Start: time.Now(),
Metadata: metadata,
Chain: common.Reverse(chain),
Rule: "",
UploadTotal: upload,
DownloadTotal: download,
UploadTotal: atomic.NewInt64(0),
DownloadTotal: atomic.NewInt64(0),
},
}

View File

@@ -0,0 +1,14 @@
//go:build !with_clash_api
package experimental
import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
)
func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`)
}

View File

@@ -1,160 +0,0 @@
package trackerconn
import (
"io"
"net"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
)
func New(conn net.Conn, readCounter *atomic.Int64, writeCounter *atomic.Int64, direct bool) N.ExtendedConn {
trackerConn := &Conn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
if direct {
return (*DirectConn)(trackerConn)
} else {
return trackerConn
}
}
func NewHook(conn net.Conn, readCounter func(n int64), writeCounter func(n int64), direct bool) N.ExtendedConn {
trackerConn := &HookConn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
if direct {
return (*DirectHookConn)(trackerConn)
} else {
return trackerConn
}
}
type Conn struct {
N.ExtendedConn
readCounter *atomic.Int64
writeCounter *atomic.Int64
}
func (c *Conn) Read(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Read(p)
c.readCounter.Add(int64(n))
return n, err
}
func (c *Conn) ReadBuffer(buffer *buf.Buffer) error {
err := c.ExtendedConn.ReadBuffer(buffer)
if err != nil {
return err
}
c.readCounter.Add(int64(buffer.Len()))
return nil
}
func (c *Conn) Write(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Write(p)
c.writeCounter.Add(int64(n))
return n, err
}
func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
dataLen := int64(buffer.Len())
err := c.ExtendedConn.WriteBuffer(buffer)
if err != nil {
return err
}
c.writeCounter.Add(dataLen)
return nil
}
func (c *Conn) Upstream() any {
return c.ExtendedConn
}
type HookConn struct {
N.ExtendedConn
readCounter func(n int64)
writeCounter func(n int64)
}
func (c *HookConn) Read(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Read(p)
c.readCounter(int64(n))
return n, err
}
func (c *HookConn) ReadBuffer(buffer *buf.Buffer) error {
err := c.ExtendedConn.ReadBuffer(buffer)
if err != nil {
return err
}
c.readCounter(int64(buffer.Len()))
return nil
}
func (c *HookConn) Write(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Write(p)
c.writeCounter(int64(n))
return n, err
}
func (c *HookConn) WriteBuffer(buffer *buf.Buffer) error {
dataLen := int64(buffer.Len())
err := c.ExtendedConn.WriteBuffer(buffer)
if err != nil {
return err
}
c.writeCounter(dataLen)
return nil
}
func (c *HookConn) Upstream() any {
return c.ExtendedConn
}
type DirectConn Conn
func (c *DirectConn) WriteTo(w io.Writer) (n int64, err error) {
reader := N.UnwrapReader(c.ExtendedConn)
if wt, ok := reader.(io.WriterTo); ok {
n, err = wt.WriteTo(w)
c.readCounter.Add(n)
return
} else {
return bufio.Copy(w, (*Conn)(c))
}
}
func (c *DirectConn) ReadFrom(r io.Reader) (n int64, err error) {
writer := N.UnwrapWriter(c.ExtendedConn)
if rt, ok := writer.(io.ReaderFrom); ok {
n, err = rt.ReadFrom(r)
c.writeCounter.Add(n)
return
} else {
return bufio.Copy((*Conn)(c), r)
}
}
type DirectHookConn HookConn
func (c *DirectHookConn) WriteTo(w io.Writer) (n int64, err error) {
reader := N.UnwrapReader(c.ExtendedConn)
if wt, ok := reader.(io.WriterTo); ok {
n, err = wt.WriteTo(w)
c.readCounter(n)
return
} else {
return bufio.Copy(w, (*HookConn)(c))
}
}
func (c *DirectHookConn) ReadFrom(r io.Reader) (n int64, err error) {
writer := N.UnwrapWriter(c.ExtendedConn)
if rt, ok := writer.(io.ReaderFrom); ok {
n, err = rt.ReadFrom(r)
c.writeCounter(n)
return
} else {
return bufio.Copy((*HookConn)(c), r)
}
}

View File

@@ -1,73 +0,0 @@
package trackerconn
import (
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
)
func NewPacket(conn N.PacketConn, readCounter *atomic.Int64, writeCounter *atomic.Int64) *PacketConn {
return &PacketConn{conn, readCounter, writeCounter}
}
func NewHookPacket(conn N.PacketConn, readCounter func(n int64), writeCounter func(n int64)) *HookPacketConn {
return &HookPacketConn{conn, readCounter, writeCounter}
}
type PacketConn struct {
N.PacketConn
readCounter *atomic.Int64
writeCounter *atomic.Int64
}
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = c.PacketConn.ReadPacket(buffer)
if err == nil {
c.readCounter.Add(int64(buffer.Len()))
}
return
}
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
dataLen := int64(buffer.Len())
err := c.PacketConn.WritePacket(buffer, destination)
if err != nil {
return err
}
c.writeCounter.Add(dataLen)
return nil
}
func (c *PacketConn) Upstream() any {
return c.PacketConn
}
type HookPacketConn struct {
N.PacketConn
readCounter func(n int64)
writeCounter func(n int64)
}
func (c *HookPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = c.PacketConn.ReadPacket(buffer)
if err == nil {
c.readCounter(int64(buffer.Len()))
}
return
}
func (c *HookPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
dataLen := int64(buffer.Len())
err := c.PacketConn.WritePacket(buffer, destination)
if err != nil {
return err
}
c.writeCounter(dataLen)
return nil
}
func (c *HookPacketConn) Upstream() any {
return c.PacketConn
}

View File

@@ -1,24 +0,0 @@
package experimental
import (
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
)
type V2RayServerConstructor = func(logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error)
var v2rayServerConstructor V2RayServerConstructor
func RegisterV2RayServerConstructor(constructor V2RayServerConstructor) {
v2rayServerConstructor = constructor
}
func NewV2RayServer(logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
if v2rayServerConstructor == nil {
return nil, os.ErrInvalid
}
return v2rayServerConstructor(logger, options)
}

View File

@@ -1,75 +0,0 @@
package v2rayapi
import (
"errors"
"net"
"net/http"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func init() {
experimental.RegisterV2RayServerConstructor(NewServer)
}
var _ adapter.V2RayServer = (*Server)(nil)
type Server struct {
logger log.Logger
listen string
tcpListener net.Listener
grpcServer *grpc.Server
statsService *StatsService
}
func NewServer(logger log.Logger, options option.V2RayAPIOptions) (adapter.V2RayServer, error) {
grpcServer := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))
statsService := NewStatsService(common.PtrValueOrDefault(options.Stats))
if statsService != nil {
RegisterStatsServiceServer(grpcServer, statsService)
}
server := &Server{
logger: logger,
listen: options.Listen,
grpcServer: grpcServer,
statsService: statsService,
}
return server, nil
}
func (s *Server) Start() error {
listener, err := net.Listen("tcp", s.listen)
if err != nil {
return err
}
s.logger.Info("grpc server started at ", listener.Addr())
s.tcpListener = listener
go func() {
err = s.grpcServer.Serve(listener)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Error(err)
}
}()
return nil
}
func (s *Server) Close() error {
if s.grpcServer != nil {
s.grpcServer.Stop()
}
return common.Close(
common.PtrOrNil(s.grpcServer),
s.tcpListener,
)
}
func (s *Server) StatsService() adapter.V2RayStatsService {
return s.statsService
}

View File

@@ -1,194 +0,0 @@
package v2rayapi
import (
"context"
"net"
"regexp"
"runtime"
"strings"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/trackerconn"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
"go.uber.org/atomic"
)
func init() {
StatsService_ServiceDesc.ServiceName = "v2ray.core.app.stats.command.StatsService"
}
var (
_ adapter.V2RayStatsService = (*StatsService)(nil)
_ StatsServiceServer = (*StatsService)(nil)
)
type StatsService struct {
createdAt time.Time
directIO bool
inbounds map[string]bool
outbounds map[string]bool
access sync.Mutex
counters map[string]*atomic.Int64
}
func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
if !options.Enabled {
return nil
}
inbounds := make(map[string]bool)
outbounds := make(map[string]bool)
for _, inbound := range options.Inbounds {
inbounds[inbound] = true
}
for _, outbound := range options.Outbounds {
outbounds[outbound] = true
}
return &StatsService{
createdAt: time.Now(),
directIO: options.DirectIO,
inbounds: inbounds,
outbounds: outbounds,
counters: make(map[string]*atomic.Int64),
}
}
func (s *StatsService) RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn {
var readCounter *atomic.Int64
var writeCounter *atomic.Int64
countInbound := inbound != "" && s.inbounds[inbound]
countOutbound := outbound != "" && s.outbounds[outbound]
if !countInbound && !countOutbound {
return conn
}
s.access.Lock()
if countInbound {
readCounter = s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink", readCounter)
writeCounter = s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink", writeCounter)
}
if countOutbound {
readCounter = s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink", readCounter)
writeCounter = s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink", writeCounter)
}
s.access.Unlock()
return trackerconn.New(conn, readCounter, writeCounter, s.directIO)
}
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn {
var readCounter *atomic.Int64
var writeCounter *atomic.Int64
countInbound := inbound != "" && s.inbounds[inbound]
countOutbound := outbound != "" && s.outbounds[outbound]
if !countInbound && !countOutbound {
return conn
}
s.access.Lock()
if countInbound {
readCounter = s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink", readCounter)
writeCounter = s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink", writeCounter)
}
if countOutbound {
readCounter = s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink", readCounter)
writeCounter = s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink", writeCounter)
}
s.access.Unlock()
return trackerconn.NewPacket(conn, readCounter, writeCounter)
}
func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
s.access.Lock()
counter, loaded := s.counters[request.Name]
s.access.Unlock()
if !loaded {
return nil, E.New(request.Name, " not found.")
}
var value int64
if request.Reset_ {
value = counter.Swap(0)
} else {
value = counter.Load()
}
return &GetStatsResponse{Stat: &Stat{Name: request.Name, Value: value}}, nil
}
func (s *StatsService) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
var response QueryStatsResponse
s.access.Lock()
defer s.access.Unlock()
if request.Regexp {
matchers := make([]*regexp.Regexp, 0, len(request.Patterns))
for _, pattern := range request.Patterns {
matcher, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
matchers = append(matchers, matcher)
}
for name, counter := range s.counters {
for _, matcher := range matchers {
if matcher.MatchString(name) {
var value int64
if request.Reset_ {
value = counter.Swap(0)
} else {
value = counter.Load()
}
response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
}
}
}
} else {
for name, counter := range s.counters {
for _, matcher := range request.Patterns {
if strings.Contains(name, matcher) {
var value int64
if request.Reset_ {
value = counter.Swap(0)
} else {
value = counter.Load()
}
response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
}
}
}
}
return &response, nil
}
func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest) (*SysStatsResponse, error) {
var rtm runtime.MemStats
runtime.ReadMemStats(&rtm)
response := &SysStatsResponse{
Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
NumGoroutine: uint32(runtime.NumGoroutine()),
Alloc: rtm.Alloc,
TotalAlloc: rtm.TotalAlloc,
Sys: rtm.Sys,
Mallocs: rtm.Mallocs,
Frees: rtm.Frees,
LiveObjects: rtm.Mallocs - rtm.Frees,
NumGC: rtm.NumGC,
PauseTotalNs: rtm.PauseTotalNs,
}
return response, nil
}
func (s *StatsService) mustEmbedUnimplementedStatsServiceServer() {
}
//nolint:staticcheck
func (s *StatsService) loadOrCreateCounter(name string, counter *atomic.Int64) *atomic.Int64 {
counter, loaded := s.counters[name]
if !loaded {
if counter == nil {
counter = atomic.NewInt64(0)
}
s.counters[name] = counter
}
return counter
}

View File

@@ -1,678 +0,0 @@
package v2rayapi
import (
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetStatsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Name of the stat counter.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Whether or not to reset the counter to fetching its value.
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
}
func (x *GetStatsRequest) Reset() {
*x = GetStatsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetStatsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetStatsRequest) ProtoMessage() {}
func (x *GetStatsRequest) ProtoReflect() protoreflect.Message {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetStatsRequest.ProtoReflect.Descriptor instead.
func (*GetStatsRequest) Descriptor() ([]byte, []int) {
return file_experimental_v2rayapi_stats_proto_rawDescGZIP(), []int{0}
}
func (x *GetStatsRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetStatsRequest) GetReset_() bool {
if x != nil {
return x.Reset_
}
return false
}
type Stat struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *Stat) Reset() {
*x = Stat{}
if protoimpl.UnsafeEnabled {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Stat) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Stat) ProtoMessage() {}
func (x *Stat) ProtoReflect() protoreflect.Message {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Stat.ProtoReflect.Descriptor instead.
func (*Stat) Descriptor() ([]byte, []int) {
return file_experimental_v2rayapi_stats_proto_rawDescGZIP(), []int{1}
}
func (x *Stat) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Stat) GetValue() int64 {
if x != nil {
return x.Value
}
return 0
}
type GetStatsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"`
}
func (x *GetStatsResponse) Reset() {
*x = GetStatsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetStatsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetStatsResponse) ProtoMessage() {}
func (x *GetStatsResponse) ProtoReflect() protoreflect.Message {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetStatsResponse.ProtoReflect.Descriptor instead.
func (*GetStatsResponse) Descriptor() ([]byte, []int) {
return file_experimental_v2rayapi_stats_proto_rawDescGZIP(), []int{2}
}
func (x *GetStatsResponse) GetStat() *Stat {
if x != nil {
return x.Stat
}
return nil
}
type QueryStatsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Deprecated, use Patterns instead
Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
Patterns []string `protobuf:"bytes,3,rep,name=patterns,proto3" json:"patterns,omitempty"`
Regexp bool `protobuf:"varint,4,opt,name=regexp,proto3" json:"regexp,omitempty"`
}
func (x *QueryStatsRequest) Reset() {
*x = QueryStatsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *QueryStatsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*QueryStatsRequest) ProtoMessage() {}
func (x *QueryStatsRequest) ProtoReflect() protoreflect.Message {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use QueryStatsRequest.ProtoReflect.Descriptor instead.
func (*QueryStatsRequest) Descriptor() ([]byte, []int) {
return file_experimental_v2rayapi_stats_proto_rawDescGZIP(), []int{3}
}
func (x *QueryStatsRequest) GetPattern() string {
if x != nil {
return x.Pattern
}
return ""
}
func (x *QueryStatsRequest) GetReset_() bool {
if x != nil {
return x.Reset_
}
return false
}
func (x *QueryStatsRequest) GetPatterns() []string {
if x != nil {
return x.Patterns
}
return nil
}
func (x *QueryStatsRequest) GetRegexp() bool {
if x != nil {
return x.Regexp
}
return false
}
type QueryStatsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"`
}
func (x *QueryStatsResponse) Reset() {
*x = QueryStatsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *QueryStatsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*QueryStatsResponse) ProtoMessage() {}
func (x *QueryStatsResponse) ProtoReflect() protoreflect.Message {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use QueryStatsResponse.ProtoReflect.Descriptor instead.
func (*QueryStatsResponse) Descriptor() ([]byte, []int) {
return file_experimental_v2rayapi_stats_proto_rawDescGZIP(), []int{4}
}
func (x *QueryStatsResponse) GetStat() []*Stat {
if x != nil {
return x.Stat
}
return nil
}
type SysStatsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *SysStatsRequest) Reset() {
*x = SysStatsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SysStatsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SysStatsRequest) ProtoMessage() {}
func (x *SysStatsRequest) ProtoReflect() protoreflect.Message {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SysStatsRequest.ProtoReflect.Descriptor instead.
func (*SysStatsRequest) Descriptor() ([]byte, []int) {
return file_experimental_v2rayapi_stats_proto_rawDescGZIP(), []int{5}
}
type SysStatsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"`
NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"`
Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"`
TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"`
Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"`
Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"`
Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"`
LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"`
PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"`
Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"`
}
func (x *SysStatsResponse) Reset() {
*x = SysStatsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SysStatsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SysStatsResponse) ProtoMessage() {}
func (x *SysStatsResponse) ProtoReflect() protoreflect.Message {
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SysStatsResponse.ProtoReflect.Descriptor instead.
func (*SysStatsResponse) Descriptor() ([]byte, []int) {
return file_experimental_v2rayapi_stats_proto_rawDescGZIP(), []int{6}
}
func (x *SysStatsResponse) GetNumGoroutine() uint32 {
if x != nil {
return x.NumGoroutine
}
return 0
}
func (x *SysStatsResponse) GetNumGC() uint32 {
if x != nil {
return x.NumGC
}
return 0
}
func (x *SysStatsResponse) GetAlloc() uint64 {
if x != nil {
return x.Alloc
}
return 0
}
func (x *SysStatsResponse) GetTotalAlloc() uint64 {
if x != nil {
return x.TotalAlloc
}
return 0
}
func (x *SysStatsResponse) GetSys() uint64 {
if x != nil {
return x.Sys
}
return 0
}
func (x *SysStatsResponse) GetMallocs() uint64 {
if x != nil {
return x.Mallocs
}
return 0
}
func (x *SysStatsResponse) GetFrees() uint64 {
if x != nil {
return x.Frees
}
return 0
}
func (x *SysStatsResponse) GetLiveObjects() uint64 {
if x != nil {
return x.LiveObjects
}
return 0
}
func (x *SysStatsResponse) GetPauseTotalNs() uint64 {
if x != nil {
return x.PauseTotalNs
}
return 0
}
func (x *SysStatsResponse) GetUptime() uint32 {
if x != nil {
return x.Uptime
}
return 0
}
var File_experimental_v2rayapi_stats_proto protoreflect.FileDescriptor
var file_experimental_v2rayapi_stats_proto_rawDesc = []byte{
0x0a, 0x21, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76,
0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61,
0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x22, 0x3b, 0x0a, 0x0f, 0x47, 0x65,
0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
0x52, 0x05, 0x72, 0x65, 0x73, 0x65, 0x74, 0x22, 0x30, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x74, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x10, 0x47, 0x65, 0x74,
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a,
0x04, 0x73, 0x74, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x22, 0x77,
0x0a, 0x11, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x14, 0x0a,
0x05, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65,
0x73, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x12,
0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x22, 0x45, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79,
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a,
0x04, 0x73, 0x74, 0x61, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x22, 0x11,
0x0a, 0x0f, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x22, 0xa2, 0x02, 0x0a, 0x10, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4e, 0x75, 0x6d, 0x47, 0x6f, 0x72,
0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x4e, 0x75,
0x6d, 0x47, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x75,
0x6d, 0x47, 0x43, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4e, 0x75, 0x6d, 0x47, 0x43,
0x12, 0x14, 0x0a, 0x05, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
0x05, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41,
0x6c, 0x6c, 0x6f, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x54, 0x6f, 0x74, 0x61,
0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x79, 0x73, 0x18, 0x05, 0x20,
0x01, 0x28, 0x04, 0x52, 0x03, 0x53, 0x79, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x61, 0x6c, 0x6c,
0x6f, 0x63, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x4d, 0x61, 0x6c, 0x6c, 0x6f,
0x63, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x72, 0x65, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
0x04, 0x52, 0x05, 0x46, 0x72, 0x65, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x69, 0x76, 0x65,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x4c,
0x69, 0x76, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x61,
0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04,
0x52, 0x0c, 0x50, 0x61, 0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x12, 0x16,
0x0a, 0x06, 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06,
0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x32, 0xb4, 0x02, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74,
0x61, 0x74, 0x73, 0x12, 0x26, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74,
0x61, 0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53,
0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
0x74, 0x61, 0x74, 0x73, 0x12, 0x28, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e,
0x74, 0x61, 0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65,
0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29,
0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x32,
0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x0b, 0x47,
0x65, 0x74, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x26, 0x2e, 0x65, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x61,
0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x27, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61,
0x6c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74,
0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x34, 0x5a,
0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x61, 0x67, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x2f, 0x65, 0x78,
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79,
0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_experimental_v2rayapi_stats_proto_rawDescOnce sync.Once
file_experimental_v2rayapi_stats_proto_rawDescData = file_experimental_v2rayapi_stats_proto_rawDesc
)
func file_experimental_v2rayapi_stats_proto_rawDescGZIP() []byte {
file_experimental_v2rayapi_stats_proto_rawDescOnce.Do(func() {
file_experimental_v2rayapi_stats_proto_rawDescData = protoimpl.X.CompressGZIP(file_experimental_v2rayapi_stats_proto_rawDescData)
})
return file_experimental_v2rayapi_stats_proto_rawDescData
}
var (
file_experimental_v2rayapi_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
file_experimental_v2rayapi_stats_proto_goTypes = []interface{}{
(*GetStatsRequest)(nil), // 0: experimental.v2rayapi.GetStatsRequest
(*Stat)(nil), // 1: experimental.v2rayapi.Stat
(*GetStatsResponse)(nil), // 2: experimental.v2rayapi.GetStatsResponse
(*QueryStatsRequest)(nil), // 3: experimental.v2rayapi.QueryStatsRequest
(*QueryStatsResponse)(nil), // 4: experimental.v2rayapi.QueryStatsResponse
(*SysStatsRequest)(nil), // 5: experimental.v2rayapi.SysStatsRequest
(*SysStatsResponse)(nil), // 6: experimental.v2rayapi.SysStatsResponse
}
)
var file_experimental_v2rayapi_stats_proto_depIdxs = []int32{
1, // 0: experimental.v2rayapi.GetStatsResponse.stat:type_name -> experimental.v2rayapi.Stat
1, // 1: experimental.v2rayapi.QueryStatsResponse.stat:type_name -> experimental.v2rayapi.Stat
0, // 2: experimental.v2rayapi.StatsService.GetStats:input_type -> experimental.v2rayapi.GetStatsRequest
3, // 3: experimental.v2rayapi.StatsService.QueryStats:input_type -> experimental.v2rayapi.QueryStatsRequest
5, // 4: experimental.v2rayapi.StatsService.GetSysStats:input_type -> experimental.v2rayapi.SysStatsRequest
2, // 5: experimental.v2rayapi.StatsService.GetStats:output_type -> experimental.v2rayapi.GetStatsResponse
4, // 6: experimental.v2rayapi.StatsService.QueryStats:output_type -> experimental.v2rayapi.QueryStatsResponse
6, // 7: experimental.v2rayapi.StatsService.GetSysStats:output_type -> experimental.v2rayapi.SysStatsResponse
5, // [5:8] is the sub-list for method output_type
2, // [2:5] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_experimental_v2rayapi_stats_proto_init() }
func file_experimental_v2rayapi_stats_proto_init() {
if File_experimental_v2rayapi_stats_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_experimental_v2rayapi_stats_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetStatsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_experimental_v2rayapi_stats_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Stat); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_experimental_v2rayapi_stats_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetStatsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_experimental_v2rayapi_stats_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*QueryStatsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_experimental_v2rayapi_stats_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*QueryStatsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_experimental_v2rayapi_stats_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SysStatsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_experimental_v2rayapi_stats_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SysStatsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_experimental_v2rayapi_stats_proto_rawDesc,
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_experimental_v2rayapi_stats_proto_goTypes,
DependencyIndexes: file_experimental_v2rayapi_stats_proto_depIdxs,
MessageInfos: file_experimental_v2rayapi_stats_proto_msgTypes,
}.Build()
File_experimental_v2rayapi_stats_proto = out.File
file_experimental_v2rayapi_stats_proto_rawDesc = nil
file_experimental_v2rayapi_stats_proto_goTypes = nil
file_experimental_v2rayapi_stats_proto_depIdxs = nil
}

View File

@@ -1,53 +0,0 @@
syntax = "proto3";
package experimental.v2rayapi;
option go_package = "github.com/sagernet/sing-box/experimental/v2rayapi";
message GetStatsRequest {
// Name of the stat counter.
string name = 1;
// Whether or not to reset the counter to fetching its value.
bool reset = 2;
}
message Stat {
string name = 1;
int64 value = 2;
}
message GetStatsResponse {
Stat stat = 1;
}
message QueryStatsRequest {
// Deprecated, use Patterns instead
string pattern = 1;
bool reset = 2;
repeated string patterns = 3;
bool regexp = 4;
}
message QueryStatsResponse {
repeated Stat stat = 1;
}
message SysStatsRequest {}
message SysStatsResponse {
uint32 NumGoroutine = 1;
uint32 NumGC = 2;
uint64 Alloc = 3;
uint64 TotalAlloc = 4;
uint64 Sys = 5;
uint64 Mallocs = 6;
uint64 Frees = 7;
uint64 LiveObjects = 8;
uint64 PauseTotalNs = 9;
uint32 Uptime = 10;
}
service StatsService {
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
}

View File

@@ -1,173 +0,0 @@
package v2rayapi
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// StatsServiceClient is the client API for StatsService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type StatsServiceClient interface {
GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error)
QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error)
GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error)
}
type statsServiceClient struct {
cc grpc.ClientConnInterface
}
func NewStatsServiceClient(cc grpc.ClientConnInterface) StatsServiceClient {
return &statsServiceClient{cc}
}
func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
out := new(GetStatsResponse)
err := c.cc.Invoke(ctx, "/experimental.v2rayapi.StatsService/GetStats", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) {
out := new(QueryStatsResponse)
err := c.cc.Invoke(ctx, "/experimental.v2rayapi.StatsService/QueryStats", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) {
out := new(SysStatsResponse)
err := c.cc.Invoke(ctx, "/experimental.v2rayapi.StatsService/GetSysStats", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// StatsServiceServer is the server API for StatsService service.
// All implementations must embed UnimplementedStatsServiceServer
// for forward compatibility
type StatsServiceServer interface {
GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error)
GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error)
mustEmbedUnimplementedStatsServiceServer()
}
// UnimplementedStatsServiceServer must be embedded to have forward compatible implementations.
type UnimplementedStatsServiceServer struct{}
func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented")
}
func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryStats not implemented")
}
func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
}
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
// UnsafeStatsServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to StatsServiceServer will
// result in compilation errors.
type UnsafeStatsServiceServer interface {
mustEmbedUnimplementedStatsServiceServer()
}
func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) {
s.RegisterService(&StatsService_ServiceDesc, srv)
}
func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetStatsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StatsServiceServer).GetStats(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/experimental.v2rayapi.StatsService/GetStats",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _StatsService_QueryStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryStatsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StatsServiceServer).QueryStats(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/experimental.v2rayapi.StatsService/QueryStats",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StatsServiceServer).QueryStats(ctx, req.(*QueryStatsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SysStatsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StatsServiceServer).GetSysStats(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/experimental.v2rayapi.StatsService/GetSysStats",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StatsServiceServer).GetSysStats(ctx, req.(*SysStatsRequest))
}
return interceptor(ctx, in, info, handler)
}
// StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var StatsService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "experimental.v2rayapi.StatsService",
HandlerType: (*StatsServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetStats",
Handler: _StatsService_GetStats_Handler,
},
{
MethodName: "QueryStats",
Handler: _StatsService_QueryStats_Handler,
},
{
MethodName: "GetSysStats",
Handler: _StatsService_GetSysStats_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "experimental/v2rayapi/stats.proto",
}

37
go.mod
View File

@@ -4,59 +4,52 @@ go 1.18
require (
berty.tech/go-libtor v1.0.385
github.com/Dreamacro/clash v1.11.8
github.com/caddyserver/certmagic v0.17.2
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc
github.com/cretz/bine v0.2.0
github.com/database64128/tfo-go/v2 v2.0.1
github.com/database64128/tfo-go v1.1.2
github.com/dustin/go-humanize v1.0.0
github.com/fsnotify/fsnotify v1.5.4
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.3.0+incompatible
github.com/gofrs/uuid v4.2.0+incompatible
github.com/hashicorp/yamux v0.1.1
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.0.4
github.com/miekg/dns v1.1.50
github.com/oschwald/maxminddb-golang v1.10.0
github.com/pires/go-proxyproto v0.6.2
github.com/refraction-networking/utls v1.1.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
github.com/sagernet/sing v0.0.0-20221006081821-c4e9bf11fa00
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.10.0
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b
golang.org/x/net v0.0.0-20221004154528-8021a29435af
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
)
//replace github.com/sagernet/sing => ../sing
require (
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
@@ -66,11 +59,11 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.23.0 // indirect
go.uber.org/zap v1.22.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/text v0.3.7 // indirect

83
go.sum
View File

@@ -3,21 +3,13 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8=
github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -25,8 +17,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
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/tfo-go/v2 v2.0.1 h1:VFfturVoq6NmPGfhXO1K15x82KR19aAfw1RYtTzyVv4=
github.com/database64128/tfo-go/v2 v2.0.1/go.mod h1:FDdt4JaAsRU66wsYHxSVytYimPkKIHupVsxM+5DhvjY=
github.com/database64128/tfo-go v1.1.2 h1:GwxtJp09BdUTVEoeT421t231eNZoGOCRkklbl4WI1kU=
github.com/database64128/tfo-go v1.1.2/go.mod h1:jgrSUPyOvTGQyn6irCOpk7L2W/q/0VLZZcovQiMi+bI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -51,8 +43,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
@@ -87,16 +79,14 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
@@ -109,8 +99,6 @@ github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -131,30 +119,30 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/refraction-networking/utls v1.1.2 h1:a7GQauRt72VG+wtNm0lnrAaCGlyX47gEi1++dSsDBpw=
github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tSdX1+SqUB7rL0QnGqyg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kxbgGa2Xp0H2CCsx1o2pVTt0f+hmfuHH4=
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20221006081821-c4e9bf11fa00 h1:UkgEDnH3L9eBxob+3AbE9wM4mjFnRLnaPjfLSNe+C74=
github.com/sagernet/sing v0.0.0-20221006081821-c4e9bf11fa00/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 h1:AEdyJxEDFq38z0pBX/0MpikQapGMIch+1ADe9k1bJqU=
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3 h1:9Igu/lgB1na+YTSEX6/YtPugAlMRyxLCDb7X+I0gdAE=
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3/go.mod h1:qbqV9lwcXJnj1Tw4we7oA6Z8zGE/kCXQBCzuhzRWVw8=
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 h1:AZzFNRR/ZwMTceUQ1b/mxx6oyKqmFymdMn/yleJmoVM=
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
@@ -175,8 +163,6 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
@@ -186,18 +172,17 @@ go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q=
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -226,11 +211,9 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4=
golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -239,7 +222,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -251,7 +233,6 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -259,13 +240,12 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -288,7 +268,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -347,8 +326,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=

Some files were not shown because too many files have changed in this diff Show More