Compare commits
87 Commits
v1.1-beta3
...
v1.1-beta1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e0958b4ac | ||
|
|
6a26737508 | ||
|
|
92a92f39c5 | ||
|
|
fc533cd38d | ||
|
|
68e286499d | ||
|
|
f5c1900aad | ||
|
|
6591dd58ca | ||
|
|
54af113363 | ||
|
|
3f1fe814ef | ||
|
|
5a2cebebd1 | ||
|
|
b8009d61b2 | ||
|
|
a61a64bf9e | ||
|
|
7d17c52fea | ||
|
|
f5b15b392b | ||
|
|
8a53846efd | ||
|
|
badc454452 | ||
|
|
a01bb569d1 | ||
|
|
89ff9f8368 | ||
|
|
7f816a2ebc | ||
|
|
39c141651a | ||
|
|
b0ad9bb6f1 | ||
|
|
d135d0f287 | ||
|
|
b183ccf23d | ||
|
|
c2969bc186 | ||
|
|
bd86bfcd22 | ||
|
|
8aec64b855 | ||
|
|
1445bdba37 | ||
|
|
29d08e63b5 | ||
|
|
1173fdea64 | ||
|
|
968430c338 | ||
|
|
3e5bee6faf | ||
|
|
aa613cba73 | ||
|
|
1e510511ae | ||
|
|
1b44faed17 | ||
|
|
c7a485815c | ||
|
|
7f9c870bba | ||
|
|
b5564ef3d3 | ||
|
|
8ce244dd04 | ||
|
|
0f57b93925 | ||
|
|
c90a77a185 | ||
|
|
c6586f19fa | ||
|
|
cbab86ae38 | ||
|
|
17b5f031f1 | ||
|
|
b00b6b9e25 | ||
|
|
fb6b3b0401 | ||
|
|
22ea878fe9 | ||
|
|
abe3dc6039 | ||
|
|
852829b9dc | ||
|
|
407509c985 | ||
|
|
9856b73cb5 | ||
|
|
f42356fbcb | ||
|
|
d0b467671a | ||
|
|
c18c545798 | ||
|
|
693ef293ac | ||
|
|
a006627795 | ||
|
|
0738b184e4 | ||
|
|
42524ba04e | ||
|
|
63fc95b96d | ||
|
|
ab436fc137 | ||
|
|
1546770bfd | ||
|
|
f4b2099488 | ||
|
|
a2c4d68031 | ||
|
|
cfe14f2817 | ||
|
|
a5402ffb69 | ||
|
|
4d24cf5ec4 | ||
|
|
668d354771 | ||
|
|
ad14719b14 | ||
|
|
d9aa0a67d6 | ||
|
|
92bf784f4f | ||
|
|
395b13103a | ||
|
|
628cf56d3c | ||
|
|
ac5582537f | ||
|
|
9aa7a20d96 | ||
|
|
189f02c802 | ||
|
|
2373281c41 | ||
|
|
e8f4c2d36f | ||
|
|
07b6db23c1 | ||
|
|
9a3360e5d0 | ||
|
|
007a278ac8 | ||
|
|
1db7f45370 | ||
|
|
b271e19a23 | ||
|
|
79b6bdfda1 | ||
|
|
38088f28b0 | ||
|
|
dfb8b5f2fa | ||
|
|
9913e0e025 | ||
|
|
ce567ffdde | ||
|
|
5a9913eca5 |
34
.github/workflows/test.yml
vendored
Normal file
34
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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
3
.gitignore
vendored
@@ -4,4 +4,5 @@
|
|||||||
/*.db
|
/*.db
|
||||||
/site/
|
/site/
|
||||||
/bin/
|
/bin/
|
||||||
/dist/
|
/dist/
|
||||||
|
/sing-box
|
||||||
@@ -9,8 +9,9 @@ builds:
|
|||||||
gcflags:
|
gcflags:
|
||||||
- all=-trimpath={{.Env.GOPATH}}
|
- all=-trimpath={{.Env.GOPATH}}
|
||||||
ldflags:
|
ldflags:
|
||||||
- -X github.com/sagernet/sing-box/constant.Commit={{ .ShortCommit }} -s -w -buildid=
|
- -s -w -buildid=
|
||||||
tags:
|
tags:
|
||||||
|
- with_gvisor
|
||||||
- with_quic
|
- with_quic
|
||||||
- with_wireguard
|
- with_wireguard
|
||||||
- with_clash_api
|
- with_clash_api
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ ENV CGO_ENABLED=0
|
|||||||
RUN set -ex \
|
RUN set -ex \
|
||||||
&& apk add git build-base \
|
&& apk add git build-base \
|
||||||
&& export COMMIT=$(git rev-parse --short HEAD) \
|
&& export COMMIT=$(git rev-parse --short HEAD) \
|
||||||
&& go build -v -trimpath -tags 'no_gvisor,with_quic,with_wireguard,with_acme' \
|
&& go build -v -trimpath -tags with_quic,with_wireguard,with_acme \
|
||||||
-o /go/bin/sing-box \
|
-o /go/bin/sing-box \
|
||||||
-ldflags "-X github.com/sagernet/sing-box/constant.Commit=${COMMIT} -w -s -buildid=" \
|
-ldflags "-s -w -buildid=" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
FROM alpine AS dist
|
FROM alpine AS dist
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
|
|||||||
22
Makefile
22
Makefile
@@ -1,9 +1,8 @@
|
|||||||
NAME = sing-box
|
NAME = sing-box
|
||||||
COMMIT = $(shell git rev-parse --short HEAD)
|
COMMIT = $(shell git rev-parse --short HEAD)
|
||||||
TAGS ?= with_quic,with_wireguard,with_clash_api
|
TAGS ?= with_gvisor,with_quic,with_wireguard,with_clash_api
|
||||||
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \
|
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
|
||||||
'-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \
|
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
|
||||||
-w -s -buildid='
|
|
||||||
MAIN = ./cmd/sing-box
|
MAIN = ./cmd/sing-box
|
||||||
|
|
||||||
.PHONY: test release
|
.PHONY: test release
|
||||||
@@ -61,14 +60,19 @@ release_install:
|
|||||||
go install -v github.com/tcnksm/ghr@latest
|
go install -v github.com/tcnksm/ghr@latest
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@go test -v . && \
|
@go test -v ./... && \
|
||||||
pushd test && \
|
cd test && \
|
||||||
go mod tidy && \
|
go mod tidy && \
|
||||||
go test -v -tags with_quic,with_wireguard,with_grpc,with_ech,with_utls . && \
|
go test -v -tags "$(TAGS_TEST)" .
|
||||||
popd
|
|
||||||
|
test_stdio:
|
||||||
|
@go test -v ./... && \
|
||||||
|
cd test && \
|
||||||
|
go mod tidy && \
|
||||||
|
go test -v -tags "$(TAGS_TEST),force_stdio" .
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf bin dist
|
rm -rf bin dist sing-box
|
||||||
rm -f $(shell go env GOPATH)/sing-box
|
rm -f $(shell go env GOPATH)/sing-box
|
||||||
|
|
||||||
update:
|
update:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ type ClashServer interface {
|
|||||||
Mode() string
|
Mode() string
|
||||||
StoreSelected() bool
|
StoreSelected() bool
|
||||||
CacheFile() ClashCacheFile
|
CacheFile() ClashCacheFile
|
||||||
|
HistoryStorage() *urltest.HistoryStorage
|
||||||
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
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)
|
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
||||||
}
|
}
|
||||||
@@ -36,3 +38,13 @@ func OutboundTag(detour Outbound) string {
|
|||||||
}
|
}
|
||||||
return detour.Tag()
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-box/option"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
@@ -38,16 +38,14 @@ type InboundContext struct {
|
|||||||
|
|
||||||
// cache
|
// cache
|
||||||
|
|
||||||
InboundDetour string
|
InboundDetour string
|
||||||
LastInbound string
|
LastInbound string
|
||||||
OriginDestination M.Socksaddr
|
OriginDestination M.Socksaddr
|
||||||
DomainStrategy dns.DomainStrategy
|
InboundOptions option.InboundOptions
|
||||||
SniffEnabled bool
|
DestinationAddresses []netip.Addr
|
||||||
SniffOverrideDestination bool
|
SourceGeoIPCode string
|
||||||
DestinationAddresses []netip.Addr
|
GeoIPCode string
|
||||||
SourceGeoIPCode string
|
ProcessInfo *process.Info
|
||||||
GeoIPCode string
|
|
||||||
ProcessInfo *process.Info
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type inboundContextKey struct{}
|
type inboundContextKey struct{}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
mdns "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Router interface {
|
type Router interface {
|
||||||
@@ -27,11 +27,11 @@ type Router interface {
|
|||||||
GeoIPReader() *geoip.Reader
|
GeoIPReader() *geoip.Reader
|
||||||
LoadGeosite(code string) (Rule, error)
|
LoadGeosite(code string) (Rule, error)
|
||||||
|
|
||||||
Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error)
|
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
|
||||||
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
||||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||||
|
|
||||||
InterfaceBindManager() control.BindManager
|
InterfaceFinder() control.InterfaceFinder
|
||||||
DefaultInterface() string
|
DefaultInterface() string
|
||||||
AutoDetectInterface() bool
|
AutoDetectInterface() bool
|
||||||
DefaultMark() int
|
DefaultMark() int
|
||||||
@@ -41,7 +41,10 @@ type Router interface {
|
|||||||
Rules() []Rule
|
Rules() []Rule
|
||||||
|
|
||||||
ClashServer() ClashServer
|
ClashServer() ClashServer
|
||||||
SetClashServer(controller ClashServer)
|
SetClashServer(server ClashServer)
|
||||||
|
|
||||||
|
V2RayServer() V2RayServer
|
||||||
|
SetV2RayServer(server V2RayServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rule interface {
|
type Rule interface {
|
||||||
|
|||||||
@@ -38,13 +38,25 @@ type myUpstreamHandlerWrapper struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
w.metadata.Destination = metadata.Destination
|
myMetadata := w.metadata
|
||||||
return w.connectionHandler(ctx, conn, w.metadata)
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
|
return w.connectionHandler(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
w.metadata.Destination = metadata.Destination
|
myMetadata := w.metadata
|
||||||
return w.packetHandler(ctx, conn, w.metadata)
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
|
return w.packetHandler(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
@@ -78,13 +90,23 @@ func NewUpstreamContextHandler(
|
|||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
myMetadata.Destination = metadata.Destination
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
return w.connectionHandler(ctx, conn, *myMetadata)
|
return w.connectionHandler(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
myMetadata.Destination = metadata.Destination
|
if metadata.Source.IsValid() {
|
||||||
|
myMetadata.Source = metadata.Source
|
||||||
|
}
|
||||||
|
if metadata.Destination.IsValid() {
|
||||||
|
myMetadata.Destination = metadata.Destination
|
||||||
|
}
|
||||||
return w.packetHandler(ctx, conn, *myMetadata)
|
return w.packetHandler(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
33
box.go
33
box.go
@@ -31,6 +31,7 @@ type Box struct {
|
|||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
logFile *os.File
|
logFile *os.File
|
||||||
clashServer adapter.ClashServer
|
clashServer adapter.ClashServer
|
||||||
|
v2rayServer adapter.V2RayServer
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +40,14 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
logOptions := common.PtrValueOrDefault(options.Log)
|
logOptions := common.PtrValueOrDefault(options.Log)
|
||||||
|
|
||||||
var needClashAPI bool
|
var needClashAPI bool
|
||||||
if options.Experimental != nil && options.Experimental.ClashAPI != nil && options.Experimental.ClashAPI.ExternalController != "" {
|
var needV2RayAPI bool
|
||||||
needClashAPI = true
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var logFactory log.Factory
|
var logFactory log.Factory
|
||||||
@@ -149,6 +156,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var clashServer adapter.ClashServer
|
var clashServer adapter.ClashServer
|
||||||
|
var v2rayServer adapter.V2RayServer
|
||||||
if needClashAPI {
|
if needClashAPI {
|
||||||
clashServer, err = experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI))
|
clashServer, err = experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -156,15 +164,23 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
}
|
}
|
||||||
router.SetClashServer(clashServer)
|
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)
|
||||||
|
}
|
||||||
return &Box{
|
return &Box{
|
||||||
router: router,
|
router: router,
|
||||||
inbounds: inbounds,
|
inbounds: inbounds,
|
||||||
outbounds: outbounds,
|
outbounds: outbounds,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
logFactory: logFactory,
|
logFactory: logFactory,
|
||||||
logger: logFactory.NewLogger(""),
|
logger: logFactory.Logger(),
|
||||||
logFile: logFile,
|
logFile: logFile,
|
||||||
clashServer: clashServer,
|
clashServer: clashServer,
|
||||||
|
v2rayServer: v2rayServer,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -223,6 +239,12 @@ func (s *Box) start() error {
|
|||||||
return E.Cause(err, "start clash api server")
|
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)")
|
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -244,6 +266,11 @@ func (s *Box) Close() error {
|
|||||||
s.router,
|
s.router,
|
||||||
s.logFactory,
|
s.logFactory,
|
||||||
s.clashServer,
|
s.clashServer,
|
||||||
|
s.v2rayServer,
|
||||||
common.PtrOrNil(s.logFile),
|
common.PtrOrNil(s.logFile),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Box) Router() adapter.Router {
|
||||||
|
return s.router
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -25,30 +25,40 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printVersion(cmd *cobra.Command, args []string) {
|
func printVersion(cmd *cobra.Command, args []string) {
|
||||||
var version string
|
if nameOnly {
|
||||||
if !nameOnly {
|
os.Stdout.WriteString(C.Version + "\n")
|
||||||
version = "sing-box "
|
return
|
||||||
}
|
}
|
||||||
version += F.ToString(C.Version)
|
version := "sing-box version " + C.Version + "\n\n"
|
||||||
if C.Commit != "" {
|
version += "Environment: " + runtime.Version() + " " + runtime.GOOS + "/" + runtime.GOARCH + "\n"
|
||||||
version += "." + C.Commit
|
|
||||||
}
|
var tags string
|
||||||
if !nameOnly {
|
var revision string
|
||||||
version += " ("
|
|
||||||
version += runtime.Version()
|
debugInfo, loaded := debug.ReadBuildInfo()
|
||||||
version += ", "
|
if loaded {
|
||||||
version += runtime.GOOS
|
for _, setting := range debugInfo.Settings {
|
||||||
version += "/"
|
switch setting.Key {
|
||||||
version += runtime.GOARCH
|
case "-tags":
|
||||||
version += ", "
|
tags = setting.Value
|
||||||
version += "CGO "
|
case "vcs.revision":
|
||||||
if C.CGO_ENABLED {
|
revision = setting.Value
|
||||||
version += "enabled"
|
}
|
||||||
} else {
|
|
||||||
version += "disabled"
|
|
||||||
}
|
}
|
||||||
version += ")"
|
|
||||||
}
|
}
|
||||||
version += "\n"
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
os.Stdout.WriteString(version)
|
os.Stdout.WriteString(version)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
_ "github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func WrapH2(err error) error {
|
|||||||
if err == io.ErrUnexpectedEOF {
|
if err == io.ErrUnexpectedEOF {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
if Contains(err, "client disconnected", "body closed by handler") {
|
if Contains(err, "client disconnected", "body closed by handler", "response body closed", "; CANCEL") {
|
||||||
return net.ErrClosed
|
return net.ErrClosed
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|||||||
210
common/badtls/badtls.go
Normal file
210
common/badtls/badtls.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
//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()
|
||||||
|
}
|
||||||
12
common/badtls/badtls_stub.go
Normal file
12
common/badtls/badtls_stub.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build !go1.19 || go1.20
|
||||||
|
|
||||||
|
package badtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Create(conn *tls.Conn) (TLSConn, error) {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
13
common/badtls/conn.go
Normal file
13
common/badtls/conn.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package badtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TLSConn interface {
|
||||||
|
net.Conn
|
||||||
|
HandshakeContext(ctx context.Context) error
|
||||||
|
ConnectionState() tls.ConnectionState
|
||||||
|
}
|
||||||
26
common/badtls/link.go
Normal file
26
common/badtls/link.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//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
|
||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/database64128/tfo-go"
|
"github.com/database64128/tfo-go/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var warnBindInterfaceOnUnsupportedPlatform = warning.New(
|
var warnBindInterfaceOnUnsupportedPlatform = warning.New(
|
||||||
@@ -65,25 +65,23 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
|||||||
var listener net.ListenConfig
|
var listener net.ListenConfig
|
||||||
if options.BindInterface != "" {
|
if options.BindInterface != "" {
|
||||||
warnBindInterfaceOnUnsupportedPlatform.Check()
|
warnBindInterfaceOnUnsupportedPlatform.Check()
|
||||||
bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)
|
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
} else if router.AutoDetectInterface() {
|
} else if router.AutoDetectInterface() {
|
||||||
if C.IsWindows {
|
const useInterfaceName = C.IsLinux
|
||||||
bindFunc := control.BindToInterfaceIndexFunc(func(network, address string) int {
|
bindFunc := control.BindToInterfaceFunc(router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||||
return router.InterfaceMonitor().DefaultInterfaceIndex(M.ParseSocksaddr(address).Addr)
|
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||||
})
|
if C.IsLinux {
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
return router.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
} else {
|
||||||
} else {
|
return "", router.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
||||||
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)
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
|
||||||
}
|
|
||||||
} else if router.DefaultInterface() != "" {
|
} else if router.DefaultInterface() != "" {
|
||||||
bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())
|
bindFunc := control.BindToInterface(router.InterfaceFinder(), router.DefaultInterface(), -1)
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
}
|
}
|
||||||
@@ -112,7 +110,13 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
|||||||
if options.TCPFastOpen {
|
if options.TCPFastOpen {
|
||||||
warnTFOOnUnsupportedPlatform.Check()
|
warnTFOOnUnsupportedPlatform.Check()
|
||||||
}
|
}
|
||||||
if !options.UDPFragment {
|
var udpFragment bool
|
||||||
|
if options.UDPFragment != nil {
|
||||||
|
udpFragment = *options.UDPFragment
|
||||||
|
} else {
|
||||||
|
udpFragment = options.UDPFragmentDefault
|
||||||
|
}
|
||||||
|
if !udpFragment {
|
||||||
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
|
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
|
||||||
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
|
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
|
||||||
}
|
}
|
||||||
@@ -142,7 +146,7 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
|||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
return d.udpDialer.DialContext(ctx, network, address.String())
|
return d.udpDialer.DialContext(ctx, network, address.String())
|
||||||
}
|
}
|
||||||
return d.dialer.DialContext(ctx, network, address.Unwrap().String())
|
return DialSlowContext(&d.dialer, ctx, network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
|||||||
142
common/dialer/tfo.go
Normal file
142
common/dialer/tfo.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
c.err = E.Cause(err, "dial tcp fast open")
|
||||||
|
}
|
||||||
|
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) LazyHeadroom() 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)
|
||||||
|
}
|
||||||
@@ -466,10 +466,7 @@ func (c *ClientPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if buffer.FreeLen() < int(length) {
|
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||||
return destination, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
_, err = io.ReadFull(c.ExtendedConn, buffer.Extend(int(length)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package mux
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@@ -15,6 +14,7 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"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 {
|
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
@@ -26,14 +26,21 @@ func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var stream net.Conn
|
var group task.Group
|
||||||
for {
|
group.Append0(func(ctx context.Context) error {
|
||||||
stream, err = session.Accept()
|
var stream net.Conn
|
||||||
if err != nil {
|
for {
|
||||||
return err
|
stream, err = session.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
|
||||||
}
|
}
|
||||||
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
|
})
|
||||||
}
|
group.Cleanup(func() {
|
||||||
|
session.Close()
|
||||||
|
})
|
||||||
|
return group.Run(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
|
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
|
||||||
@@ -158,9 +165,6 @@ func (c *ServerPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksad
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if buffer.FreeLen() < int(length) {
|
|
||||||
return destination, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -223,9 +227,6 @@ func (c *ServerPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if buffer.FreeLen() < int(length) {
|
|
||||||
return destination, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -28,11 +28,5 @@ type Info struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||||
info, err := findProcessInfo(searcher, ctx, network, source, destination)
|
return 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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ func (l *Listener) Accept() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
if header != nil {
|
if header != nil {
|
||||||
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
|
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
|
||||||
Source: M.SocksaddrFromNet(header.SourceAddr),
|
Source: M.SocksaddrFromNet(header.SourceAddr).Unwrap(),
|
||||||
Destination: M.SocksaddrFromNet(header.DestinationAddr),
|
Destination: M.SocksaddrFromNet(header.DestinationAddr).Unwrap(),
|
||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
return conn, nil
|
return conn, nil
|
||||||
|
|||||||
@@ -2,14 +2,11 @@ package redir
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@@ -32,6 +29,18 @@ func TProxy(fd uintptr, isIPv6 bool) error {
|
|||||||
return err
|
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) {
|
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
||||||
controlMessages, err := unix.ParseSocketControlMessage(oob)
|
controlMessages, err := unix.ParseSocketControlMessage(oob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -46,79 +55,3 @@ func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
|||||||
}
|
}
|
||||||
return netip.AddrPort{}, E.New("not found")
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,19 +3,20 @@
|
|||||||
package redir
|
package redir
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TProxy(fd uintptr, isIPv6 bool) error {
|
func TProxy(fd uintptr, isIPv6 bool) error {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TProxyWriteBack() control.Func {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
||||||
return netip.AddrPort{}, os.ErrInvalid
|
return netip.AddrPort{}, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
"github.com/sagernet/sing/common/task"
|
"github.com/sagernet/sing/common/task"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.InboundContext, error) {
|
func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.InboundContext, error) {
|
||||||
@@ -44,18 +45,13 @@ func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DomainNameQuery(ctx context.Context, packet []byte) (*adapter.InboundContext, error) {
|
func DomainNameQuery(ctx context.Context, packet []byte) (*adapter.InboundContext, error) {
|
||||||
var parser dnsmessage.Parser
|
var msg mDNS.Msg
|
||||||
_, err := parser.Start(packet)
|
err := msg.Unpack(packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
question, err := parser.Question()
|
if len(msg.Question) == 0 || msg.Question[0].Qclass != mDNS.ClassINET || !M.IsDomainName(msg.Question[0].Name) {
|
||||||
if err != nil {
|
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
domain := question.Name.String()
|
return &adapter.InboundContext{Protocol: C.ProtocolDNS}, nil
|
||||||
if question.Class == dnsmessage.ClassINET && IsDomainName(domain) {
|
|
||||||
return &adapter.InboundContext{Protocol: C.ProtocolDNS /*, Domain: domain*/}, nil
|
|
||||||
}
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package sniff
|
|
||||||
|
|
||||||
import _ "unsafe" // for linkname
|
|
||||||
|
|
||||||
//go:linkname IsDomainName net.isDomainName
|
|
||||||
func IsDomainName(domain string) bool
|
|
||||||
@@ -24,8 +24,7 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if typeByte&0x40 == 0 {
|
||||||
if typeByte&0x80 == 0 || typeByte&0x40 == 0 {
|
|
||||||
return nil, E.New("bad type byte")
|
return nil, E.New("bad type byte")
|
||||||
}
|
}
|
||||||
var versionNumber uint32
|
var versionNumber uint32
|
||||||
@@ -145,9 +144,6 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
|
|||||||
default:
|
default:
|
||||||
return nil, E.New("bad packet number length")
|
return nil, E.New("bad packet number length")
|
||||||
}
|
}
|
||||||
if packetNumber != 0 {
|
|
||||||
return nil, E.New("bad packet number: ", packetNumber)
|
|
||||||
}
|
|
||||||
extHdrLen := hdrLen + int(packetNumberLength)
|
extHdrLen := hdrLen + int(packetNumberLength)
|
||||||
copy(newPacket[extHdrLen:hdrLen+4], packet[extHdrLen:])
|
copy(newPacket[extHdrLen:hdrLen+4], packet[extHdrLen:])
|
||||||
data := newPacket[extHdrLen : int(packetLen)+hdrLen]
|
data := newPacket[extHdrLen : int(packetLen)+hdrLen]
|
||||||
@@ -172,37 +168,76 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var frameType byte
|
||||||
|
var frameLen uint64
|
||||||
|
var fragments []struct {
|
||||||
|
offset uint64
|
||||||
|
length uint64
|
||||||
|
payload []byte
|
||||||
|
}
|
||||||
decryptedReader := bytes.NewReader(decrypted)
|
decryptedReader := bytes.NewReader(decrypted)
|
||||||
frameType, err := decryptedReader.ReadByte()
|
for {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for frameType == 0x0 {
|
|
||||||
// skip padding
|
|
||||||
frameType, err = decryptedReader.ReadByte()
|
frameType, err = decryptedReader.ReadByte()
|
||||||
if err != nil {
|
if err == io.EOF {
|
||||||
return nil, err
|
break
|
||||||
|
}
|
||||||
|
switch frameType {
|
||||||
|
case 0x0:
|
||||||
|
continue
|
||||||
|
case 0x1:
|
||||||
|
continue
|
||||||
|
case 0x6:
|
||||||
|
var offset uint64
|
||||||
|
offset, err = qtls.ReadUvarint(decryptedReader)
|
||||||
|
if err != nil {
|
||||||
|
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
|
||||||
|
}
|
||||||
|
var length uint64
|
||||||
|
length, err = qtls.ReadUvarint(decryptedReader)
|
||||||
|
if err != nil {
|
||||||
|
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
|
||||||
|
}
|
||||||
|
index := len(decrypted) - decryptedReader.Len()
|
||||||
|
fragments = append(fragments, struct {
|
||||||
|
offset uint64
|
||||||
|
length uint64
|
||||||
|
payload []byte
|
||||||
|
}{offset, length, decrypted[index : index+int(length)]})
|
||||||
|
frameLen += length
|
||||||
|
_, err = decryptedReader.Seek(int64(length), io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// ignore unknown frame type
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if frameType != 0x6 {
|
|
||||||
// not crypto frame
|
|
||||||
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, nil
|
|
||||||
}
|
|
||||||
_, err = qtls.ReadUvarint(decryptedReader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = qtls.ReadUvarint(decryptedReader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
tlsHdr := make([]byte, 5)
|
tlsHdr := make([]byte, 5)
|
||||||
tlsHdr[0] = 0x16
|
tlsHdr[0] = 0x16
|
||||||
binary.BigEndian.PutUint16(tlsHdr[1:], uint16(0x0303))
|
binary.BigEndian.PutUint16(tlsHdr[1:], uint16(0x0303))
|
||||||
binary.BigEndian.PutUint16(tlsHdr[3:], uint16(decryptedReader.Len()))
|
binary.BigEndian.PutUint16(tlsHdr[3:], uint16(frameLen))
|
||||||
metadata, err := TLSClientHello(ctx, io.MultiReader(bytes.NewReader(tlsHdr), decryptedReader))
|
var index uint64
|
||||||
|
var length int
|
||||||
|
var readers []io.Reader
|
||||||
|
readers = append(readers, bytes.NewReader(tlsHdr))
|
||||||
|
find:
|
||||||
|
for {
|
||||||
|
for _, fragment := range fragments {
|
||||||
|
if fragment.offset == index {
|
||||||
|
readers = append(readers, bytes.NewReader(fragment.payload))
|
||||||
|
index = fragment.offset + fragment.length
|
||||||
|
length++
|
||||||
|
continue find
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length == len(fragments) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, E.New("bad fragments")
|
||||||
|
}
|
||||||
|
metadata, err := TLSClientHello(ctx, io.MultiReader(readers...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
|
||||||
}
|
}
|
||||||
metadata.Protocol = C.ProtocolQUIC
|
metadata.Protocol = C.ProtocolQUIC
|
||||||
return metadata, nil
|
return metadata, nil
|
||||||
|
|||||||
@@ -19,6 +19,15 @@ func TestSniffQUICv1(t *testing.T) {
|
|||||||
require.Equal(t, metadata.Domain, "cloudflare-quic.com")
|
require.Equal(t, metadata.Domain, "cloudflare-quic.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSniffQUICFragment(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
pkt, err := hex.DecodeString("cc00000001082e3d5d1b64040c55000044d0ccea69e773f6631c1d18b04ae9ee75fcfc34ef74fa62533c93534338a86f101a05d70e0697fb483063fa85db1c59ccfbda5c35234931d8524d8aac37eaaad649470a67794cd754b23c98695238b8363452333bc8c4858376b4166e001da2006e35cf98a91e11a56419b2786775284942d0f7163982f7c248867d12dd374957481dbc564013ff785e1916195eef671f725908f761099d992d69231336ba81d9e25fe2fa3a6eff4318a6ccf10176fc841a1b315f7b35c5b292266fc869d76ca533e7d14e86d82db2e22eacd350977e47d2e012d8a5891c5aaf2a0f4c2b2dae897c161e5b68cbb4dee952472bdc1e21504b8f02534ec4366ce3f8bf86efc78e0232778fbd554457567112abdcafcf6d4d8fcf35083c25d9495679614aba21696e338c62b585046cc55ba8c09c844361d889a47c3ea703b4e23545a9ab2c0bb369693a9ddfb5daffa85cf80fdd6ad66738664e5b0a551729b4955cff7255afcb04dee88c2f072c9de7400947a1bd9327ac5d012a33000ada021d4c03d249fb017d6ac9200b2f9436beab8183ddfbe2d8aee31ffb7df9e1cc181c1af80c39a89965d18ed12da8e3ebe2ae1fbe4b348f83ba19e3e3d1c9b22bcf03ab6ad9b30fe180623faa291ebad83bcd71d7b57f2f5e2f3b8e81d24fb70b2f2159239e8f21ffafef2747aba47d97ab4081e603c018b10678cf99cab1fb42156a14486fa435153979d7279fd22cd40af7088bfc7eff41af2f4b3c0c8864d0040d74dff427f7bffdb8c278474ea00311326cf4925471a8cf596cb92119f19e0f789490ba9cb77b98015a987d93e0324cf1a38b55109f00c3e6ddc5180fb107bf468323afec9bb49fd6a86418569789d66cafe3b8253c2aebb3af3782c1c54dd560487d031d28e6a6e23e159581bb1d47efc4da3fe1d169f9ffb0ca9ba61af0a38a92fde5bc5e6ec026e8378a6315a7b95abf1d2da790a391306ce74d0baf8e2ce648ca74c487f2c0a76a28a80cdf5bd34316eb607684fe7e6d9e83824a00e07660d0b90e3cddd61ebf10748263474afa88c300549e64ce2e90560bb1a12dee7e9484f729a8a4ee7c5651adb5194b3b3ae38e501567c7dbf36e7bb37a2c20b74655f47f2d9af18e52e9d4c9c9eee8e63745779b8f0b06f3a09d846ba62eb978ad77c85de1ee2fee3fbb4c2d283c73e1ccba56a4658e48a2665d200f7f9342f8e84c2ba490094a4f94feec89e42d2f654f564c2beb2997bafa1fc2c68ad8e160b63587d49abc31b834878d52acfb05fb73d0e059b206162e3c90b40c4bc08407ffcb3c08431895b691a3fea923f1f3b48db75d3e6b91fd319ffe4d486e0e14bd5c6affc838dee63d9e0b80f169b5e6c02c7321dcb20deb2b8e707b60e345a308d505bbf26a93d8f18b39d62632e9a77cbe48b3b32eb8819d6311a49820d40f5acbf0273c91c36b2269a03e72ee64df3dfb10ddefe73c64ef60870b2b77bd99dea655f5fe791b538a929a14d99f6d69685d72431ea5f0f4b27a044f2f575ab474fcc3857895934de1ca2581798eaef2c17fe5aaf2e6add97fa32997c7026f15c1b1ad0e6043ae506027a7c0242546fdc851cca39a204e56879f2cef838be8ec66e0f2292f8c862e06f810eb9b80c7a467ce6e90155206352c7f82b1173ba3b98d35bb72c259a60db20dd1a43fe6d7aef0265e6eaa5caafd9b64b448ff745a2046acbdb65cf2a5007809808a4828dc99097feedc734c236260c584")
|
||||||
|
require.NoError(t, err)
|
||||||
|
metadata, err := sniff.QUICClientHello(context.Background(), pkt)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, metadata.Domain, "cloudflare-quic.com")
|
||||||
|
}
|
||||||
|
|
||||||
func FuzzSniffQUIC(f *testing.F) {
|
func FuzzSniffQUIC(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
sniff.QUICClientHello(context.Background(), data)
|
sniff.QUICClientHello(context.Background(), data)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@@ -19,8 +18,11 @@ type (
|
|||||||
PacketSniffer = func(ctx context.Context, packet []byte) (*adapter.InboundContext, error)
|
PacketSniffer = func(ctx context.Context, packet []byte) (*adapter.InboundContext, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, sniffers ...StreamSniffer) (*adapter.InboundContext, error) {
|
func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, timeout time.Duration, sniffers ...StreamSniffer) (*adapter.InboundContext, error) {
|
||||||
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
if timeout == 0 {
|
||||||
|
timeout = C.ReadPayloadTimeout
|
||||||
|
}
|
||||||
|
err := conn.SetReadDeadline(time.Now().Add(timeout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -30,23 +32,25 @@ func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, sniffers
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var metadata *adapter.InboundContext
|
var metadata *adapter.InboundContext
|
||||||
|
var errors []error
|
||||||
for _, sniffer := range sniffers {
|
for _, sniffer := range sniffers {
|
||||||
metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes()))
|
metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes()))
|
||||||
if err != nil {
|
if metadata != nil {
|
||||||
continue
|
return metadata, nil
|
||||||
}
|
}
|
||||||
return metadata, nil
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
return nil, os.ErrInvalid
|
return nil, E.Errors(errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PeekPacket(ctx context.Context, packet []byte, sniffers ...PacketSniffer) (*adapter.InboundContext, error) {
|
func PeekPacket(ctx context.Context, packet []byte, sniffers ...PacketSniffer) (*adapter.InboundContext, error) {
|
||||||
|
var errors []error
|
||||||
for _, sniffer := range sniffers {
|
for _, sniffer := range sniffers {
|
||||||
sniffMetadata, err := sniffer(ctx, packet)
|
metadata, err := sniffer(ctx, packet)
|
||||||
if err != nil {
|
if metadata != nil {
|
||||||
continue
|
return metadata, nil
|
||||||
}
|
}
|
||||||
return sniffMetadata, nil
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
return nil, os.ErrInvalid
|
return nil, E.Errors(errors...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/certmagic"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/mholt/acmez/acme"
|
"github.com/mholt/acmez/acme"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/badtls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -35,7 +37,17 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e
|
|||||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
err := tlsConn.HandshakeContext(ctx)
|
err := tlsConn.HandshakeContext(ctx)
|
||||||
return tlsConn, err
|
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 {
|
type Dialer struct {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type echClientConfig struct {
|
type echClientConfig struct {
|
||||||
@@ -166,19 +165,17 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
|
|||||||
return &echClientConfig{&tlsConfig}, nil
|
return &echClientConfig{&tlsConfig}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeHTTPS = 65
|
|
||||||
|
|
||||||
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||||
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||||
message := &dnsmessage.Message{
|
message := &mDNS.Msg{
|
||||||
Header: dnsmessage.Header{
|
MsgHdr: mDNS.MsgHdr{
|
||||||
RecursionDesired: true,
|
RecursionDesired: true,
|
||||||
},
|
},
|
||||||
Questions: []dnsmessage.Question{
|
Question: []mDNS.Question{
|
||||||
{
|
{
|
||||||
Name: dnsmessage.MustNewName(serverName + "."),
|
Name: serverName + ".",
|
||||||
Type: typeHTTPS,
|
Qtype: mDNS.TypeHTTPS,
|
||||||
Class: dnsmessage.ClassINET,
|
Qclass: mDNS.ClassINET,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -186,19 +183,10 @@ func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serve
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if response.RCode != dnsmessage.RCodeSuccess {
|
if response.Rcode != mDNS.RcodeSuccess {
|
||||||
return nil, dns.RCodeError(response.RCode)
|
return nil, dns.RCodeError(response.Rcode)
|
||||||
}
|
}
|
||||||
content, err := response.Pack()
|
for _, rr := range response.Answer {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var mMsg mDNS.Msg
|
|
||||||
err = mMsg.Unpack(content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, rr := range mMsg.Answer {
|
|
||||||
switch resource := rr.(type) {
|
switch resource := rr.(type) {
|
||||||
case *mDNS.HTTPS:
|
case *mDNS.HTTPS:
|
||||||
for _, value := range resource.Value {
|
for _, value := range resource.Value {
|
||||||
|
|||||||
50
common/tls/mkcert.go
Normal file
50
common/tls/mkcert.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -2,7 +2,11 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"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/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
)
|
)
|
||||||
@@ -10,3 +14,21 @@ import (
|
|||||||
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
return newSTDServer(ctx, logger, options)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
|||||||
c.config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errInsecureUnused = E.New("tls: insecure unused")
|
||||||
|
|
||||||
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -43,9 +45,13 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
var err error
|
var err error
|
||||||
if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
||||||
tlsConfig, acmeService, err = startACME(ctx, common.PtrValueOrDefault(options.ACME))
|
tlsConfig, acmeService, err = startACME(ctx, common.PtrValueOrDefault(options.ACME))
|
||||||
|
//nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if options.Insecure {
|
||||||
|
return nil, errInsecureUnused
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tlsConfig = &tls.Config{}
|
tlsConfig = &tls.Config{}
|
||||||
}
|
}
|
||||||
@@ -102,17 +108,23 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
}
|
}
|
||||||
key = content
|
key = content
|
||||||
}
|
}
|
||||||
if certificate == nil {
|
if certificate == nil && key == nil && options.Insecure {
|
||||||
return nil, E.New("missing certificate")
|
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
return GenerateKeyPair(info.ServerName)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if certificate == nil {
|
||||||
|
return nil, E.New("missing certificate")
|
||||||
|
} else if key == nil {
|
||||||
|
return nil, E.New("missing key")
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPair, err := tls.X509KeyPair(certificate, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "parse x509 key pair")
|
||||||
|
}
|
||||||
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
}
|
}
|
||||||
if key == nil {
|
|
||||||
return nil, E.New("missing key")
|
|
||||||
}
|
|
||||||
keyPair, err := tls.X509KeyPair(certificate, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "parse x509 key pair")
|
|
||||||
}
|
|
||||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
|
||||||
}
|
}
|
||||||
return &STDServerConfig{
|
return &STDServerConfig{
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ type utlsConnWrapper struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error {
|
func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error {
|
||||||
return c.Conn.Handshake()
|
return c.UConn.Handshake()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
|
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
|
||||||
@@ -146,6 +146,8 @@ func newUTLSClient(router adapter.Router, serverAddress string, options option.O
|
|||||||
id = utls.HelloAndroid_11_OkHttp
|
id = utls.HelloAndroid_11_OkHttp
|
||||||
case "random":
|
case "random":
|
||||||
id = utls.HelloRandomized
|
id = utls.HelloRandomized
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown uTLS fingerprint: ", options.UTLS.Fingerprint)
|
||||||
}
|
}
|
||||||
return &utlsClientConfig{&tlsConfig, id}, nil
|
return &utlsClientConfig{&tlsConfig, id}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,3 +3,5 @@ package constant
|
|||||||
import E "github.com/sagernet/sing/common/exceptions"
|
import E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
var ErrTLSRequired = E.New("TLS required")
|
var ErrTLSRequired = E.New("TLS required")
|
||||||
|
|
||||||
|
var ErrQUICNotIncluded = E.New(`QUIC is not included in this build, rebuild with -tags with_quic`)
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeTun = "tun"
|
TypeTun = "tun"
|
||||||
TypeRedirect = "redirect"
|
TypeRedirect = "redirect"
|
||||||
TypeTProxy = "tproxy"
|
TypeTProxy = "tproxy"
|
||||||
TypeDirect = "direct"
|
TypeDirect = "direct"
|
||||||
TypeBlock = "block"
|
TypeBlock = "block"
|
||||||
TypeDNS = "dns"
|
TypeDNS = "dns"
|
||||||
TypeSocks = "socks"
|
TypeSocks = "socks"
|
||||||
TypeHTTP = "http"
|
TypeHTTP = "http"
|
||||||
TypeMixed = "mixed"
|
TypeMixed = "mixed"
|
||||||
TypeShadowsocks = "shadowsocks"
|
TypeShadowsocks = "shadowsocks"
|
||||||
TypeVMess = "vmess"
|
TypeVMess = "vmess"
|
||||||
TypeTrojan = "trojan"
|
TypeTrojan = "trojan"
|
||||||
TypeNaive = "naive"
|
TypeNaive = "naive"
|
||||||
TypeWireGuard = "wireguard"
|
TypeWireGuard = "wireguard"
|
||||||
TypeHysteria = "hysteria"
|
TypeHysteria = "hysteria"
|
||||||
TypeTor = "tor"
|
TypeTor = "tor"
|
||||||
TypeSSH = "ssh"
|
TypeSSH = "ssh"
|
||||||
TypeShadowTLS = "shadowtls"
|
TypeShadowTLS = "shadowtls"
|
||||||
|
TypeShadowsocksR = "shadowsocksr"
|
||||||
|
TypeVLESS = "vless"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeSelector = "selector"
|
TypeSelector = "selector"
|
||||||
|
TypeURLTest = "urltest"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
//go:build with_quic
|
|
||||||
|
|
||||||
package constant
|
|
||||||
|
|
||||||
const QUIC_AVAILABLE = true
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
//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`)
|
|
||||||
@@ -3,10 +3,11 @@ package constant
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TCPTimeout = 5 * time.Second
|
TCPTimeout = 5 * time.Second
|
||||||
ReadPayloadTimeout = 300 * time.Millisecond
|
ReadPayloadTimeout = 300 * time.Millisecond
|
||||||
DNSTimeout = 10 * time.Second
|
DNSTimeout = 10 * time.Second
|
||||||
QUICTimeout = 30 * time.Second
|
QUICTimeout = 30 * time.Second
|
||||||
STUNTimeout = 15 * time.Second
|
STUNTimeout = 15 * time.Second
|
||||||
UDPTimeout = 5 * time.Minute
|
UDPTimeout = 5 * time.Minute
|
||||||
|
DefaultURLTestInterval = 1 * time.Minute
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
var (
|
var Version = "1.1-beta10"
|
||||||
Version = "1.1-beta3"
|
|
||||||
Commit = ""
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,3 +1,119 @@
|
|||||||
|
#### 1.1-beta10
|
||||||
|
|
||||||
|
* Add [sniff_timeout](/configuration/shared/listen#sniff_timeout) listen option
|
||||||
|
* Add [custom route](/configuration/inbound/tun#inet4_route_address) support for tun **1**
|
||||||
|
* Fix interface monitor
|
||||||
|
* Fix websocket headroom
|
||||||
|
* Fix uTLS handshake
|
||||||
|
* Fix ssh outbound
|
||||||
|
* Fix sniff fragmented quic client hello
|
||||||
|
* Fix DF for hysteria
|
||||||
|
* Fix naive overflow
|
||||||
|
* Check destination before udp connect
|
||||||
|
* Update uTLS to v1.1.5
|
||||||
|
* Update tfo-go to v2.0.2
|
||||||
|
* Update fsnotify to v1.6.0
|
||||||
|
* Update grpc to v1.50.1
|
||||||
|
|
||||||
|
*1*:
|
||||||
|
|
||||||
|
The `strict_route` on windows is removed.
|
||||||
|
|
||||||
|
#### 1.0.6
|
||||||
|
|
||||||
|
* Fix ssh outbound
|
||||||
|
* Fix sniff fragmented quic client hello
|
||||||
|
* Fix naive overflow
|
||||||
|
* Check destination before udp connect
|
||||||
|
|
||||||
|
#### 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
|
#### 1.1-beta3
|
||||||
|
|
||||||
* Fix using custom TLS client in http2 client
|
* Fix using custom TLS client in http2 client
|
||||||
|
|||||||
@@ -104,8 +104,10 @@
|
|||||||
|
|
||||||
The default rule uses the following matching logic:
|
The default rule uses the following matching logic:
|
||||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
||||||
|
(`port` || `port_range`) &&
|
||||||
(`source_geoip` || `source_ip_cidr`) &&
|
(`source_geoip` || `source_ip_cidr`) &&
|
||||||
`other fields`
|
(`source_port` || `source_port_range`) &&
|
||||||
|
`other fields`
|
||||||
|
|
||||||
#### inbound
|
#### inbound
|
||||||
|
|
||||||
|
|||||||
@@ -103,8 +103,10 @@
|
|||||||
|
|
||||||
默认规则使用以下匹配逻辑:
|
默认规则使用以下匹配逻辑:
|
||||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
||||||
|
(`port` || `port_range`) &&
|
||||||
(`source_geoip` || `source_ip_cidr`) &&
|
(`source_geoip` || `source_ip_cidr`) &&
|
||||||
`other fields`
|
(`source_port` || `source_port_range`) &&
|
||||||
|
`other fields`
|
||||||
|
|
||||||
#### inbound
|
#### inbound
|
||||||
|
|
||||||
|
|||||||
@@ -9,24 +9,39 @@
|
|||||||
"external_controller": "127.0.0.1:9090",
|
"external_controller": "127.0.0.1:9090",
|
||||||
"external_ui": "folder",
|
"external_ui": "folder",
|
||||||
"secret": "",
|
"secret": "",
|
||||||
|
"direct_io": false,
|
||||||
"default_mode": "rule",
|
"default_mode": "rule",
|
||||||
"store_selected": false,
|
"store_selected": false,
|
||||||
"cache_file": "cache.db"
|
"cache_file": "cache.db"
|
||||||
|
},
|
||||||
|
"v2ray_api": {
|
||||||
|
"listen": "127.0.0.1:8080",
|
||||||
|
"stats": {
|
||||||
|
"enabled": true,
|
||||||
|
"direct_io": false,
|
||||||
|
"inbounds": [
|
||||||
|
"socks-in"
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
"proxy",
|
||||||
|
"direct"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
Traffic statistics and connection management can degrade performance.
|
||||||
|
|
||||||
### Clash API Fields
|
### Clash API Fields
|
||||||
|
|
||||||
!!! error ""
|
!!! error ""
|
||||||
|
|
||||||
Clash API is not included by default, see [Installation](/#installation).
|
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
|
#### external_controller
|
||||||
|
|
||||||
RESTful web API listening address. Clash API will be disabled if empty.
|
RESTful web API listening address. Clash API will be disabled if empty.
|
||||||
@@ -43,6 +58,10 @@ Secret for the RESTful API (optional)
|
|||||||
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
|
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
|
||||||
ALWAYS set a secret if RESTful API is listening on 0.0.0.0
|
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
|
||||||
|
|
||||||
Default mode in clash, `rule` will be used if empty.
|
Default mode in clash, `rule` will be used if empty.
|
||||||
@@ -59,4 +78,34 @@ Store selected outbound for the `Selector` outbound in cache file.
|
|||||||
|
|
||||||
#### cache_file
|
#### cache_file
|
||||||
|
|
||||||
Cache file path, `cache.db` will be used if empty.
|
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.
|
||||||
|
|||||||
@@ -9,24 +9,39 @@
|
|||||||
"external_controller": "127.0.0.1:9090",
|
"external_controller": "127.0.0.1:9090",
|
||||||
"external_ui": "folder",
|
"external_ui": "folder",
|
||||||
"secret": "",
|
"secret": "",
|
||||||
|
"direct_io": false,
|
||||||
"default_mode": "rule",
|
"default_mode": "rule",
|
||||||
"store_selected": false,
|
"store_selected": false,
|
||||||
"cache_file": "cache.db"
|
"cache_file": "cache.db"
|
||||||
|
},
|
||||||
|
"v2ray_api": {
|
||||||
|
"listen": "127.0.0.1:8080",
|
||||||
|
"stats": {
|
||||||
|
"enabled": true,
|
||||||
|
"direct_io": false,
|
||||||
|
"inbounds": [
|
||||||
|
"socks-in"
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
"proxy",
|
||||||
|
"direct"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
流量统计和连接管理会降低性能。
|
||||||
|
|
||||||
### Clash API 字段
|
### Clash API 字段
|
||||||
|
|
||||||
!!! error ""
|
!!! error ""
|
||||||
|
|
||||||
默认安装不包含 Clash API,参阅 [安装](/zh/#_2)。
|
默认安装不包含 Clash API,参阅 [安装](/zh/#_2)。
|
||||||
|
|
||||||
!!! note ""
|
|
||||||
|
|
||||||
流量统计和连接管理将禁用 Linux 中的 TCP splice 并降低性能,使用风险自负。
|
|
||||||
|
|
||||||
#### external_controller
|
#### external_controller
|
||||||
|
|
||||||
RESTful web API 监听地址。如果为空,则禁用 Clash API。
|
RESTful web API 监听地址。如果为空,则禁用 Clash API。
|
||||||
@@ -41,6 +56,10 @@ RESTful API 的密钥(可选)
|
|||||||
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
|
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
|
||||||
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
|
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
|
||||||
|
|
||||||
|
#### direct_io
|
||||||
|
|
||||||
|
允许像 splice 这样的没有实时流量报告的无损中继。
|
||||||
|
|
||||||
#### default_mode
|
#### default_mode
|
||||||
|
|
||||||
Clash 中的默认模式,默认使用 `rule`。
|
Clash 中的默认模式,默认使用 `rule`。
|
||||||
@@ -57,4 +76,34 @@ Clash 中的默认模式,默认使用 `rule`。
|
|||||||
|
|
||||||
#### cache_file
|
#### cache_file
|
||||||
|
|
||||||
缓存文件路径,默认使用`cache.db`。
|
缓存文件路径,默认使用`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
|
||||||
|
|
||||||
|
统计流量的出站列表。
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
... // Listen Fields
|
... // Listen Fields
|
||||||
|
|
||||||
|
"version": 2,
|
||||||
|
"password": "fuck me till the daylight",
|
||||||
"handshake": {
|
"handshake": {
|
||||||
"server": "google.com",
|
"server": "google.com",
|
||||||
"server_port": 443,
|
"server_port": 443,
|
||||||
@@ -20,12 +22,25 @@
|
|||||||
|
|
||||||
See [Listen Fields](/configuration/shared/listen) for details.
|
See [Listen Fields](/configuration/shared/listen) for details.
|
||||||
|
|
||||||
|
|
||||||
### Fields
|
### 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
|
#### handshake
|
||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
Handshake server address and [dial options](/configuration/shared/dial).
|
Handshake server address and [Dial options](/configuration/shared/dial).
|
||||||
|
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
... // 监听字段
|
... // 监听字段
|
||||||
|
|
||||||
|
"version": 2,
|
||||||
|
"password": "fuck me till the daylight",
|
||||||
"handshake": {
|
"handshake": {
|
||||||
"server": "google.com",
|
"server": "google.com",
|
||||||
"server_port": 443,
|
"server_port": 443,
|
||||||
@@ -22,6 +24,21 @@
|
|||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
|
|
||||||
|
#### 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
|
#### handshake
|
||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|||||||
@@ -8,15 +8,22 @@
|
|||||||
{
|
{
|
||||||
"type": "tun",
|
"type": "tun",
|
||||||
"tag": "tun-in",
|
"tag": "tun-in",
|
||||||
|
|
||||||
"interface_name": "tun0",
|
"interface_name": "tun0",
|
||||||
"inet4_address": "172.19.0.1/30",
|
"inet4_address": "172.19.0.1/30",
|
||||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||||
"mtu": 9000,
|
"mtu": 9000,
|
||||||
"auto_route": true,
|
"auto_route": true,
|
||||||
"strict_route": true,
|
"strict_route": true,
|
||||||
|
"inet4_route_address": [
|
||||||
|
"0.0.0.0/1",
|
||||||
|
"128.0.0.0/1"
|
||||||
|
],
|
||||||
|
"inet6_route_address": [
|
||||||
|
"::/1",
|
||||||
|
"8000::/1"
|
||||||
|
],
|
||||||
"endpoint_independent_nat": false,
|
"endpoint_independent_nat": false,
|
||||||
"stack": "gvisor",
|
"stack": "system",
|
||||||
"include_uid": [
|
"include_uid": [
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
@@ -39,8 +46,8 @@
|
|||||||
"exclude_package": [
|
"exclude_package": [
|
||||||
"com.android.captiveportallogin"
|
"com.android.captiveportallogin"
|
||||||
],
|
],
|
||||||
|
...
|
||||||
... // Listen Fields
|
// Listen Fields
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -86,7 +93,9 @@ Set the default route to the Tun.
|
|||||||
|
|
||||||
#### strict_route
|
#### strict_route
|
||||||
|
|
||||||
Enforce strict routing rules in Linux when `auto_route` is enabled:
|
*In Linux*:
|
||||||
|
|
||||||
|
Enforce strict routing rules when `auto_route` is enabled:
|
||||||
|
|
||||||
* Let unsupported network unreachable
|
* Let unsupported network unreachable
|
||||||
* Route all connections to tun
|
* Route all connections to tun
|
||||||
@@ -94,6 +103,14 @@ Enforce strict routing rules in Linux 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
|
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.
|
not be accessible by others.
|
||||||
|
|
||||||
|
#### inet4_route_address
|
||||||
|
|
||||||
|
Use custom routes instead of default when `auto_route` is enabled.
|
||||||
|
|
||||||
|
#### inet6_route_address
|
||||||
|
|
||||||
|
Use custom routes instead of default when `auto_route` is enabled.
|
||||||
|
|
||||||
#### endpoint_independent_nat
|
#### endpoint_independent_nat
|
||||||
|
|
||||||
!!! info ""
|
!!! info ""
|
||||||
@@ -112,15 +129,15 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
|
|||||||
|
|
||||||
TCP/IP stack.
|
TCP/IP stack.
|
||||||
|
|
||||||
| Stack | Description | Status |
|
| Stack | Description | Status |
|
||||||
|------------------|--------------------------------------------------------------------------------|-------------------|
|
|------------------|----------------------------------------------------------------------------------|-------------------|
|
||||||
| gVisor (default) | Based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
| system (default) | Sometimes better performance | recommended |
|
||||||
| system | Less compatibility and 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 |
|
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||||
|
|
||||||
!!! warning ""
|
!!! warning ""
|
||||||
|
|
||||||
The LWIP stack is not included by default, see [Installation](/#installation).
|
gVisor and LWIP stacks is not included by default, see [Installation](/#installation).
|
||||||
|
|
||||||
#### include_uid
|
#### include_uid
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,16 @@
|
|||||||
"mtu": 9000,
|
"mtu": 9000,
|
||||||
"auto_route": true,
|
"auto_route": true,
|
||||||
"strict_route": true,
|
"strict_route": true,
|
||||||
|
"inet4_route_address": [
|
||||||
|
"0.0.0.0/1",
|
||||||
|
"128.0.0.0/1"
|
||||||
|
],
|
||||||
|
"inet6_route_address": [
|
||||||
|
"::/1",
|
||||||
|
"8000::/1"
|
||||||
|
],
|
||||||
"endpoint_independent_nat": false,
|
"endpoint_independent_nat": false,
|
||||||
"stack": "gvisor",
|
"stack": "system",
|
||||||
"include_uid": [
|
"include_uid": [
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
@@ -86,13 +94,23 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
#### strict_route
|
#### strict_route
|
||||||
|
|
||||||
在 Linux 中启用 `auto_route` 时执行严格的路由规则。
|
*在 Linux 中*:
|
||||||
|
|
||||||
|
启用 `auto_route` 时执行严格的路由规则。
|
||||||
|
|
||||||
* 让不支持的网络无法到达
|
* 让不支持的网络无法到达
|
||||||
* 将所有连接路由到 tun
|
* 将所有连接路由到 tun
|
||||||
|
|
||||||
它可以防止地址泄漏,并使 DNS 劫持在 Android 和使用 systemd-resolved 的 Linux 上工作,但你的设备将无法其他设备被访问。
|
它可以防止地址泄漏,并使 DNS 劫持在 Android 和使用 systemd-resolved 的 Linux 上工作,但你的设备将无法其他设备被访问。
|
||||||
|
|
||||||
|
#### inet4_route_address
|
||||||
|
|
||||||
|
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
||||||
|
|
||||||
|
#### inet6_route_address
|
||||||
|
|
||||||
|
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
||||||
|
|
||||||
#### endpoint_independent_nat
|
#### endpoint_independent_nat
|
||||||
|
|
||||||
启用独立于端点的 NAT。
|
启用独立于端点的 NAT。
|
||||||
@@ -107,15 +125,15 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
|
|||||||
|
|
||||||
TCP/IP 栈。
|
TCP/IP 栈。
|
||||||
|
|
||||||
| 栈 | 描述 | 状态 |
|
| 栈 | 描述 | 状态 |
|
||||||
|------------------|--------------------------------------------------------------------------|-------|
|
|-------------|--------------------------------------------------------------------------|-------|
|
||||||
| gVisor (default) | 基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
| system (默认) | 有时性能更好 | 推荐 |
|
||||||
| system | 兼容性较差,有时性能更好。 | 推荐 |
|
| gVisor | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
||||||
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
||||||
|
|
||||||
!!! warning ""
|
!!! warning ""
|
||||||
|
|
||||||
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
|
默认安装不包含 gVisor 和 LWIP 栈,请参阅 [安装](/zh/#_2)。
|
||||||
|
|
||||||
#### include_uid
|
#### include_uid
|
||||||
|
|
||||||
@@ -145,10 +163,10 @@ TCP/IP 栈。
|
|||||||
|
|
||||||
限制被路由的 Android 用户。
|
限制被路由的 Android 用户。
|
||||||
|
|
||||||
| 常用用户 | ID |
|
| 常用用户 | ID |
|
||||||
|--|-----|
|
|--|-----|
|
||||||
| 您 | 0 |
|
| 您 | 0 |
|
||||||
| 工作资料 | 10 |
|
| 工作资料 | 10 |
|
||||||
|
|
||||||
#### include_package
|
#### include_package
|
||||||
|
|
||||||
@@ -160,4 +178,4 @@ TCP/IP 栈。
|
|||||||
|
|
||||||
### 监听字段
|
### 监听字段
|
||||||
|
|
||||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||||
|
|||||||
@@ -15,21 +15,24 @@
|
|||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
|
|
||||||
| Type | Format |
|
| Type | Format |
|
||||||
|---------------|------------------------------|
|
|----------------|--------------------------------|
|
||||||
| `direct` | [Direct](./direct) |
|
| `direct` | [Direct](./direct) |
|
||||||
| `block` | [Block](./block) |
|
| `block` | [Block](./block) |
|
||||||
| `socks` | [SOCKS](./socks) |
|
| `socks` | [SOCKS](./socks) |
|
||||||
| `http` | [HTTP](./http) |
|
| `http` | [HTTP](./http) |
|
||||||
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
||||||
| `vmess` | [VMess](./vmess) |
|
| `vmess` | [VMess](./vmess) |
|
||||||
| `trojan` | [Trojan](./trojan) |
|
| `trojan` | [Trojan](./trojan) |
|
||||||
| `wireguard` | [Wireguard](./wireguard) |
|
| `wireguard` | [Wireguard](./wireguard) |
|
||||||
| `hysteria` | [Hysteria](./hysteria) |
|
| `hysteria` | [Hysteria](./hysteria) |
|
||||||
| `tor` | [Tor](./tor) |
|
| `shadowsocksr` | [ShadowsocksR](./shadowsocksr) |
|
||||||
| `ssh` | [SSH](./ssh) |
|
| `vless` | [VLESS](./vless) |
|
||||||
| `dns` | [DNS](./dns) |
|
| `tor` | [Tor](./tor) |
|
||||||
| `selector` | [Selector](./selector) |
|
| `ssh` | [SSH](./ssh) |
|
||||||
|
| `dns` | [DNS](./dns) |
|
||||||
|
| `selector` | [Selector](./selector) |
|
||||||
|
| `urltest` | [URLTest](./urltest) |
|
||||||
|
|
||||||
#### tag
|
#### tag
|
||||||
|
|
||||||
|
|||||||
@@ -15,21 +15,24 @@
|
|||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
|
|
||||||
| 类型 | 格式 |
|
| 类型 | 格式 |
|
||||||
|---------------|------------------------------|
|
|----------------|--------------------------------|
|
||||||
| `direct` | [Direct](./direct) |
|
| `direct` | [Direct](./direct) |
|
||||||
| `block` | [Block](./block) |
|
| `block` | [Block](./block) |
|
||||||
| `socks` | [SOCKS](./socks) |
|
| `socks` | [SOCKS](./socks) |
|
||||||
| `http` | [HTTP](./http) |
|
| `http` | [HTTP](./http) |
|
||||||
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
||||||
| `vmess` | [VMess](./vmess) |
|
| `vmess` | [VMess](./vmess) |
|
||||||
| `trojan` | [Trojan](./trojan) |
|
| `trojan` | [Trojan](./trojan) |
|
||||||
| `wireguard` | [Wireguard](./wireguard) |
|
| `wireguard` | [Wireguard](./wireguard) |
|
||||||
| `hysteria` | [Hysteria](./hysteria) |
|
| `hysteria` | [Hysteria](./hysteria) |
|
||||||
| `tor` | [Tor](./tor) |
|
| `shadowsocksr` | [ShadowsocksR](./shadowsocksr) |
|
||||||
| `ssh` | [SSH](./ssh) |
|
| `vless` | [VLESS](./vless) |
|
||||||
| `dns` | [DNS](./dns) |
|
| `tor` | [Tor](./tor) |
|
||||||
| `selector` | [Selector](./selector) |
|
| `ssh` | [SSH](./ssh) |
|
||||||
|
| `dns` | [DNS](./dns) |
|
||||||
|
| `selector` | [Selector](./selector) |
|
||||||
|
| `urltest` | [URLTest](./urltest) |
|
||||||
|
|
||||||
#### tag
|
#### tag
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
"method": "2022-blake3-aes-128-gcm",
|
"method": "2022-blake3-aes-128-gcm",
|
||||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
|
"plugin": "",
|
||||||
|
"plugin_opts": "",
|
||||||
"network": "udp",
|
"network": "udp",
|
||||||
"udp_over_tcp": false,
|
"udp_over_tcp": false,
|
||||||
"multiplex": {},
|
"multiplex": {},
|
||||||
@@ -65,6 +67,16 @@ Legacy encryption methods:
|
|||||||
|
|
||||||
The shadowsocks password.
|
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
|
#### network
|
||||||
|
|
||||||
Enabled network
|
Enabled network
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
"method": "2022-blake3-aes-128-gcm",
|
"method": "2022-blake3-aes-128-gcm",
|
||||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
|
"plugin": "",
|
||||||
|
"plugin_opts": "",
|
||||||
"network": "udp",
|
"network": "udp",
|
||||||
"udp_over_tcp": false,
|
"udp_over_tcp": false,
|
||||||
"multiplex": {},
|
"multiplex": {},
|
||||||
@@ -65,6 +67,16 @@
|
|||||||
|
|
||||||
Shadowsocks 密码。
|
Shadowsocks 密码。
|
||||||
|
|
||||||
|
#### plugin
|
||||||
|
|
||||||
|
Shadowsocks SIP003 插件,由内部实现。
|
||||||
|
|
||||||
|
仅支持 `obfs-local` 和 `v2ray-plugin`。
|
||||||
|
|
||||||
|
#### plugin_opts
|
||||||
|
|
||||||
|
Shadowsocks SIP003 插件参数。
|
||||||
|
|
||||||
#### network
|
#### network
|
||||||
|
|
||||||
启用的网络协议
|
启用的网络协议
|
||||||
|
|||||||
106
docs/configuration/outbound/shadowsocksr.md
Normal file
106
docs/configuration/outbound/shadowsocksr.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
### 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.
|
||||||
106
docs/configuration/outbound/shadowsocksr.zh.md
Normal file
106
docs/configuration/outbound/shadowsocksr.zh.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
### 结构
|
||||||
|
|
||||||
|
```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/)。
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
|
"version": 2,
|
||||||
|
"password": "fuck me till the daylight",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
... // Dial Fields
|
... // Dial Fields
|
||||||
@@ -27,6 +29,21 @@ The server address.
|
|||||||
|
|
||||||
The server port.
|
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
|
#### tls
|
||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
|
"version": 2,
|
||||||
|
"password": "fuck me till the daylight",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
... // 拨号字段
|
... // 拨号字段
|
||||||
@@ -27,6 +29,21 @@
|
|||||||
|
|
||||||
服务器端口。
|
服务器端口。
|
||||||
|
|
||||||
|
#### 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
|
#### tls
|
||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|||||||
37
docs/configuration/outbound/urltest.md
Normal file
37
docs/configuration/outbound/urltest.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
### 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.
|
||||||
37
docs/configuration/outbound/urltest.zh.md
Normal file
37
docs/configuration/outbound/urltest.zh.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
### 结构
|
||||||
|
|
||||||
|
```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`。
|
||||||
70
docs/configuration/outbound/vless.md
Normal file
70
docs/configuration/outbound/vless.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
### 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.
|
||||||
70
docs/configuration/outbound/vless.zh.md
Normal file
70
docs/configuration/outbound/vless.zh.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
### 结构
|
||||||
|
|
||||||
|
```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/)。
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"authenticated_length": true,
|
"authenticated_length": true,
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
"packet_addr": false,
|
"packet_encoding": "",
|
||||||
"multiplex": {},
|
"multiplex": {},
|
||||||
"transport": {},
|
"transport": {},
|
||||||
|
|
||||||
@@ -84,9 +84,13 @@ Both is enabled by default.
|
|||||||
|
|
||||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||||
|
|
||||||
#### packet_addr
|
#### packet_encoding
|
||||||
|
|
||||||
Enable packetaddr support.
|
| Encoding | Description |
|
||||||
|
|------------|-----------------------|
|
||||||
|
| (none) | Disabled |
|
||||||
|
| packetaddr | Supported by v2ray 5+ |
|
||||||
|
| xudp | Supported by xray |
|
||||||
|
|
||||||
#### multiplex
|
#### multiplex
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"authenticated_length": true,
|
"authenticated_length": true,
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
"packet_addr": false,
|
"packet_encoding": "",
|
||||||
"multiplex": {},
|
"multiplex": {},
|
||||||
"transport": {},
|
"transport": {},
|
||||||
|
|
||||||
@@ -84,9 +84,13 @@ VMess 用户 ID。
|
|||||||
|
|
||||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||||
|
|
||||||
#### packet_addr
|
#### packet_encoding
|
||||||
|
|
||||||
启用 packetaddr 支持。
|
| 编码 | 描述 |
|
||||||
|
|------------|---------------|
|
||||||
|
| (空) | 禁用 |
|
||||||
|
| packetaddr | 由 v2ray 5+ 支持 |
|
||||||
|
| xudp | 由 xray 支持 |
|
||||||
|
|
||||||
#### multiplex
|
#### multiplex
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
|
|
||||||
WireGuard is not included by default, see [Installation](/#installation).
|
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
|
### Fields
|
||||||
|
|
||||||
#### server
|
#### server
|
||||||
@@ -44,7 +48,9 @@ The server port.
|
|||||||
|
|
||||||
Use system tun support.
|
Use system tun support.
|
||||||
|
|
||||||
Requires privileges and cannot conflict with system interfaces.
|
Requires privilege and cannot conflict with system interfaces.
|
||||||
|
|
||||||
|
Forced if gVisor not included in the build.
|
||||||
|
|
||||||
#### interface_name
|
#### interface_name
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
|
|
||||||
默认安装不包含 WireGuard, 参阅 [安装](/zh/#_2)。
|
默认安装不包含 WireGuard, 参阅 [安装](/zh/#_2)。
|
||||||
|
|
||||||
|
!!! warning ""
|
||||||
|
|
||||||
|
默认安装不包含被非特权 WireGuard 需要的 gVisor, 参阅 [安装](/zh/#_2)。
|
||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
|
|
||||||
#### server
|
#### server
|
||||||
@@ -46,6 +50,8 @@
|
|||||||
|
|
||||||
需要特权且不能与系统接口冲突。
|
需要特权且不能与系统接口冲突。
|
||||||
|
|
||||||
|
如果 gVisor 未包含在构建中,则强制执行。
|
||||||
|
|
||||||
#### interface_name
|
#### interface_name
|
||||||
|
|
||||||
启用 `system_interface` 时的自定义设备名称。
|
启用 `system_interface` 时的自定义设备名称。
|
||||||
|
|||||||
@@ -107,8 +107,10 @@
|
|||||||
|
|
||||||
The default rule uses the following matching logic:
|
The default rule uses the following matching logic:
|
||||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||||
|
(`port` || `port_range`) &&
|
||||||
(`source_geoip` || `source_ip_cidr`) &&
|
(`source_geoip` || `source_ip_cidr`) &&
|
||||||
`other fields`
|
(`source_port` || `source_port_range`) &&
|
||||||
|
`other fields`
|
||||||
|
|
||||||
#### inbound
|
#### inbound
|
||||||
|
|
||||||
|
|||||||
@@ -105,8 +105,10 @@
|
|||||||
|
|
||||||
默认规则使用以下匹配逻辑:
|
默认规则使用以下匹配逻辑:
|
||||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||||
|
(`port` || `port_range`) &&
|
||||||
(`source_geoip` || `source_ip_cidr`) &&
|
(`source_geoip` || `source_ip_cidr`) &&
|
||||||
`other fields`
|
(`source_port` || `source_port_range`) &&
|
||||||
|
`other fields`
|
||||||
|
|
||||||
#### inbound
|
#### inbound
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
"sniff": false,
|
"sniff": false,
|
||||||
"sniff_override_destination": false,
|
"sniff_override_destination": false,
|
||||||
|
"sniff_timeout": "300ms",
|
||||||
"domain_strategy": "prefer_ipv6",
|
"domain_strategy": "prefer_ipv6",
|
||||||
"udp_timeout": 300,
|
"udp_timeout": 300,
|
||||||
"proxy_protocol": false,
|
"proxy_protocol": false,
|
||||||
@@ -57,6 +58,12 @@ Override the connection destination address with the sniffed domain.
|
|||||||
|
|
||||||
If the domain name is invalid (like tor), this will not work.
|
If the domain name is invalid (like tor), this will not work.
|
||||||
|
|
||||||
|
#### sniff_timeout
|
||||||
|
|
||||||
|
Timeout for sniffing.
|
||||||
|
|
||||||
|
300ms is used by default.
|
||||||
|
|
||||||
#### domain_strategy
|
#### domain_strategy
|
||||||
|
|
||||||
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
"sniff": false,
|
"sniff": false,
|
||||||
"sniff_override_destination": false,
|
"sniff_override_destination": false,
|
||||||
|
"sniff_timeout": "300ms",
|
||||||
"domain_strategy": "prefer_ipv6",
|
"domain_strategy": "prefer_ipv6",
|
||||||
"udp_timeout": 300,
|
"udp_timeout": 300,
|
||||||
"proxy_protocol": false,
|
"proxy_protocol": false,
|
||||||
@@ -58,6 +59,12 @@
|
|||||||
|
|
||||||
如果域名无效(如 Tor),将不生效。
|
如果域名无效(如 Tor),将不生效。
|
||||||
|
|
||||||
|
#### sniff_timeout
|
||||||
|
|
||||||
|
探测超时时间。
|
||||||
|
|
||||||
|
默认使用 300ms。
|
||||||
|
|
||||||
#### domain_strategy
|
#### domain_strategy
|
||||||
|
|
||||||
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。
|
可选值: `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。
|
||||||
|
|||||||
@@ -53,17 +53,6 @@ we need to do this.
|
|||||||
|
|
||||||
The library needs to be updated with the upstream.
|
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
|
#### smux
|
||||||
|
|
||||||
Link: [GitHub repository](https://github.com/SagerNet/smux)
|
Link: [GitHub repository](https://github.com/SagerNet/smux)
|
||||||
|
|||||||
@@ -95,7 +95,9 @@
|
|||||||
| cn | 17.8M | 140.3M |
|
| cn | 17.8M | 140.3M |
|
||||||
| cn (Loyalsoldier) | 74.3M | 246.7M |
|
| cn (Loyalsoldier) | 74.3M | 246.7M |
|
||||||
|
|
||||||
#### Shadowsocks benchmark
|
#### Benchmark
|
||||||
|
|
||||||
|
##### Shadowsocks
|
||||||
|
|
||||||
| / | none | aes-128-gcm | 2022-blake3-aes-128-gcm |
|
| / | none | aes-128-gcm | 2022-blake3-aes-128-gcm |
|
||||||
|------------------------------------|:---------:|:-----------:|:-----------------------:|
|
|------------------------------------|:---------:|:-----------:|:-----------------------:|
|
||||||
@@ -103,6 +105,13 @@
|
|||||||
| shadowsocks-rust (v1.15.0-alpha.5) | 10.7 Gbps | / | 9.36 Gbps |
|
| shadowsocks-rust (v1.15.0-alpha.5) | 10.7 Gbps | / | 9.36 Gbps |
|
||||||
| sing-box | 29.0 Gbps | / | 11.8 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
|
||||||
|
|
||||||
| / | License |
|
| / | License |
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ 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_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_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_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_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_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_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_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
|
||||||
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
| `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). |
|
||||||
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
|
| `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). |
|
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||||
|
|
||||||
@@ -44,10 +46,6 @@ sing-box version
|
|||||||
It is also recommended to use systemd to manage sing-box service,
|
It is also recommended to use systemd to manage sing-box service,
|
||||||
see [Linux server installation example](./examples/linux-server-installation).
|
see [Linux server installation example](./examples/linux-server-installation).
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
[](https://github.com/sagernet/sing-box/graphs/contributors)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ 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_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_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
|
||||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||||
|
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
|
||||||
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
||||||
| `with_utls` | 启用 [uTLS](https://github.com/refraction-networking/utls) 支持, 参阅 [TLS](./configuration/shared/tls#utls)。 |
|
| `with_utls` | 启用 uTLS 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||||
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
| `with_clash_api` | 启用 Clash API 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||||
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
| `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_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
||||||
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||||
|
|
||||||
@@ -41,13 +43,9 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
|||||||
sing-box version
|
sing-box version
|
||||||
```
|
```
|
||||||
|
|
||||||
同时推荐使用 Systemd 来管理 sing-box 服务器实例。
|
同时推荐使用 systemd 来管理 sing-box 服务器实例。
|
||||||
参阅 [Linux 服务器安装示例](./examples/linux-server-installation)。
|
参阅 [Linux 服务器安装示例](./examples/linux-server-installation)。
|
||||||
|
|
||||||
## 贡献者
|
|
||||||
|
|
||||||
[](https://github.com/sagernet/sing-box/graphs/contributors)
|
|
||||||
|
|
||||||
## 授权
|
## 授权
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
//go:build with_clash_api
|
|
||||||
|
|
||||||
package experimental
|
package experimental
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
|
type ClashServerConstructor = func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
|
||||||
return clashapi.NewServer(router, logFactory, options)
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/common/json"
|
"github.com/sagernet/sing-box/common/json"
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||||
|
"github.com/sagernet/websocket"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler {
|
func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler {
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ func findProxyByName(router adapter.Router) func(next http.Handler) http.Handler
|
|||||||
func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||||
var info badjson.JSONObject
|
var info badjson.JSONObject
|
||||||
var clashType string
|
var clashType string
|
||||||
var isGroup bool
|
|
||||||
switch detour.Type() {
|
switch detour.Type() {
|
||||||
case C.TypeDirect:
|
case C.TypeDirect:
|
||||||
clashType = "Direct"
|
clashType = "Direct"
|
||||||
@@ -70,18 +69,31 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
|||||||
case C.TypeSocks:
|
case C.TypeSocks:
|
||||||
clashType = "Socks"
|
clashType = "Socks"
|
||||||
case C.TypeHTTP:
|
case C.TypeHTTP:
|
||||||
clashType = "Http"
|
clashType = "HTTP"
|
||||||
case C.TypeShadowsocks:
|
case C.TypeShadowsocks:
|
||||||
clashType = "Shadowsocks"
|
clashType = "Shadowsocks"
|
||||||
case C.TypeVMess:
|
case C.TypeVMess:
|
||||||
clashType = "Vmess"
|
clashType = "VMess"
|
||||||
case C.TypeTrojan:
|
case C.TypeTrojan:
|
||||||
clashType = "Trojan"
|
clashType = "Trojan"
|
||||||
|
case C.TypeHysteria:
|
||||||
|
clashType = "Hysteria"
|
||||||
|
case C.TypeWireGuard:
|
||||||
|
clashType = "WireGuard"
|
||||||
|
case C.TypeShadowsocksR:
|
||||||
|
clashType = "ShadowsocksR"
|
||||||
|
case C.TypeVLESS:
|
||||||
|
clashType = "VLESS"
|
||||||
|
case C.TypeTor:
|
||||||
|
clashType = "Tor"
|
||||||
|
case C.TypeSSH:
|
||||||
|
clashType = "SSH"
|
||||||
case C.TypeSelector:
|
case C.TypeSelector:
|
||||||
clashType = "Selector"
|
clashType = "Selector"
|
||||||
isGroup = true
|
case C.TypeURLTest:
|
||||||
|
clashType = "URLTest"
|
||||||
default:
|
default:
|
||||||
clashType = "Socks"
|
clashType = "Direct"
|
||||||
}
|
}
|
||||||
info.Put("type", clashType)
|
info.Put("type", clashType)
|
||||||
info.Put("name", detour.Tag())
|
info.Put("name", detour.Tag())
|
||||||
@@ -92,10 +104,9 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
|||||||
} else {
|
} else {
|
||||||
info.Put("history", []*urltest.History{})
|
info.Put("history", []*urltest.History{})
|
||||||
}
|
}
|
||||||
if isGroup {
|
if group, isGroup := detour.(adapter.OutboundGroup); isGroup {
|
||||||
selector := detour.(adapter.OutboundGroup)
|
info.Put("now", group.Now())
|
||||||
info.Put("now", selector.Now())
|
info.Put("all", group.All())
|
||||||
info.Put("all", selector.All())
|
|
||||||
}
|
}
|
||||||
return &info
|
return &info
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/json"
|
"github.com/sagernet/sing-box/common/json"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
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/cachefile"
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
@@ -22,13 +23,17 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/websocket"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
experimental.RegisterClashServerConstructor(NewServer)
|
||||||
|
}
|
||||||
|
|
||||||
var _ adapter.ClashServer = (*Server)(nil)
|
var _ adapter.ClashServer = (*Server)(nil)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
@@ -38,12 +43,13 @@ type Server struct {
|
|||||||
trafficManager *trafficontrol.Manager
|
trafficManager *trafficontrol.Manager
|
||||||
urlTestHistory *urltest.HistoryStorage
|
urlTestHistory *urltest.HistoryStorage
|
||||||
tcpListener net.Listener
|
tcpListener net.Listener
|
||||||
|
directIO bool
|
||||||
mode string
|
mode string
|
||||||
storeSelected bool
|
storeSelected bool
|
||||||
cacheFile adapter.ClashCacheFile
|
cacheFile adapter.ClashCacheFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (*Server, error) {
|
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
|
||||||
trafficManager := trafficontrol.NewManager()
|
trafficManager := trafficontrol.NewManager()
|
||||||
chiRouter := chi.NewRouter()
|
chiRouter := chi.NewRouter()
|
||||||
server := &Server{
|
server := &Server{
|
||||||
@@ -55,12 +61,14 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||||||
},
|
},
|
||||||
trafficManager: trafficManager,
|
trafficManager: trafficManager,
|
||||||
urlTestHistory: urltest.NewHistoryStorage(),
|
urlTestHistory: urltest.NewHistoryStorage(),
|
||||||
|
directIO: options.DirectIO,
|
||||||
mode: strings.ToLower(options.DefaultMode),
|
mode: strings.ToLower(options.DefaultMode),
|
||||||
}
|
}
|
||||||
if server.mode == "" {
|
if server.mode == "" {
|
||||||
server.mode = "rule"
|
server.mode = "rule"
|
||||||
}
|
}
|
||||||
if options.StoreSelected {
|
if options.StoreSelected {
|
||||||
|
server.storeSelected = true
|
||||||
cachePath := os.ExpandEnv(options.CacheFile)
|
cachePath := os.ExpandEnv(options.CacheFile)
|
||||||
if cachePath == "" {
|
if cachePath == "" {
|
||||||
cachePath = "cache.db"
|
cachePath = "cache.db"
|
||||||
@@ -80,7 +88,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||||||
chiRouter.Use(cors.Handler)
|
chiRouter.Use(cors.Handler)
|
||||||
chiRouter.Group(func(r chi.Router) {
|
chiRouter.Group(func(r chi.Router) {
|
||||||
r.Use(authentication(options.Secret))
|
r.Use(authentication(options.Secret))
|
||||||
r.Get("/", hello)
|
r.Get("/", hello(options.ExternalUI != ""))
|
||||||
r.Get("/logs", getLogs(logFactory))
|
r.Get("/logs", getLogs(logFactory))
|
||||||
r.Get("/traffic", traffic(trafficManager))
|
r.Get("/traffic", traffic(trafficManager))
|
||||||
r.Get("/version", version)
|
r.Get("/version", version)
|
||||||
@@ -143,8 +151,12 @@ func (s *Server) CacheFile() adapter.ClashCacheFile {
|
|||||||
return s.cacheFile
|
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) {
|
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)
|
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule, s.directIO)
|
||||||
return tracker, tracker
|
return tracker, tracker
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,8 +244,14 @@ func authentication(serverSecret string) func(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hello(w http.ResponseWriter, r *http.Request) {
|
func hello(redirect bool) func(w http.ResponseWriter, r *http.Request) {
|
||||||
render.JSON(w, r, render.M{"hello": "clash"})
|
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"})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/experimental/trackerconn"
|
||||||
"github.com/sagernet/sing/common"
|
"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"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
@@ -45,7 +44,7 @@ type trackerInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type tcpTracker struct {
|
type tcpTracker struct {
|
||||||
net.Conn `json:"-"`
|
N.ExtendedConn `json:"-"`
|
||||||
*trackerInfo
|
*trackerInfo
|
||||||
manager *Manager
|
manager *Manager
|
||||||
}
|
}
|
||||||
@@ -54,25 +53,9 @@ func (tt *tcpTracker) ID() string {
|
|||||||
return tt.UUID.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 {
|
func (tt *tcpTracker) Close() error {
|
||||||
tt.manager.Leave(tt)
|
tt.manager.Leave(tt)
|
||||||
return tt.Conn.Close()
|
return tt.ExtendedConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Leave() {
|
func (tt *tcpTracker) Leave() {
|
||||||
@@ -80,10 +63,18 @@ func (tt *tcpTracker) Leave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Upstream() any {
|
func (tt *tcpTracker) Upstream() any {
|
||||||
return tt.Conn
|
return tt.ExtendedConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *tcpTracker {
|
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 {
|
||||||
uuid, _ := uuid.NewV4()
|
uuid, _ := uuid.NewV4()
|
||||||
|
|
||||||
var chain []string
|
var chain []string
|
||||||
@@ -106,8 +97,17 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
|
|||||||
next = group.Now()
|
next = group.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upload := atomic.NewInt64(0)
|
||||||
|
download := atomic.NewInt64(0)
|
||||||
|
|
||||||
t := &tcpTracker{
|
t := &tcpTracker{
|
||||||
Conn: conn,
|
ExtendedConn: trackerconn.NewHook(conn, func(n int64) {
|
||||||
|
upload.Add(n)
|
||||||
|
manager.PushUploaded(n)
|
||||||
|
}, func(n int64) {
|
||||||
|
download.Add(n)
|
||||||
|
manager.PushDownloaded(n)
|
||||||
|
}, directIO),
|
||||||
manager: manager,
|
manager: manager,
|
||||||
trackerInfo: &trackerInfo{
|
trackerInfo: &trackerInfo{
|
||||||
UUID: uuid,
|
UUID: uuid,
|
||||||
@@ -115,8 +115,8 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
|
|||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
Chain: common.Reverse(chain),
|
Chain: common.Reverse(chain),
|
||||||
Rule: "",
|
Rule: "",
|
||||||
UploadTotal: atomic.NewInt64(0),
|
UploadTotal: upload,
|
||||||
DownloadTotal: atomic.NewInt64(0),
|
DownloadTotal: download,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,27 +140,6 @@ func (ut *udpTracker) ID() string {
|
|||||||
return ut.UUID.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 {
|
func (ut *udpTracker) Close() error {
|
||||||
ut.manager.Leave(ut)
|
ut.manager.Leave(ut)
|
||||||
return ut.PacketConn.Close()
|
return ut.PacketConn.Close()
|
||||||
@@ -174,6 +153,14 @@ func (ut *udpTracker) Upstream() any {
|
|||||||
return ut.PacketConn
|
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 {
|
func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *udpTracker {
|
||||||
uuid, _ := uuid.NewV4()
|
uuid, _ := uuid.NewV4()
|
||||||
|
|
||||||
@@ -197,17 +184,26 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, route
|
|||||||
next = group.Now()
|
next = group.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upload := atomic.NewInt64(0)
|
||||||
|
download := atomic.NewInt64(0)
|
||||||
|
|
||||||
ut := &udpTracker{
|
ut := &udpTracker{
|
||||||
PacketConn: conn,
|
PacketConn: trackerconn.NewHookPacket(conn, func(n int64) {
|
||||||
manager: manager,
|
upload.Add(n)
|
||||||
|
manager.PushUploaded(n)
|
||||||
|
}, func(n int64) {
|
||||||
|
download.Add(n)
|
||||||
|
manager.PushDownloaded(n)
|
||||||
|
}),
|
||||||
|
manager: manager,
|
||||||
trackerInfo: &trackerInfo{
|
trackerInfo: &trackerInfo{
|
||||||
UUID: uuid,
|
UUID: uuid,
|
||||||
Start: time.Now(),
|
Start: time.Now(),
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
Chain: common.Reverse(chain),
|
Chain: common.Reverse(chain),
|
||||||
Rule: "",
|
Rule: "",
|
||||||
UploadTotal: atomic.NewInt64(0),
|
UploadTotal: upload,
|
||||||
DownloadTotal: atomic.NewInt64(0),
|
DownloadTotal: download,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
//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`)
|
|
||||||
}
|
|
||||||
160
experimental/trackerconn/conn.go
Normal file
160
experimental/trackerconn/conn.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
73
experimental/trackerconn/packet_conn.go
Normal file
73
experimental/trackerconn/packet_conn.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
24
experimental/v2rayapi.go
Normal file
24
experimental/v2rayapi.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
75
experimental/v2rayapi/server.go
Normal file
75
experimental/v2rayapi/server.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
194
experimental/v2rayapi/stats.go
Normal file
194
experimental/v2rayapi/stats.go
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
678
experimental/v2rayapi/stats.pb.go
Normal file
678
experimental/v2rayapi/stats.pb.go
Normal file
@@ -0,0 +1,678 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
53
experimental/v2rayapi/stats.proto
Normal file
53
experimental/v2rayapi/stats.proto
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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) {}
|
||||||
|
}
|
||||||
173
experimental/v2rayapi/stats_grpc.pb.go
Normal file
173
experimental/v2rayapi/stats_grpc.pb.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
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",
|
||||||
|
}
|
||||||
41
go.mod
41
go.mod
@@ -4,41 +4,42 @@ go 1.18
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
berty.tech/go-libtor v1.0.385
|
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/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/database64128/tfo-go v1.1.2
|
github.com/database64128/tfo-go/v2 v2.0.2
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/fsnotify/fsnotify v1.5.4
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-chi/render v1.0.2
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/gofrs/uuid v4.3.0+incompatible
|
github.com/gofrs/uuid v4.3.0+incompatible
|
||||||
github.com/gorilla/websocket v1.5.0
|
|
||||||
github.com/hashicorp/yamux v0.1.1
|
github.com/hashicorp/yamux v0.1.1
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/mholt/acmez v1.0.4
|
github.com/mholt/acmez v1.0.4
|
||||||
github.com/miekg/dns v1.1.50
|
github.com/miekg/dns v1.1.50
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0
|
github.com/oschwald/maxminddb-golang v1.10.0
|
||||||
github.com/pires/go-proxyproto v0.6.2
|
github.com/pires/go-proxyproto v0.6.2
|
||||||
github.com/refraction-networking/utls v1.1.2
|
github.com/refraction-networking/utls v1.1.5
|
||||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
|
|
||||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
|
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
|
||||||
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f
|
github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
|
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24
|
github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd
|
||||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f
|
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||||
|
github.com/spf13/cobra v1.6.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
go.uber.org/atomic v1.10.0
|
go.uber.org/atomic v1.10.0
|
||||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
|
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab
|
||||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a
|
||||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
|
golang.org/x/net v0.0.0-20221017152216-f25eb7ecb193
|
||||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2
|
golang.org/x/sys v0.1.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0
|
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
|
||||||
google.golang.org/grpc v1.49.0
|
google.golang.org/grpc v1.50.1
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||||
)
|
)
|
||||||
@@ -53,9 +54,9 @@ require (
|
|||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
github.com/klauspost/compress v1.13.6 // indirect
|
github.com/klauspost/compress v1.15.9 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||||
github.com/libdns/libdns v0.2.1 // indirect
|
github.com/libdns/libdns v0.2.1 // indirect
|
||||||
github.com/marten-seemann/qpack v0.2.1 // indirect
|
github.com/marten-seemann/qpack v0.2.1 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||||
@@ -69,7 +70,7 @@ require (
|
|||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
go.uber.org/zap v1.22.0 // indirect
|
go.uber.org/zap v1.23.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // 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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
|||||||
93
go.sum
93
go.sum
@@ -3,6 +3,8 @@ 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.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.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/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 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||||
@@ -10,6 +12,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
|
|||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
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 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
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 h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
|
||||||
@@ -21,8 +25,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.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 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||||
github.com/database64128/tfo-go v1.1.2 h1:GwxtJp09BdUTVEoeT421t231eNZoGOCRkklbl4WI1kU=
|
github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg=
|
||||||
github.com/database64128/tfo-go v1.1.2/go.mod h1:jgrSUPyOvTGQyn6irCOpk7L2W/q/0VLZZcovQiMi+bI=
|
github.com/database64128/tfo-go/v2 v2.0.2/go.mod h1:FDdt4JaAsRU66wsYHxSVytYimPkKIHupVsxM+5DhvjY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -36,8 +40,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
|
|||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
@@ -77,24 +81,22 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
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.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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.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 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
@@ -129,14 +131,12 @@ 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
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.5 h1:JtrojoNhbUQkBqEg05sP3gDgDj6hIEAAVKbI9lx4n6w=
|
||||||
github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tSdX1+SqUB7rL0QnGqyg=
|
github.com/refraction-networking/utls v1.1.5/go.mod h1:jRQxtYi7nkq1p28HF2lwOH5zQm9aC8rpK0O9lIIzGh8=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
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/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 h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
|
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 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
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 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
@@ -145,20 +145,22 @@ github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTY
|
|||||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
|
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-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-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f h1:w1TJq7Lw3It35tDyMsZLtYz4T2msf1UK9JxC85L5+sk=
|
github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo=
|
||||||
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
|
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 h1:AEdyJxEDFq38z0pBX/0MpikQapGMIch+1ADe9k1bJqU=
|
||||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
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-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 h1:LsmPeFvj4GhiV5Y7Rm8I845XysdxVN4MQmfZ36P5bmw=
|
github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd h1:TtoZDwg09Cpqi+gCmCtL6w4oEUZ5lHz+vHIjdr1UBNY=
|
||||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU=
|
||||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
|
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 h1:AZzFNRR/ZwMTceUQ1b/mxx6oyKqmFymdMn/yleJmoVM=
|
||||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
|
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
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/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
||||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||||
|
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
|
||||||
|
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -184,18 +186,18 @@ 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 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
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.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||||
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
|
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
||||||
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
|
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
|
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q=
|
||||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/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-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-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-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-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-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-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/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
|
||||||
|
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/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-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 h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||||
@@ -225,10 +227,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
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-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-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-20211112202133-69e39bad7dc2/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-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
|
|
||||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
|
golang.org/x/net v0.0.0-20221017152216-f25eb7ecb193 h1:3Moaxt4TfzNcQH6DWvlYKraN1ozhBXQHcgvXjRGeim0=
|
||||||
|
golang.org/x/net v0.0.0-20221017152216-f25eb7ecb193/go.mod h1:RpDiru2p0u2F0lLpEoqnP2+7xs0ifAuOcJ442g6GU2s=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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/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=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -236,8 +239,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -258,12 +261,13 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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-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-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-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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||||
|
golang.org/x/sys v0.1.0/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-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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -295,8 +299,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
|
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
|
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
@@ -312,8 +316,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
|||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||||
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
|
google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -338,9 +342,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ package inbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/settings"
|
"github.com/sagernet/sing-box/common/settings"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-dns"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -42,7 +40,6 @@ type myInboundAdapter struct {
|
|||||||
tcpListener net.Listener
|
tcpListener net.Listener
|
||||||
udpConn *net.UDPConn
|
udpConn *net.UDPConn
|
||||||
udpAddr M.Socksaddr
|
udpAddr M.Socksaddr
|
||||||
packetAccess sync.RWMutex
|
|
||||||
packetOutboundClosed chan struct{}
|
packetOutboundClosed chan struct{}
|
||||||
packetOutbound chan *myInboundPacket
|
packetOutbound chan *myInboundPacket
|
||||||
}
|
}
|
||||||
@@ -139,17 +136,15 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
|
|||||||
metadata.Inbound = a.tag
|
metadata.Inbound = a.tag
|
||||||
metadata.InboundType = a.protocol
|
metadata.InboundType = a.protocol
|
||||||
metadata.InboundDetour = a.listenOptions.Detour
|
metadata.InboundDetour = a.listenOptions.Detour
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
|
||||||
if !metadata.Source.IsValid() {
|
if !metadata.Source.IsValid() {
|
||||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
|
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||||
}
|
}
|
||||||
if !metadata.Destination.IsValid() {
|
if !metadata.Destination.IsValid() {
|
||||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
|
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||||
}
|
}
|
||||||
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
|
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
|
||||||
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr())
|
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr()).Unwrap()
|
||||||
}
|
}
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/database64128/tfo-go"
|
"github.com/database64128/tfo-go/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-dns"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
@@ -51,10 +50,8 @@ func (a *myInboundAdapter) loopUDPIn() {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = a.tag
|
metadata.Inbound = a.tag
|
||||||
metadata.InboundType = a.protocol
|
metadata.InboundType = a.protocol
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
|
||||||
metadata.OriginDestination = a.udpAddr
|
metadata.OriginDestination = a.udpAddr
|
||||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -83,10 +80,8 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = a.tag
|
metadata.Inbound = a.tag
|
||||||
metadata.InboundType = a.protocol
|
metadata.InboundType = a.protocol
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
|
||||||
metadata.OriginDestination = a.udpAddr
|
metadata.OriginDestination = a.udpAddr
|
||||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,10 +104,8 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = a.tag
|
metadata.Inbound = a.tag
|
||||||
metadata.InboundType = a.protocol
|
metadata.InboundType = a.protocol
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
|
||||||
metadata.OriginDestination = a.udpAddr
|
metadata.OriginDestination = a.udpAddr
|
||||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -137,10 +130,8 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = a.tag
|
metadata.Inbound = a.tag
|
||||||
metadata.InboundType = a.protocol
|
metadata.InboundType = a.protocol
|
||||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
|
||||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
|
||||||
metadata.OriginDestination = a.udpAddr
|
metadata.OriginDestination = a.udpAddr
|
||||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -208,17 +199,12 @@ func (s *myInboundPacketAdapter) Upstream() any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
s.packetAccess.RLock()
|
|
||||||
defer s.packetAccess.RUnlock()
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
case s.packetOutbound <- &myInboundPacket{buffer, destination}:
|
||||||
|
return nil
|
||||||
case <-s.packetOutboundClosed:
|
case <-s.packetOutboundClosed:
|
||||||
return os.ErrClosed
|
return os.ErrClosed
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.packetOutbound <- &myInboundPacket{buffer, destination}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *myInboundPacketAdapter) Close() error {
|
func (s *myInboundPacketAdapter) Close() error {
|
||||||
|
|||||||
@@ -72,8 +72,12 @@ func (h *HTTP) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
var err error
|
||||||
if h.tlsConfig != nil {
|
if h.tlsConfig != nil {
|
||||||
conn = h.tlsConfig.Server(conn)
|
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/hysteria"
|
"github.com/sagernet/sing-box/transport/hysteria"
|
||||||
"github.com/sagernet/sing-dns"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -255,24 +254,23 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
|
|
||||||
OK: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.tag
|
metadata.Inbound = h.tag
|
||||||
metadata.InboundType = C.TypeHysteria
|
metadata.InboundType = C.TypeHysteria
|
||||||
metadata.SniffEnabled = h.listenOptions.SniffEnabled
|
metadata.InboundOptions = h.listenOptions.InboundOptions
|
||||||
metadata.SniffOverrideDestination = h.listenOptions.SniffOverrideDestination
|
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||||
metadata.DomainStrategy = dns.DomainStrategy(h.listenOptions.DomainStrategy)
|
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
|
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port).Unwrap()
|
||||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr())
|
|
||||||
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
|
|
||||||
if !request.UDP {
|
if !request.UDP {
|
||||||
|
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
|
||||||
|
OK: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata)
|
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination, false), metadata)
|
||||||
} else {
|
} else {
|
||||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
var id uint32
|
var id uint32
|
||||||
@@ -282,6 +280,13 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
|
|||||||
h.udpSessions[id] = nCh
|
h.udpSessions[id] = nCh
|
||||||
h.udpSessionId += 1
|
h.udpSessionId += 1
|
||||||
h.udpAccess.Unlock()
|
h.udpAccess.Unlock()
|
||||||
|
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
|
||||||
|
OK: true,
|
||||||
|
UDPSessionID: id,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
|
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
|
||||||
h.udpAccess.Lock()
|
h.udpAccess.Lock()
|
||||||
if ch, ok := h.udpSessions[id]; ok {
|
if ch, ok := h.udpSessions[id]; ok {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
@@ -105,7 +106,7 @@ func (n *Naive) Start() error {
|
|||||||
|
|
||||||
if common.Contains(n.network, N.NetworkUDP) {
|
if common.Contains(n.network, N.NetworkUDP) {
|
||||||
err := n.configureHTTP3Listener()
|
err := n.configureHTTP3Listener()
|
||||||
if !C.QUIC_AVAILABLE && len(n.network) > 1 {
|
if !include.WithQUIC && len(n.network) > 1 {
|
||||||
log.Warn(E.Cause(err, "naive http3 disabled"))
|
log.Warn(E.Cause(err, "naive http3 disabled"))
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -251,7 +252,14 @@ func (c *naiveH1Conn) read(p []byte) (n int, err error) {
|
|||||||
c.paddingRemaining = 0
|
c.paddingRemaining = 0
|
||||||
}
|
}
|
||||||
if c.readPadding < kFirstPaddings {
|
if c.readPadding < kFirstPaddings {
|
||||||
paddingHdr := p[:3]
|
var paddingHdr []byte
|
||||||
|
if len(p) >= 3 {
|
||||||
|
paddingHdr = p[:3]
|
||||||
|
} else {
|
||||||
|
_paddingHdr := make([]byte, 3)
|
||||||
|
defer common.KeepAlive(_paddingHdr)
|
||||||
|
paddingHdr = common.Dup(_paddingHdr)
|
||||||
|
}
|
||||||
_, err = io.ReadFull(c.Conn, paddingHdr)
|
_, err = io.ReadFull(c.Conn, paddingHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -423,7 +431,14 @@ func (c *naiveH2Conn) read(p []byte) (n int, err error) {
|
|||||||
c.paddingRemaining = 0
|
c.paddingRemaining = 0
|
||||||
}
|
}
|
||||||
if c.readPadding < kFirstPaddings {
|
if c.readPadding < kFirstPaddings {
|
||||||
paddingHdr := p[:3]
|
var paddingHdr []byte
|
||||||
|
if len(p) >= 3 {
|
||||||
|
paddingHdr = p[:3]
|
||||||
|
} else {
|
||||||
|
_paddingHdr := make([]byte, 3)
|
||||||
|
defer common.KeepAlive(_paddingHdr)
|
||||||
|
paddingHdr = common.Dup(_paddingHdr)
|
||||||
|
}
|
||||||
_, err = io.ReadFull(c.reader, paddingHdr)
|
_, err = io.ReadFull(c.reader, paddingHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -6,12 +6,17 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/transport/shadowtls"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@@ -22,6 +27,8 @@ type ShadowTLS struct {
|
|||||||
myInboundAdapter
|
myInboundAdapter
|
||||||
handshakeDialer N.Dialer
|
handshakeDialer N.Dialer
|
||||||
handshakeAddr M.Socksaddr
|
handshakeAddr M.Socksaddr
|
||||||
|
v2 bool
|
||||||
|
password string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
||||||
@@ -37,6 +44,16 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
},
|
},
|
||||||
handshakeDialer: dialer.New(router, options.Handshake.DialerOptions),
|
handshakeDialer: dialer.New(router, options.Handshake.DialerOptions),
|
||||||
handshakeAddr: options.Handshake.ServerOptions.Build(),
|
handshakeAddr: options.Handshake.ServerOptions.Build(),
|
||||||
|
password: options.Password,
|
||||||
|
}
|
||||||
|
switch options.Version {
|
||||||
|
case 0:
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
inbound.v2 = true
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
|
||||||
}
|
}
|
||||||
inbound.connHandler = inbound
|
inbound.connHandler = inbound
|
||||||
return inbound, nil
|
return inbound, nil
|
||||||
@@ -47,19 +64,39 @@ func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata a
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var handshake task.Group
|
if !s.v2 {
|
||||||
handshake.Append("client handshake", func(ctx context.Context) error {
|
var handshake task.Group
|
||||||
return s.copyUntilHandshakeFinished(handshakeConn, conn)
|
handshake.Append("client handshake", func(ctx context.Context) error {
|
||||||
})
|
return s.copyUntilHandshakeFinished(handshakeConn, conn)
|
||||||
handshake.Append("server handshake", func(ctx context.Context) error {
|
})
|
||||||
return s.copyUntilHandshakeFinished(conn, handshakeConn)
|
handshake.Append("server handshake", func(ctx context.Context) error {
|
||||||
})
|
return s.copyUntilHandshakeFinished(conn, handshakeConn)
|
||||||
handshake.FastFail()
|
})
|
||||||
err = handshake.Run(ctx)
|
handshake.FastFail()
|
||||||
if err != nil {
|
handshake.Cleanup(func() {
|
||||||
return err
|
handshakeConn.Close()
|
||||||
|
})
|
||||||
|
err = handshake.Run(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.newConnection(ctx, conn, metadata)
|
||||||
|
} else {
|
||||||
|
hashConn := shadowtls.NewHashWriteConn(conn, s.password)
|
||||||
|
go bufio.Copy(hashConn, handshakeConn)
|
||||||
|
var request *buf.Buffer
|
||||||
|
request, err = s.copyUntilHandshakeFinishedV2(handshakeConn, conn, hashConn)
|
||||||
|
if err == nil {
|
||||||
|
handshakeConn.Close()
|
||||||
|
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewConn(conn), request), metadata)
|
||||||
|
} else if err == os.ErrPermission {
|
||||||
|
s.logger.WarnContext(ctx, "fallback connection")
|
||||||
|
hashConn.Fallback()
|
||||||
|
return common.Error(bufio.Copy(handshakeConn, conn))
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return s.newConnection(ctx, conn, metadata)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) error {
|
func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) error {
|
||||||
@@ -91,3 +128,39 @@ func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ShadowTLS) copyUntilHandshakeFinishedV2(dst net.Conn, src io.Reader, hash *shadowtls.HashWriteConn) (*buf.Buffer, error) {
|
||||||
|
const applicationData = 0x17
|
||||||
|
var tlsHdr [5]byte
|
||||||
|
var applicationDataCount int
|
||||||
|
for {
|
||||||
|
_, err := io.ReadFull(src, tlsHdr[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
length := binary.BigEndian.Uint16(tlsHdr[3:])
|
||||||
|
if tlsHdr[0] == applicationData {
|
||||||
|
data := buf.NewSize(int(length))
|
||||||
|
_, err = data.ReadFullFrom(src, int(length))
|
||||||
|
if err != nil {
|
||||||
|
data.Release()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if length >= 8 && bytes.Equal(data.To(8), hash.Sum()) {
|
||||||
|
data.Advance(8)
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), data))
|
||||||
|
data.Release()
|
||||||
|
applicationDataCount++
|
||||||
|
} else {
|
||||||
|
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), io.LimitReader(src, int64(length))))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if applicationDataCount > 3 {
|
||||||
|
return nil, os.ErrPermission
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func (t *TProxy) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
|
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||||
return t.newConnection(ctx, conn, metadata)
|
return t.newConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,14 +84,15 @@ func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.B
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "get tproxy destination")
|
return E.Cause(err, "get tproxy destination")
|
||||||
}
|
}
|
||||||
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
metadata.Destination = M.SocksaddrFromNetIP(destination).Unwrap()
|
||||||
t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
|
t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
|
||||||
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{source: natConn}
|
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn, destination: metadata.Destination}
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type tproxyPacketWriter struct {
|
type tproxyPacketWriter struct {
|
||||||
|
ctx context.Context
|
||||||
source N.PacketConn
|
source N.PacketConn
|
||||||
destination M.Socksaddr
|
destination M.Socksaddr
|
||||||
conn *net.UDPConn
|
conn *net.UDPConn
|
||||||
@@ -99,25 +100,27 @@ type tproxyPacketWriter struct {
|
|||||||
|
|
||||||
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
var udpConn *net.UDPConn
|
if w.destination == destination && w.conn != nil {
|
||||||
|
_, err := w.conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
|
||||||
|
if err == nil {
|
||||||
|
w.conn = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var listener net.ListenConfig
|
||||||
|
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||||
|
listener.Control = control.Append(listener.Control, redir.TProxyWriteBack())
|
||||||
|
packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
udpConn := packetConn.(*net.UDPConn)
|
||||||
if w.destination == destination {
|
if w.destination == destination {
|
||||||
if w.conn != nil {
|
w.conn = udpConn
|
||||||
udpConn = w.conn
|
} else {
|
||||||
}
|
defer udpConn.Close()
|
||||||
}
|
}
|
||||||
if udpConn == nil {
|
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
|
||||||
var err error
|
|
||||||
udpConn, err = redir.DialUDP(destination.UDPAddr(), M.SocksaddrFromNet(w.source.LocalAddr()).UDPAddr())
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "tproxy udp write back")
|
|
||||||
}
|
|
||||||
if w.destination == destination {
|
|
||||||
w.conn = udpConn
|
|
||||||
} else {
|
|
||||||
defer udpConn.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return common.Error(udpConn.Write(buffer.Bytes()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *tproxyPacketWriter) Close() error {
|
func (w *tproxyPacketWriter) Close() error {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user