mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
28 Commits
v1.1.5
...
dev-ssm-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d34091f8fc | ||
|
|
ec4a0c8497 | ||
|
|
21cb227bc2 | ||
|
|
1610bdc5dd | ||
|
|
3296a2f7b2 | ||
|
|
2bd91baad0 | ||
|
|
a624cd9b49 | ||
|
|
02afba132f | ||
|
|
99890a1af0 | ||
|
|
437f1f819c | ||
|
|
92a79e6158 | ||
|
|
c9efd0a74f | ||
|
|
9da349748a | ||
|
|
2423cbbbfe | ||
|
|
4833f6d5db | ||
|
|
9db3cb5cb7 | ||
|
|
c14b353a29 | ||
|
|
19d08b55c8 | ||
|
|
39514b3ca0 | ||
|
|
7ea9d48987 | ||
|
|
df3a982141 | ||
|
|
687b4509df | ||
|
|
41ec2e7944 | ||
|
|
1bd3a9144d | ||
|
|
6e852cc99b | ||
|
|
8320dd0b51 | ||
|
|
960d04d172 | ||
|
|
86ea035bdd |
28
.github/renovate.json
vendored
Normal file
28
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"commitMessagePrefix": "[dependencies]",
|
||||
"extends": [
|
||||
"config:base",
|
||||
":disableRateLimiting"
|
||||
],
|
||||
"baseBranches": [
|
||||
"dev-next"
|
||||
],
|
||||
"golang": {
|
||||
"enabled": false
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": [
|
||||
"github-actions"
|
||||
],
|
||||
"groupName": "github-actions"
|
||||
},
|
||||
{
|
||||
"matchManagers": [
|
||||
"dockerfile"
|
||||
],
|
||||
"groupName": "Dockerfile"
|
||||
}
|
||||
]
|
||||
}
|
||||
33
.github/workflows/debug.yml
vendored
33
.github/workflows/debug.yml
vendored
@@ -19,24 +19,20 @@ jobs:
|
||||
name: Debug build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.7.0
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
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')
|
||||
echo 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') >> $GITHUB_OUTPUT
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
@@ -58,22 +54,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.7
|
||||
go-version: 1.18.10
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go118-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: |
|
||||
go test -v ./...
|
||||
run: make
|
||||
cross:
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -190,19 +185,19 @@ jobs:
|
||||
TAGS: with_clash_api,with_quic
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
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')
|
||||
echo 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') >> $GITHUB_OUTPUT
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
@@ -211,7 +206,7 @@ jobs:
|
||||
id: build
|
||||
run: make
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sing-box-${{ matrix.name }}
|
||||
path: sing-box*
|
||||
|
||||
10
.github/workflows/docker.yml
vendored
10
.github/workflows/docker.yml
vendored
@@ -9,20 +9,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Setup QEMU for Docker Buildx
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Docker metadata
|
||||
id: metadata
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ghcr.io/sagernet/sing-box
|
||||
- name: Get tag to build
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Build and release Docker images
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
platforms: linux/386,linux/amd64,linux/arm64,linux/s390x
|
||||
target: dist
|
||||
|
||||
17
.github/workflows/lint.yml
vendored
17
.github/workflows/lint.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/debug.yml'
|
||||
- '!.github/workflows/lint.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main-next
|
||||
@@ -19,31 +19,24 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.7.0
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
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')
|
||||
echo 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') >> $GITHUB_OUTPUT
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
go mod download -x
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
|
||||
4
.github/workflows/mkdocs.yml
vendored
4
.github/workflows/mkdocs.yml
vendored
@@ -10,8 +10,8 @@ jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.x
|
||||
- run: pip install mkdocs-material mkdocs-static-i18n
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
- uses: actions/stale@v7
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 5 days'
|
||||
days-before-stale: 60
|
||||
|
||||
@@ -3,19 +3,24 @@ linters:
|
||||
enable:
|
||||
- gofumpt
|
||||
- govet
|
||||
# - gci
|
||||
- gci
|
||||
- staticcheck
|
||||
- paralleltest
|
||||
|
||||
run:
|
||||
skip-dirs:
|
||||
- transport/simple-obfs
|
||||
- transport/clashssr
|
||||
- transport/cloudflaretls
|
||||
- transport/shadowtls/tls
|
||||
- transport/shadowtls/tls_go119
|
||||
|
||||
linters-settings:
|
||||
# gci:
|
||||
# sections:
|
||||
# - standard
|
||||
# - prefix(github.com/sagernet/)
|
||||
# - default
|
||||
gci:
|
||||
custom-order: true
|
||||
sections:
|
||||
- standard
|
||||
- prefix(github.com/sagernet/)
|
||||
- default
|
||||
staticcheck:
|
||||
go: '1.19'
|
||||
go: '1.20'
|
||||
|
||||
@@ -17,6 +17,7 @@ builds:
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_clash_api
|
||||
- with_ssm_api
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
targets:
|
||||
@@ -50,6 +51,7 @@ builds:
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_clash_api
|
||||
- with_ssm_api
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
overrides:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.19-alpine AS builder
|
||||
FROM golang:1.20-alpine AS builder
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
COPY . /go/src/github.com/sagernet/sing-box
|
||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||
|
||||
6
Makefile
6
Makefile
@@ -1,6 +1,6 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,with_ssm_api
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
|
||||
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
|
||||
MAIN = ./cmd/sing-box
|
||||
@@ -16,11 +16,11 @@ install:
|
||||
fmt:
|
||||
@gofumpt -l -w .
|
||||
@gofmt -s -w .
|
||||
@gci write -s "standard,prefix(github.com/sagernet/),default" .
|
||||
@gci write --custom-order -s "standard,prefix(github.com/sagernet/),default" .
|
||||
|
||||
fmt_install:
|
||||
go install -v mvdan.cc/gofumpt@latest
|
||||
go install -v github.com/daixiang0/gci@v0.4.0
|
||||
go install -v github.com/daixiang0/gci@latest
|
||||
|
||||
lint:
|
||||
GOOS=linux golangci-lint run ./...
|
||||
|
||||
@@ -45,6 +45,19 @@ type V2RayServer interface {
|
||||
}
|
||||
|
||||
type V2RayStatsService interface {
|
||||
RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn
|
||||
RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn
|
||||
RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn
|
||||
RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn
|
||||
}
|
||||
|
||||
type SSMServer interface {
|
||||
Service
|
||||
RoutedConnection(metadata InboundContext, conn net.Conn) net.Conn
|
||||
RoutedPacketConnection(metadata InboundContext, conn N.PacketConn) N.PacketConn
|
||||
}
|
||||
|
||||
type ManagedShadowsocksServer interface {
|
||||
Inbound
|
||||
Method() string
|
||||
Password() string
|
||||
UpdateUsers(users []string, uPSKs []string) error
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ type InboundContext struct {
|
||||
SourceGeoIPCode string
|
||||
GeoIPCode string
|
||||
ProcessInfo *process.Info
|
||||
|
||||
// dns cache
|
||||
|
||||
QueryType uint16
|
||||
}
|
||||
|
||||
type inboundContextKey struct{}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
type Router interface {
|
||||
Service
|
||||
|
||||
Inbound(tag string) (Inbound, bool)
|
||||
Outbounds() []Outbound
|
||||
Outbound(tag string) (Outbound, bool)
|
||||
DefaultOutbound(network string) Outbound
|
||||
@@ -45,6 +46,23 @@ type Router interface {
|
||||
|
||||
V2RayServer() V2RayServer
|
||||
SetV2RayServer(server V2RayServer)
|
||||
|
||||
SSMServer() SSMServer
|
||||
SetSSMServer(server SSMServer)
|
||||
}
|
||||
|
||||
type routerContextKey struct{}
|
||||
|
||||
func ContextWithRouter(ctx context.Context, router Router) context.Context {
|
||||
return context.WithValue(ctx, (*routerContextKey)(nil), router)
|
||||
}
|
||||
|
||||
func RouterFromContext(ctx context.Context) Router {
|
||||
metadata := ctx.Value((*routerContextKey)(nil))
|
||||
if metadata == nil {
|
||||
return nil
|
||||
}
|
||||
return metadata.(Router)
|
||||
}
|
||||
|
||||
type Rule interface {
|
||||
|
||||
@@ -3,6 +3,10 @@ package adapter
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type V2RayServerTransport interface {
|
||||
@@ -12,6 +16,12 @@ type V2RayServerTransport interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
type V2RayServerTransportHandler interface {
|
||||
N.TCPConnectionHandler
|
||||
E.Handler
|
||||
FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error
|
||||
}
|
||||
|
||||
type V2RayClientTransport interface {
|
||||
DialContext(ctx context.Context) (net.Conn, error)
|
||||
}
|
||||
|
||||
21
box.go
21
box.go
@@ -32,6 +32,7 @@ type Box struct {
|
||||
logFile *os.File
|
||||
clashServer adapter.ClashServer
|
||||
v2rayServer adapter.V2RayServer
|
||||
ssmServer adapter.SSMServer
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
@@ -41,6 +42,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
|
||||
var needClashAPI bool
|
||||
var needV2RayAPI bool
|
||||
var needSSMAPI bool
|
||||
if options.Experimental != nil {
|
||||
if options.Experimental.ClashAPI != nil && options.Experimental.ClashAPI.ExternalController != "" {
|
||||
needClashAPI = true
|
||||
@@ -48,6 +50,9 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
if options.Experimental.V2RayAPI != nil && options.Experimental.V2RayAPI.Listen != "" {
|
||||
needV2RayAPI = true
|
||||
}
|
||||
if options.Experimental.SSMAPI != nil && options.Experimental.SSMAPI.Listen != "" {
|
||||
needSSMAPI = true
|
||||
}
|
||||
}
|
||||
|
||||
var logFactory log.Factory
|
||||
@@ -156,6 +161,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
|
||||
var clashServer adapter.ClashServer
|
||||
var v2rayServer adapter.V2RayServer
|
||||
var ssmServer adapter.SSMServer
|
||||
if needClashAPI {
|
||||
clashServer, err = experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI))
|
||||
if err != nil {
|
||||
@@ -170,6 +176,13 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
}
|
||||
router.SetV2RayServer(v2rayServer)
|
||||
}
|
||||
if needSSMAPI {
|
||||
ssmServer, err = experimental.NewSSMServer(router, logFactory.NewLogger("ssm-api"), common.PtrValueOrDefault(options.Experimental.SSMAPI))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create ssm api server")
|
||||
}
|
||||
router.SetSSMServer(ssmServer)
|
||||
}
|
||||
return &Box{
|
||||
router: router,
|
||||
inbounds: inbounds,
|
||||
@@ -180,6 +193,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
logFile: logFile,
|
||||
clashServer: clashServer,
|
||||
v2rayServer: v2rayServer,
|
||||
ssmServer: ssmServer,
|
||||
done: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
@@ -244,6 +258,12 @@ func (s *Box) start() error {
|
||||
return E.Cause(err, "start v2ray api server")
|
||||
}
|
||||
}
|
||||
if s.ssmServer != nil {
|
||||
err = s.ssmServer.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start ssm api server")
|
||||
}
|
||||
}
|
||||
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
|
||||
return nil
|
||||
}
|
||||
@@ -266,6 +286,7 @@ func (s *Box) Close() error {
|
||||
s.logFactory,
|
||||
s.clashServer,
|
||||
s.v2rayServer,
|
||||
s.ssmServer,
|
||||
common.PtrOrNil(s.logFile),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ import (
|
||||
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"
|
||||
"github.com/sagernet/tfo-go"
|
||||
)
|
||||
|
||||
var warnBindInterfaceOnUnsupportedPlatform = warning.New(
|
||||
|
||||
@@ -12,8 +12,7 @@ import (
|
||||
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"
|
||||
"github.com/sagernet/tfo-go"
|
||||
)
|
||||
|
||||
type slowOpenConn struct {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"net/netip"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
)
|
||||
@@ -31,8 +30,5 @@ func (r *Reader) Lookup(addr netip.Addr) string {
|
||||
if code != "" {
|
||||
return code
|
||||
}
|
||||
if !N.IsPublicAddr(addr) {
|
||||
return "private"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package redir
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
@@ -29,7 +30,9 @@ func GetOriginalDestination(conn net.Conn) (destination netip.AddrPort, err erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destination = netip.AddrPortFrom(M.AddrFromIP(raw.Addr.Addr[:]), raw.Addr.Port)
|
||||
var port [2]byte
|
||||
binary.BigEndian.PutUint16(port[:], raw.Addr.Port)
|
||||
destination = netip.AddrPortFrom(M.AddrFromIP(raw.Addr.Addr[:]), binary.LittleEndian.Uint16(port[:]))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -10,8 +10,9 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
STDConfig = tls.Config
|
||||
STDConn = tls.Conn
|
||||
STDConfig = tls.Config
|
||||
STDConn = tls.Conn
|
||||
ConnectionState = tls.ConnectionState
|
||||
)
|
||||
|
||||
type Config interface {
|
||||
@@ -33,7 +34,7 @@ type ServerConfig interface {
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
HandshakeContext(ctx context.Context) error
|
||||
ConnectionState() tls.ConnectionState
|
||||
ConnectionState() ConnectionState
|
||||
}
|
||||
|
||||
func ParseTLSVersion(version string) (uint16, error) {
|
||||
|
||||
8
constant/dhcp.go
Normal file
8
constant/dhcp.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constant
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
DHCPTTL = time.Hour
|
||||
DHCPTimeout = time.Minute
|
||||
)
|
||||
@@ -1,3 +1,3 @@
|
||||
package constant
|
||||
|
||||
var Version = "1.1.5"
|
||||
var Version = "1.2-beta2"
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
#### 1.2-beta2
|
||||
|
||||
* Add [ShadowTLS protocol v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md)
|
||||
* Add fallback support for v2ray transport
|
||||
* Fix parse hysteria UDP message
|
||||
* Fix socks connect response
|
||||
* Disable vmess header protection if transport enabled
|
||||
|
||||
#### 1.2-beta1
|
||||
|
||||
* Add [DHCP DNS server](/configuration/dns/server) support
|
||||
* Add SSH [host key validation](/configuration/outbound/ssh) support
|
||||
* Add [query_type](/configuration/dns/rule) DNS rule item
|
||||
* Add v2ray [user stats](/configuration/experimental#statsusers) api
|
||||
* Add new clash DNS query api
|
||||
* Improve vmess request
|
||||
* Fix ipv6 redirect on Linux
|
||||
* Fix match geoip private
|
||||
|
||||
#### 1.1.5
|
||||
|
||||
* Add Go 1.20 support
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
"mixed-in"
|
||||
],
|
||||
"ip_version": 6,
|
||||
"query_type": [
|
||||
"A",
|
||||
"HTTPS",
|
||||
32768
|
||||
],
|
||||
"network": "tcp",
|
||||
"auth_user": [
|
||||
"usera",
|
||||
@@ -119,6 +124,10 @@ Tags of [Inbound](/configuration/inbound).
|
||||
|
||||
Not limited if empty.
|
||||
|
||||
#### query_type
|
||||
|
||||
DNS query type. Values can be integers or type name strings.
|
||||
|
||||
#### network
|
||||
|
||||
`tcp` or `udp`.
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
"mixed-in"
|
||||
],
|
||||
"ip_version": 6,
|
||||
"query_type": [
|
||||
"A",
|
||||
"HTTPS",
|
||||
32768
|
||||
],
|
||||
"network": "tcp",
|
||||
"auth_user": [
|
||||
"usera",
|
||||
@@ -118,6 +123,10 @@
|
||||
|
||||
默认不限制。
|
||||
|
||||
#### query_type
|
||||
|
||||
DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
#### network
|
||||
|
||||
`tcp` 或 `udp`。
|
||||
|
||||
@@ -30,16 +30,17 @@ The tag of the dns server.
|
||||
|
||||
The address of the dns server.
|
||||
|
||||
| Protocol | Format |
|
||||
|----------|-----------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| Protocol | Format |
|
||||
|----------|-------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` or `dhcp://en0` |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
@@ -53,6 +54,10 @@ The address of the dns server.
|
||||
|
||||
the RCode transport is often used to block queries. Use with rules and the `disable_cache` rule option.
|
||||
|
||||
!!! warning ""
|
||||
|
||||
DHCP transport is not included by default, see [Installation](/#installation).
|
||||
|
||||
| RCode | Description |
|
||||
|-------------------|-----------------------|
|
||||
| `success` | `No error` |
|
||||
|
||||
@@ -30,16 +30,17 @@ DNS 服务器的标签。
|
||||
|
||||
DNS 服务器的地址。
|
||||
|
||||
| 协议 | 格式 |
|
||||
|----------|-----------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| 协议 | 格式 |
|
||||
|----------|------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` 或 `dhcp://en0` |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
@@ -53,6 +54,10 @@ DNS 服务器的地址。
|
||||
|
||||
RCode 传输层传输层常用于屏蔽请求. 与 DNS 规则和 `disable_cache` 规则选项一起使用。
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 DHCP 传输层,请参阅 [安装](/zh/#_2)。
|
||||
|
||||
| RCode | 描述 |
|
||||
|-------------------|----------|
|
||||
| `success` | `无错误` |
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "folder",
|
||||
"secret": "",
|
||||
"direct_io": false,
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
"cache_file": "cache.db"
|
||||
@@ -18,13 +17,15 @@
|
||||
"listen": "127.0.0.1:8080",
|
||||
"stats": {
|
||||
"enabled": true,
|
||||
"direct_io": false,
|
||||
"inbounds": [
|
||||
"socks-in"
|
||||
],
|
||||
"outbounds": [
|
||||
"proxy",
|
||||
"direct"
|
||||
],
|
||||
"users": [
|
||||
"sekai"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58,10 +59,6 @@ Secret for the RESTful API (optional)
|
||||
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
|
||||
ALWAYS set a secret if RESTful API is listening on 0.0.0.0
|
||||
|
||||
#### direct_io
|
||||
|
||||
Allows lossless relays like splice without real-time traffic reporting.
|
||||
|
||||
#### default_mode
|
||||
|
||||
Default mode in clash, `rule` will be used if empty.
|
||||
@@ -98,10 +95,6 @@ Traffic statistics service settings.
|
||||
|
||||
Enable statistics service.
|
||||
|
||||
#### stats.direct_io
|
||||
|
||||
Allows lossless relays like splice without real-time traffic reporting.
|
||||
|
||||
#### stats.inbounds
|
||||
|
||||
Inbound list to count traffic.
|
||||
@@ -109,3 +102,7 @@ Inbound list to count traffic.
|
||||
#### stats.outbounds
|
||||
|
||||
Outbound list to count traffic.
|
||||
|
||||
#### stats.users
|
||||
|
||||
User list to count traffic.
|
||||
@@ -9,7 +9,6 @@
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "folder",
|
||||
"secret": "",
|
||||
"direct_io": false,
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
"cache_file": "cache.db"
|
||||
@@ -18,13 +17,15 @@
|
||||
"listen": "127.0.0.1:8080",
|
||||
"stats": {
|
||||
"enabled": true,
|
||||
"direct_io": false,
|
||||
"inbounds": [
|
||||
"socks-in"
|
||||
],
|
||||
"outbounds": [
|
||||
"proxy",
|
||||
"direct"
|
||||
],
|
||||
"users": [
|
||||
"sekai"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -56,10 +57,6 @@ RESTful API 的密钥(可选)
|
||||
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
|
||||
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
|
||||
|
||||
#### direct_io
|
||||
|
||||
允许像 splice 这样的没有实时流量报告的无损中继。
|
||||
|
||||
#### default_mode
|
||||
|
||||
Clash 中的默认模式,默认使用 `rule`。
|
||||
@@ -96,10 +93,6 @@ gRPC API 监听地址。如果为空,则禁用 V2Ray API。
|
||||
|
||||
启用统计服务。
|
||||
|
||||
#### stats.direct_io
|
||||
|
||||
允许像 splice 这样的没有实时流量报告的无损中继。
|
||||
|
||||
#### stats.inbounds
|
||||
|
||||
统计流量的入站列表。
|
||||
@@ -107,3 +100,7 @@ gRPC API 监听地址。如果为空,则禁用 V2Ray API。
|
||||
#### stats.outbounds
|
||||
|
||||
统计流量的出站列表。
|
||||
|
||||
#### stats.users
|
||||
|
||||
统计流量的用户列表。
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"version": 2,
|
||||
"version": 3,
|
||||
"password": "fuck me till the daylight",
|
||||
"tls": {},
|
||||
|
||||
@@ -37,12 +37,13 @@ ShadowTLS 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) |
|
||||
| `3` | [ShadowTLS v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md) |
|
||||
|
||||
#### password
|
||||
|
||||
Set password.
|
||||
|
||||
Only available in the ShadowTLS v2 protocol.
|
||||
Only available in the ShadowTLS v2/v3 protocol.
|
||||
|
||||
#### tls
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"version": 2,
|
||||
"version": 3,
|
||||
"password": "fuck me till the daylight",
|
||||
"tls": {},
|
||||
|
||||
@@ -37,12 +37,13 @@ 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) |
|
||||
| `3` | [ShadowTLS v3](https://github.com/ihciah/shadow-tls/blob/master/docs/protocol-v3-en.md) |
|
||||
|
||||
#### password
|
||||
|
||||
设置密码。
|
||||
|
||||
仅在 ShadowTLS v2 协议中可用。
|
||||
仅在 ShadowTLS v2/v3 协议中可用。
|
||||
|
||||
#### tls
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
"private_key": "",
|
||||
"private_key_path": "$HOME/.ssh/id_rsa",
|
||||
"private_key_passphrase": "",
|
||||
"host_key": [
|
||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdH..."
|
||||
],
|
||||
"host_key_algorithms": [],
|
||||
"client_version": "SSH-2.0-OpenSSH_7.4p1",
|
||||
|
||||
@@ -51,6 +54,10 @@ Private key path.
|
||||
|
||||
Private key passphrase.
|
||||
|
||||
#### host_key
|
||||
|
||||
Host key. Accept any if empty.
|
||||
|
||||
#### host_key_algorithms
|
||||
|
||||
Host key algorithms.
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
"private_key": "",
|
||||
"private_key_path": "$HOME/.ssh/id_rsa",
|
||||
"private_key_passphrase": "",
|
||||
"host_key": [
|
||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdH..."
|
||||
],
|
||||
"host_key_algorithms": [],
|
||||
"client_version": "SSH-2.0-OpenSSH_7.4p1",
|
||||
|
||||
@@ -51,6 +54,10 @@ SSH 用户, 默认使用 root。
|
||||
|
||||
密钥密码。
|
||||
|
||||
#### host_key
|
||||
|
||||
主机密钥,留空接受所有。
|
||||
|
||||
#### host_key_algorithms
|
||||
|
||||
主机密钥算法。
|
||||
|
||||
@@ -24,8 +24,9 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
|
||||
| Build Tag | Description |
|
||||
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `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_dhcp` | Build with DHCP support, see [DHCP DNS transport](./configuration/dns/server). |
|
||||
| `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). |
|
||||
|
||||
@@ -26,6 +26,7 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server),[Naive 入站](./configuration/inbound/naive),[Hysteria 入站](./configuration/inbound/hysteria),[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
|
||||
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
|
||||
| `with_dhcp` | 启用 DHCP 支持,参阅 [DHCP DNS 传输层](./configuration/dns/server)。 |
|
||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
|
||||
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
||||
|
||||
82
experimental/clashapi/dns.go
Normal file
82
experimental/clashapi/dns.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package clashapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func dnsRouter(router adapter.Router) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/query", queryDNS(router))
|
||||
return r
|
||||
}
|
||||
|
||||
func queryDNS(router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
name := r.URL.Query().Get("name")
|
||||
qTypeStr := r.URL.Query().Get("type")
|
||||
if qTypeStr == "" {
|
||||
qTypeStr = "A"
|
||||
}
|
||||
|
||||
qType, exist := dns.StringToType[qTypeStr]
|
||||
if !exist {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, newError("invalid query type"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.DNSTimeout)
|
||||
defer cancel()
|
||||
|
||||
msg := dns.Msg{}
|
||||
msg.SetQuestion(dns.Fqdn(name), qType)
|
||||
resp, err := router.Exchange(ctx, &msg)
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
responseData := render.M{
|
||||
"Status": resp.Rcode,
|
||||
"Question": resp.Question,
|
||||
"Server": "internal",
|
||||
"TC": resp.Truncated,
|
||||
"RD": resp.RecursionDesired,
|
||||
"RA": resp.RecursionAvailable,
|
||||
"AD": resp.AuthenticatedData,
|
||||
"CD": resp.CheckingDisabled,
|
||||
}
|
||||
|
||||
rr2Json := func(rr dns.RR) render.M {
|
||||
header := rr.Header()
|
||||
return render.M{
|
||||
"name": header.Name,
|
||||
"type": header.Rrtype,
|
||||
"TTL": header.Ttl,
|
||||
"data": rr.String()[len(header.String()):],
|
||||
}
|
||||
}
|
||||
|
||||
if len(resp.Answer) > 0 {
|
||||
responseData["Answer"] = common.Map(resp.Answer, rr2Json)
|
||||
}
|
||||
if len(resp.Ns) > 0 {
|
||||
responseData["Authority"] = common.Map(resp.Ns, rr2Json)
|
||||
}
|
||||
if len(resp.Extra) > 0 {
|
||||
responseData["Additional"] = common.Map(resp.Extra, rr2Json)
|
||||
}
|
||||
|
||||
render.JSON(w, r, responseData)
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
r.Mount("/script", scriptRouter())
|
||||
r.Mount("/profile", profileRouter())
|
||||
r.Mount("/cache", cacheRouter())
|
||||
r.Mount("/dns", dnsRouter(router))
|
||||
})
|
||||
if options.ExternalUI != "" {
|
||||
chiRouter.Group(func(r chi.Router) {
|
||||
|
||||
24
experimental/ssmapi.go
Normal file
24
experimental/ssmapi.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 SSMServerConstructor = func(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error)
|
||||
|
||||
var ssmServerConstructor SSMServerConstructor
|
||||
|
||||
func RegisterSSMServerConstructor(constructor SSMServerConstructor) {
|
||||
ssmServerConstructor = constructor
|
||||
}
|
||||
|
||||
func NewSSMServer(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error) {
|
||||
if ssmServerConstructor == nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
return ssmServerConstructor(router, logger, options)
|
||||
}
|
||||
214
experimental/ssmapi/api.go
Normal file
214
experimental/ssmapi/api.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package ssmapi
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func (s *Server) setupRoutes(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get("/", s.getServerInfo)
|
||||
|
||||
r.Get("/nodes", s.getNodes)
|
||||
r.Post("/nodes", s.addNode)
|
||||
r.Get("/nodes/{id}", s.getNode)
|
||||
r.Put("/nodes/{id}", s.updateNode)
|
||||
r.Delete("/nodes/{id}", s.deleteNode)
|
||||
|
||||
r.Get("/users", s.listUser)
|
||||
r.Post("/users", s.addUser)
|
||||
r.Get("/users/{username}", s.getUser)
|
||||
r.Put("/users/{username}", s.updateUser)
|
||||
r.Delete("/users/{username}", s.deleteUser)
|
||||
|
||||
r.Get("/stats", s.getStats)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) getServerInfo(writer http.ResponseWriter, request *http.Request) {
|
||||
render.JSON(writer, request, render.M{
|
||||
"server": "sing-box",
|
||||
"apiVersion": "v1",
|
||||
"_sing_box_version": C.Version,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) getNodes(writer http.ResponseWriter, request *http.Request) {
|
||||
var response struct {
|
||||
Protocols []string `json:"protocols"`
|
||||
Shadowsocks []ShadowsocksNodeObject `json:"shadowsocks,omitempty"`
|
||||
}
|
||||
for _, node := range s.nodes {
|
||||
if !common.Contains(response.Protocols, node.Protocol()) {
|
||||
response.Protocols = append(response.Protocols, node.Protocol())
|
||||
}
|
||||
switch node.Protocol() {
|
||||
case C.TypeShadowsocks:
|
||||
response.Shadowsocks = append(response.Shadowsocks, node.Shadowsocks())
|
||||
}
|
||||
}
|
||||
render.JSON(writer, request, &response)
|
||||
}
|
||||
|
||||
func (s *Server) addNode(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
func (s *Server) getNode(writer http.ResponseWriter, request *http.Request) {
|
||||
nodeID := chi.URLParam(request, "id")
|
||||
if nodeID == "" {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
for _, node := range s.nodes {
|
||||
if nodeID == node.ID() {
|
||||
render.JSON(writer, request, render.M{
|
||||
node.Protocol(): node.Object(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) updateNode(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
func (s *Server) deleteNode(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
type SSMUserObject struct {
|
||||
UserName string `json:"username"`
|
||||
Password string `json:"uPSK,omitempty"`
|
||||
DownlinkBytes int64 `json:"downlinkBytes"`
|
||||
UplinkBytes int64 `json:"uplinkBytes"`
|
||||
|
||||
DownlinkPackets int64 `json:"downlinkPackets"`
|
||||
UplinkPackets int64 `json:"uplinkPackets"`
|
||||
TCPSessions int64 `json:"tcpSessions"`
|
||||
UDPSessions int64 `json:"udpSessions"`
|
||||
}
|
||||
|
||||
func (s *Server) listUser(writer http.ResponseWriter, request *http.Request) {
|
||||
render.JSON(writer, request, render.M{
|
||||
"users": s.userManager.List(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) addUser(writer http.ResponseWriter, request *http.Request) {
|
||||
var addRequest struct {
|
||||
UserName string `json:"username"`
|
||||
Password string `json:"uPSK"`
|
||||
}
|
||||
err := render.DecodeJSON(request.Body, &addRequest)
|
||||
if err != nil {
|
||||
render.Status(request, http.StatusBadRequest)
|
||||
render.PlainText(writer, request, err.Error())
|
||||
return
|
||||
}
|
||||
err = s.userManager.Add(addRequest.UserName, addRequest.Password)
|
||||
if err != nil {
|
||||
render.Status(request, http.StatusBadRequest)
|
||||
render.PlainText(writer, request, err.Error())
|
||||
return
|
||||
}
|
||||
writer.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
func (s *Server) getUser(writer http.ResponseWriter, request *http.Request) {
|
||||
userName := chi.URLParam(request, "username")
|
||||
if userName == "" {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
uPSK, loaded := s.userManager.Get(userName)
|
||||
if !loaded {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
user := SSMUserObject{
|
||||
UserName: userName,
|
||||
Password: uPSK,
|
||||
}
|
||||
s.trafficManager.ReadUser(&user)
|
||||
render.JSON(writer, request, user)
|
||||
}
|
||||
|
||||
func (s *Server) updateUser(writer http.ResponseWriter, request *http.Request) {
|
||||
userName := chi.URLParam(request, "username")
|
||||
if userName == "" {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var updateRequest struct {
|
||||
Password string `json:"uPSK"`
|
||||
}
|
||||
err := render.DecodeJSON(request.Body, &updateRequest)
|
||||
if err != nil {
|
||||
render.Status(request, http.StatusBadRequest)
|
||||
render.PlainText(writer, request, err.Error())
|
||||
return
|
||||
}
|
||||
_, loaded := s.userManager.Get(userName)
|
||||
if !loaded {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
err = s.userManager.Update(userName, updateRequest.Password)
|
||||
if err != nil {
|
||||
render.Status(request, http.StatusBadRequest)
|
||||
render.PlainText(writer, request, err.Error())
|
||||
return
|
||||
}
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Server) deleteUser(writer http.ResponseWriter, request *http.Request) {
|
||||
userName := chi.URLParam(request, "username")
|
||||
if userName == "" {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_, loaded := s.userManager.Get(userName)
|
||||
if !loaded {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
err := s.userManager.Delete(userName)
|
||||
if err != nil {
|
||||
render.Status(request, http.StatusBadRequest)
|
||||
render.PlainText(writer, request, err.Error())
|
||||
return
|
||||
}
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Server) getStats(writer http.ResponseWriter, request *http.Request) {
|
||||
requireClear := chi.URLParam(request, "clear") == "true"
|
||||
|
||||
users := s.userManager.List()
|
||||
s.trafficManager.ReadUsers(users)
|
||||
for i := range users {
|
||||
users[i].Password = ""
|
||||
}
|
||||
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.trafficManager.ReadGlobal()
|
||||
|
||||
if requireClear {
|
||||
s.trafficManager.Clear()
|
||||
}
|
||||
|
||||
render.JSON(writer, request, render.M{
|
||||
"uplinkBytes": uplinkBytes,
|
||||
"downlinkBytes": downlinkBytes,
|
||||
"uplinkPackets": uplinkPackets,
|
||||
"downlinkPackets": downlinkPackets,
|
||||
"tcpSessions": tcpSessions,
|
||||
"udpSessions": udpSessions,
|
||||
"users": users,
|
||||
})
|
||||
}
|
||||
117
experimental/ssmapi/server.go
Normal file
117
experimental/ssmapi/server.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package ssmapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func init() {
|
||||
experimental.RegisterSSMServerConstructor(NewServer)
|
||||
}
|
||||
|
||||
var _ adapter.SSMServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
router adapter.Router
|
||||
logger log.Logger
|
||||
httpServer *http.Server
|
||||
tcpListener net.Listener
|
||||
|
||||
nodes []Node
|
||||
userManager *UserManager
|
||||
trafficManager *TrafficManager
|
||||
}
|
||||
|
||||
type Node interface {
|
||||
Protocol() string
|
||||
ID() string
|
||||
Shadowsocks() ShadowsocksNodeObject
|
||||
Object() any
|
||||
Tag() string
|
||||
UpdateUsers(users []string, uPSKs []string) error
|
||||
}
|
||||
|
||||
func NewServer(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error) {
|
||||
chiRouter := chi.NewRouter()
|
||||
server := &Server{
|
||||
router: router,
|
||||
logger: logger,
|
||||
httpServer: &http.Server{
|
||||
Addr: options.Listen,
|
||||
Handler: chiRouter,
|
||||
},
|
||||
nodes: make([]Node, 0, len(options.Nodes)),
|
||||
}
|
||||
for i, nodeOptions := range options.Nodes {
|
||||
switch nodeOptions.Type {
|
||||
case C.TypeShadowsocks:
|
||||
ssOptions := nodeOptions.ShadowsocksOptions
|
||||
inbound, loaded := router.Inbound(ssOptions.Inbound)
|
||||
if !loaded {
|
||||
return nil, E.New("parse SSM node[", i, "]: inbound", ssOptions.Inbound, "not found")
|
||||
}
|
||||
ssInbound, isSS := inbound.(adapter.ManagedShadowsocksServer)
|
||||
if !isSS {
|
||||
return nil, E.New("parse SSM node[", i, "]: inbound", ssOptions.Inbound, "is not a shadowsocks inbound")
|
||||
}
|
||||
node := &ShadowsocksNode{
|
||||
ssOptions,
|
||||
ssInbound,
|
||||
}
|
||||
server.nodes = append(server.nodes, node)
|
||||
}
|
||||
}
|
||||
server.trafficManager = NewTrafficManager(server.nodes)
|
||||
server.userManager = NewUserManager(server.nodes, server.trafficManager)
|
||||
listenPrefix := options.ListenPrefix
|
||||
if !strings.HasPrefix(listenPrefix, "/") {
|
||||
listenPrefix = "/" + listenPrefix
|
||||
}
|
||||
chiRouter.Route(listenPrefix+"server/v1", server.setupRoutes)
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
listener, err := net.Listen("tcp", s.httpServer.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.logger.Info("ssm-api started at ", listener.Addr())
|
||||
s.tcpListener = listener
|
||||
go func() {
|
||||
err = s.httpServer.Serve(listener)
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
s.logger.Error("ssm-api serve error: ", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.httpServer),
|
||||
s.tcpListener,
|
||||
s.trafficManager,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) RoutedConnection(metadata adapter.InboundContext, conn net.Conn) net.Conn {
|
||||
return s.trafficManager.RoutedConnection(metadata, conn)
|
||||
}
|
||||
|
||||
func (s *Server) RoutedPacketConnection(metadata adapter.InboundContext, conn N.PacketConn) N.PacketConn {
|
||||
return s.trafficManager.RoutedPacketConnection(metadata, conn)
|
||||
}
|
||||
54
experimental/ssmapi/shadowsocks.go
Normal file
54
experimental/ssmapi/shadowsocks.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package ssmapi
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
var _ Node = (*ShadowsocksNode)(nil)
|
||||
|
||||
type ShadowsocksNode struct {
|
||||
node option.SSMShadowsocksNode
|
||||
inbound adapter.ManagedShadowsocksServer
|
||||
}
|
||||
|
||||
type ShadowsocksNodeObject struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Passwords []string `json:"iPSKs,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
func (n *ShadowsocksNode) Protocol() string {
|
||||
return C.TypeShadowsocks
|
||||
}
|
||||
|
||||
func (n *ShadowsocksNode) ID() string {
|
||||
return n.node.ID
|
||||
}
|
||||
|
||||
func (n *ShadowsocksNode) Shadowsocks() ShadowsocksNodeObject {
|
||||
return ShadowsocksNodeObject{
|
||||
ID: n.node.ID,
|
||||
Name: n.node.Name,
|
||||
Endpoint: n.node.Address,
|
||||
Method: n.inbound.Method(),
|
||||
Passwords: []string{n.inbound.Password()},
|
||||
Tags: n.node.Tags,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ShadowsocksNode) Object() any {
|
||||
return n.Shadowsocks()
|
||||
}
|
||||
|
||||
func (n *ShadowsocksNode) Tag() string {
|
||||
return n.inbound.Tag()
|
||||
}
|
||||
|
||||
func (n *ShadowsocksNode) UpdateUsers(users []string, uPSKs []string) error {
|
||||
return n.inbound.UpdateUsers(users, uPSKs)
|
||||
}
|
||||
227
experimental/ssmapi/traffic.go
Normal file
227
experimental/ssmapi/traffic.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package ssmapi
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/experimental/trackerconn"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
type TrafficManager struct {
|
||||
nodeTags map[string]bool
|
||||
nodeUsers map[string]bool
|
||||
globalUplink *atomic.Int64
|
||||
globalDownlink *atomic.Int64
|
||||
globalUplinkPackets *atomic.Int64
|
||||
globalDownlinkPackets *atomic.Int64
|
||||
globalTCPSessions *atomic.Int64
|
||||
globalUDPSessions *atomic.Int64
|
||||
userAccess sync.Mutex
|
||||
userUplink map[string]*atomic.Int64
|
||||
userDownlink map[string]*atomic.Int64
|
||||
userUplinkPackets map[string]*atomic.Int64
|
||||
userDownlinkPackets map[string]*atomic.Int64
|
||||
userTCPSessions map[string]*atomic.Int64
|
||||
userUDPSessions map[string]*atomic.Int64
|
||||
}
|
||||
|
||||
func NewTrafficManager(nodes []Node) *TrafficManager {
|
||||
manager := &TrafficManager{
|
||||
nodeTags: make(map[string]bool),
|
||||
globalUplink: atomic.NewInt64(0),
|
||||
globalDownlink: atomic.NewInt64(0),
|
||||
globalUplinkPackets: atomic.NewInt64(0),
|
||||
globalDownlinkPackets: atomic.NewInt64(0),
|
||||
globalTCPSessions: atomic.NewInt64(0),
|
||||
globalUDPSessions: atomic.NewInt64(0),
|
||||
userUplink: make(map[string]*atomic.Int64),
|
||||
userDownlink: make(map[string]*atomic.Int64),
|
||||
userUplinkPackets: make(map[string]*atomic.Int64),
|
||||
userDownlinkPackets: make(map[string]*atomic.Int64),
|
||||
userTCPSessions: make(map[string]*atomic.Int64),
|
||||
userUDPSessions: make(map[string]*atomic.Int64),
|
||||
}
|
||||
for _, node := range nodes {
|
||||
manager.nodeTags[node.Tag()] = true
|
||||
}
|
||||
return manager
|
||||
}
|
||||
|
||||
func (s *TrafficManager) UpdateUsers(users []string) {
|
||||
nodeUsers := make(map[string]bool)
|
||||
for _, user := range users {
|
||||
nodeUsers[user] = true
|
||||
}
|
||||
s.nodeUsers = nodeUsers
|
||||
}
|
||||
|
||||
func (s *TrafficManager) userCounter(user string) (*atomic.Int64, *atomic.Int64, *atomic.Int64, *atomic.Int64, *atomic.Int64, *atomic.Int64) {
|
||||
s.userAccess.Lock()
|
||||
defer s.userAccess.Unlock()
|
||||
upCounter, loaded := s.userUplink[user]
|
||||
if !loaded {
|
||||
upCounter = atomic.NewInt64(0)
|
||||
s.userUplink[user] = upCounter
|
||||
}
|
||||
downCounter, loaded := s.userDownlink[user]
|
||||
if !loaded {
|
||||
downCounter = atomic.NewInt64(0)
|
||||
s.userDownlink[user] = downCounter
|
||||
}
|
||||
upPacketsCounter, loaded := s.userUplinkPackets[user]
|
||||
if !loaded {
|
||||
upPacketsCounter = atomic.NewInt64(0)
|
||||
s.userUplinkPackets[user] = upPacketsCounter
|
||||
}
|
||||
downPacketsCounter, loaded := s.userDownlinkPackets[user]
|
||||
if !loaded {
|
||||
downPacketsCounter = atomic.NewInt64(0)
|
||||
s.userDownlinkPackets[user] = downPacketsCounter
|
||||
}
|
||||
tcpSessionsCounter, loaded := s.userTCPSessions[user]
|
||||
if !loaded {
|
||||
tcpSessionsCounter = atomic.NewInt64(0)
|
||||
s.userTCPSessions[user] = tcpSessionsCounter
|
||||
}
|
||||
udpSessionsCounter, loaded := s.userUDPSessions[user]
|
||||
if !loaded {
|
||||
udpSessionsCounter = atomic.NewInt64(0)
|
||||
s.userUDPSessions[user] = udpSessionsCounter
|
||||
}
|
||||
return upCounter, downCounter, upPacketsCounter, downPacketsCounter, tcpSessionsCounter, udpSessionsCounter
|
||||
}
|
||||
|
||||
func createCounter(counterList []*atomic.Int64, packetCounterList []*atomic.Int64) func(n int64) {
|
||||
return func(n int64) {
|
||||
for _, counter := range counterList {
|
||||
counter.Add(n)
|
||||
}
|
||||
for _, counter := range packetCounterList {
|
||||
counter.Inc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TrafficManager) RoutedConnection(metadata adapter.InboundContext, conn net.Conn) net.Conn {
|
||||
s.globalTCPSessions.Inc()
|
||||
|
||||
var readCounter []*atomic.Int64
|
||||
var writeCounter []*atomic.Int64
|
||||
|
||||
if s.nodeTags[metadata.Inbound] {
|
||||
readCounter = append(readCounter, s.globalUplink)
|
||||
writeCounter = append(writeCounter, s.globalDownlink)
|
||||
}
|
||||
if s.nodeUsers[metadata.User] {
|
||||
upCounter, downCounter, _, _, tcpSessionCounter, _ := s.userCounter(metadata.User)
|
||||
readCounter = append(readCounter, upCounter)
|
||||
writeCounter = append(writeCounter, downCounter)
|
||||
tcpSessionCounter.Inc()
|
||||
}
|
||||
if len(readCounter) > 0 {
|
||||
return trackerconn.New(conn, readCounter, writeCounter)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
func (s *TrafficManager) RoutedPacketConnection(metadata adapter.InboundContext, conn N.PacketConn) N.PacketConn {
|
||||
s.globalUDPSessions.Inc()
|
||||
|
||||
var readCounter []*atomic.Int64
|
||||
var readPacketCounter []*atomic.Int64
|
||||
var writeCounter []*atomic.Int64
|
||||
var writePacketCounter []*atomic.Int64
|
||||
|
||||
if s.nodeTags[metadata.Inbound] {
|
||||
readCounter = append(readCounter, s.globalUplink)
|
||||
writeCounter = append(writeCounter, s.globalDownlink)
|
||||
readPacketCounter = append(readPacketCounter, s.globalUplinkPackets)
|
||||
writePacketCounter = append(writePacketCounter, s.globalDownlinkPackets)
|
||||
}
|
||||
if s.nodeUsers[metadata.User] {
|
||||
upCounter, downCounter, upPacketsCounter, downPacketsCounter, _, udpSessionCounter := s.userCounter(metadata.User)
|
||||
readCounter = append(readCounter, upCounter)
|
||||
writeCounter = append(writeCounter, downCounter)
|
||||
readPacketCounter = append(readPacketCounter, upPacketsCounter)
|
||||
writePacketCounter = append(writePacketCounter, downPacketsCounter)
|
||||
udpSessionCounter.Inc()
|
||||
}
|
||||
if len(readCounter) > 0 {
|
||||
return trackerconn.NewHookPacket(conn, createCounter(readCounter, readPacketCounter), createCounter(writeCounter, writePacketCounter))
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
func (s *TrafficManager) ReadUser(user *SSMUserObject) {
|
||||
s.userAccess.Lock()
|
||||
defer s.userAccess.Unlock()
|
||||
|
||||
s.readUser(user)
|
||||
}
|
||||
|
||||
func (s *TrafficManager) readUser(user *SSMUserObject) {
|
||||
if counter, loaded := s.userUplink[user.UserName]; loaded {
|
||||
user.UplinkBytes = counter.Load()
|
||||
}
|
||||
if counter, loaded := s.userDownlink[user.UserName]; loaded {
|
||||
user.DownlinkBytes = counter.Load()
|
||||
}
|
||||
if counter, loaded := s.userUplinkPackets[user.UserName]; loaded {
|
||||
user.UplinkPackets = counter.Load()
|
||||
}
|
||||
if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded {
|
||||
user.DownlinkPackets = counter.Load()
|
||||
}
|
||||
if counter, loaded := s.userTCPSessions[user.UserName]; loaded {
|
||||
user.TCPSessions = counter.Load()
|
||||
}
|
||||
if counter, loaded := s.userUDPSessions[user.UserName]; loaded {
|
||||
user.UDPSessions = counter.Load()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TrafficManager) ReadUsers(users []*SSMUserObject) {
|
||||
s.userAccess.Lock()
|
||||
defer s.userAccess.Unlock()
|
||||
for _, user := range users {
|
||||
s.readUser(user)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *TrafficManager) ReadGlobal() (
|
||||
uplinkBytes int64,
|
||||
downlinkBytes int64,
|
||||
uplinkPackets int64,
|
||||
downlinkPackets int64,
|
||||
tcpSessions int64,
|
||||
udpSessions int64,
|
||||
) {
|
||||
return s.globalUplink.Load(),
|
||||
s.globalDownlink.Load(),
|
||||
s.globalUplinkPackets.Load(),
|
||||
s.globalDownlinkPackets.Load(),
|
||||
s.globalTCPSessions.Load(),
|
||||
s.globalUDPSessions.Load()
|
||||
}
|
||||
|
||||
func (s *TrafficManager) Clear() {
|
||||
s.globalUplink.Store(0)
|
||||
s.globalDownlink.Store(0)
|
||||
s.globalUplinkPackets.Store(0)
|
||||
s.globalDownlinkPackets.Store(0)
|
||||
s.globalTCPSessions.Store(0)
|
||||
s.globalUDPSessions.Store(0)
|
||||
s.userAccess.Lock()
|
||||
defer s.userAccess.Unlock()
|
||||
s.userUplink = make(map[string]*atomic.Int64)
|
||||
s.userDownlink = make(map[string]*atomic.Int64)
|
||||
s.userUplinkPackets = make(map[string]*atomic.Int64)
|
||||
s.userDownlinkPackets = make(map[string]*atomic.Int64)
|
||||
s.userTCPSessions = make(map[string]*atomic.Int64)
|
||||
s.userUDPSessions = make(map[string]*atomic.Int64)
|
||||
}
|
||||
86
experimental/ssmapi/user.go
Normal file
86
experimental/ssmapi/user.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package ssmapi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type UserManager struct {
|
||||
access sync.Mutex
|
||||
usersMap map[string]string
|
||||
nodes []Node
|
||||
trafficManager *TrafficManager
|
||||
}
|
||||
|
||||
func NewUserManager(nodes []Node, trafficManager *TrafficManager) *UserManager {
|
||||
return &UserManager{
|
||||
usersMap: make(map[string]string),
|
||||
nodes: nodes,
|
||||
trafficManager: trafficManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *UserManager) postUpdate() error {
|
||||
users := make([]string, 0, len(m.usersMap))
|
||||
uPSKs := make([]string, 0, len(m.usersMap))
|
||||
for username, password := range m.usersMap {
|
||||
users = append(users, username)
|
||||
uPSKs = append(uPSKs, password)
|
||||
}
|
||||
for _, node := range m.nodes {
|
||||
err := node.UpdateUsers(users, uPSKs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m.trafficManager.UpdateUsers(users)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserManager) List() []*SSMUserObject {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
|
||||
users := make([]*SSMUserObject, 0, len(m.usersMap))
|
||||
for username, password := range m.usersMap {
|
||||
users = append(users, &SSMUserObject{
|
||||
UserName: username,
|
||||
Password: password,
|
||||
})
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
func (m *UserManager) Add(username string, password string) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if _, found := m.usersMap[username]; found {
|
||||
return E.New("user", username, "already exists")
|
||||
}
|
||||
m.usersMap[username] = password
|
||||
return m.postUpdate()
|
||||
}
|
||||
|
||||
func (m *UserManager) Get(username string) (string, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if password, found := m.usersMap[username]; found {
|
||||
return password, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (m *UserManager) Update(username string, password string) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.usersMap[username] = password
|
||||
return m.postUpdate()
|
||||
}
|
||||
|
||||
func (m *UserManager) Delete(username string) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
delete(m.usersMap, username)
|
||||
return m.postUpdate()
|
||||
}
|
||||
@@ -31,6 +31,7 @@ type StatsService struct {
|
||||
createdAt time.Time
|
||||
inbounds map[string]bool
|
||||
outbounds map[string]bool
|
||||
users map[string]bool
|
||||
access sync.Mutex
|
||||
counters map[string]*atomic.Int64
|
||||
}
|
||||
@@ -41,26 +42,32 @@ func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
|
||||
}
|
||||
inbounds := make(map[string]bool)
|
||||
outbounds := make(map[string]bool)
|
||||
users := make(map[string]bool)
|
||||
for _, inbound := range options.Inbounds {
|
||||
inbounds[inbound] = true
|
||||
}
|
||||
for _, outbound := range options.Outbounds {
|
||||
outbounds[outbound] = true
|
||||
}
|
||||
for _, user := range options.Users {
|
||||
users[user] = true
|
||||
}
|
||||
return &StatsService{
|
||||
createdAt: time.Now(),
|
||||
inbounds: inbounds,
|
||||
outbounds: outbounds,
|
||||
users: users,
|
||||
counters: make(map[string]*atomic.Int64),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StatsService) RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn {
|
||||
func (s *StatsService) RoutedConnection(inbound string, outbound string, user 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 {
|
||||
countUser := user != "" && s.users[user]
|
||||
if !countInbound && !countOutbound && !countUser {
|
||||
return conn
|
||||
}
|
||||
s.access.Lock()
|
||||
@@ -72,16 +79,21 @@ func (s *StatsService) RoutedConnection(inbound string, outbound string, conn ne
|
||||
readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
|
||||
}
|
||||
if countUser {
|
||||
readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
||||
}
|
||||
s.access.Unlock()
|
||||
return trackerconn.New(conn, readCounter, writeCounter)
|
||||
}
|
||||
|
||||
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn {
|
||||
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, user 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 {
|
||||
countUser := user != "" && s.users[user]
|
||||
if !countInbound && !countOutbound && !countUser {
|
||||
return conn
|
||||
}
|
||||
s.access.Lock()
|
||||
@@ -93,6 +105,10 @@ func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, c
|
||||
readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
|
||||
}
|
||||
if countUser {
|
||||
readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
||||
}
|
||||
s.access.Unlock()
|
||||
return trackerconn.NewPacket(conn, readCounter, writeCounter)
|
||||
}
|
||||
|
||||
31
go.mod
31
go.mod
@@ -7,7 +7,6 @@ require (
|
||||
github.com/Dreamacro/clash v1.13.0
|
||||
github.com/caddyserver/certmagic v0.17.2
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/database64128/tfo-go/v2 v2.0.2
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
@@ -15,20 +14,22 @@ require (
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.0.4
|
||||
github.com/mholt/acmez v1.1.0
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/refraction-networking/utls v1.2.0
|
||||
github.com/refraction-networking/utls v1.2.2
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||
github.com/sagernet/sing v0.1.6-0.20230114115804-bc788b027182
|
||||
github.com/sagernet/sing-dns v0.1.3
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
|
||||
github.com/sagernet/sing-tun v0.1.1
|
||||
github.com/sagernet/sing-vmess v0.1.1
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
||||
github.com/spf13/cobra v1.6.1
|
||||
@@ -37,11 +38,11 @@ require (
|
||||
go.uber.org/atomic v1.10.0
|
||||
go.uber.org/zap v1.24.0
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f
|
||||
golang.org/x/crypto v0.5.0
|
||||
golang.org/x/exp v0.0.0-20230105202349-8879d0199aa3
|
||||
golang.org/x/net v0.5.0
|
||||
golang.org/x/sys v0.4.0
|
||||
google.golang.org/grpc v1.52.3
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/sys v0.5.0
|
||||
google.golang.org/grpc v1.53.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||
)
|
||||
@@ -59,7 +60,8 @@ require (
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
@@ -72,13 +74,14 @@ require (
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
|
||||
124
go.sum
124
go.sum
@@ -7,7 +7,6 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
||||
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@@ -19,13 +18,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
||||
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||
github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg=
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
@@ -45,21 +43,37 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
|
||||
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -67,8 +81,15 @@ github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
|
||||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
|
||||
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
@@ -80,7 +101,6 @@ github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd918
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -92,8 +112,8 @@ github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/refraction-networking/utls v1.2.0 h1:U5f8wkij2NVinfLuJdFP3gCMwIHs+EzvhxmYdXgiapo=
|
||||
github.com/refraction-networking/utls v1.2.0/go.mod h1:NPq+cVqzH7D1BeOkmOcb5O/8iVewAsiVt2x1/eO0hgQ=
|
||||
github.com/refraction-networking/utls v1.2.2 h1:uBE6V173CwG8MQrSBpNZHAix1fxOvuLKYyjFAu3uqo0=
|
||||
github.com/refraction-networking/utls v1.2.2/go.mod h1:L1goe44KvhnTfctUffM2isnJpSjPlYShrhXDeZaoYKw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||
@@ -105,22 +125,26 @@ github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRK
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||
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.1.6-0.20230114115804-bc788b027182 h1:TsLXlVXH7ql2yHOYczF6hfHrrAiMiS6itVG0HMJKJ08=
|
||||
github.com/sagernet/sing v0.1.6-0.20230114115804-bc788b027182/go.mod h1:JLSXsPTGRJFo/3X7EcAOCUgJH2/gAoxSJgBsnCZRp/w=
|
||||
github.com/sagernet/sing-dns v0.1.3 h1:PbBK7wqkvnwGguhWtTV88fKvSUUr62+ENWs234Ddvns=
|
||||
github.com/sagernet/sing-dns v0.1.3/go.mod h1:+lFb7GBtFcuyt/mbf9M4Eal8xbycIhjBepGB+rckmBk=
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13 h1:S/+YvJCEChwnckGhzqSrE/Q2m6aVWhkt1I4Pv2yCMVw=
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455 h1:VA/j2Jg+JURgKw2C1Dw2tpjfOZwbLXRy8PJRbJS/HEU=
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455/go.mod h1:nonvn66ja+UNrQl3jzJy0EFRn15QUaCFAVXTXf6TgJ4=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7 h1:Plup6oEiyLzY3HDqQ+QsUBzgBGdVmcsgf3t8h940z9U=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7/go.mod h1:O5LtOs8Ivw686FqLpO0Zu+A0ROVE15VeqEK3yDRRAms=
|
||||
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
||||
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
||||
github.com/sagernet/sing-vmess v0.1.1 h1:WMdkJcc3icIqpDQZGQ7X+jfLilooIZ0zAaC0qeQTWFU=
|
||||
github.com/sagernet/sing-vmess v0.1.1/go.mod h1:COSSEmy19vMWOTEKIUSDiTEyx6yBfTYIzekDlCMow+Q=
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM=
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
|
||||
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/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -130,11 +154,14 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
@@ -144,10 +171,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
|
||||
@@ -155,61 +180,73 @@ go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20230105202349-8879d0199aa3 h1:fJwx88sMf5RXwDwziL0/Mn9Wqs+efMSo/RYcL+37W9c=
|
||||
golang.org/x/exp v0.0.0-20230105202349-8879d0199aa3/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
@@ -217,22 +254,19 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c=
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||
google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ=
|
||||
google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
|
||||
|
||||
@@ -11,8 +11,7 @@ import (
|
||||
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"
|
||||
"github.com/sagernet/tfo-go"
|
||||
)
|
||||
|
||||
func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
||||
|
||||
@@ -22,7 +22,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
if len(options.Users) > 0 && len(options.Destinations) > 0 {
|
||||
return nil, E.New("users and destinations options must not be combined")
|
||||
}
|
||||
if len(options.Users) > 0 {
|
||||
if len(options.Users) > 0 || options.Managed {
|
||||
return newShadowsocksMulti(ctx, router, logger, tag, options)
|
||||
} else if len(options.Destinations) > 0 {
|
||||
return newShadowsocksRelay(ctx, router, logger, tag, options)
|
||||
@@ -32,8 +32,9 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
}
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*Shadowsocks)(nil)
|
||||
_ adapter.InjectableInbound = (*Shadowsocks)(nil)
|
||||
_ adapter.Inbound = (*Shadowsocks)(nil)
|
||||
_ adapter.InjectableInbound = (*Shadowsocks)(nil)
|
||||
_ adapter.ManagedShadowsocksServer = (*Shadowsocks)(nil)
|
||||
)
|
||||
|
||||
type Shadowsocks struct {
|
||||
@@ -76,6 +77,18 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) Method() string {
|
||||
return h.service.Name()
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) Password() string {
|
||||
return h.service.Password()
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) UpdateUsers(names []string, uPSKs []string) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*ShadowsocksMulti)(nil)
|
||||
_ adapter.InjectableInbound = (*ShadowsocksMulti)(nil)
|
||||
_ adapter.Inbound = (*ShadowsocksMulti)(nil)
|
||||
_ adapter.InjectableInbound = (*ShadowsocksMulti)(nil)
|
||||
_ adapter.ManagedShadowsocksServer = (*ShadowsocksMulti)(nil)
|
||||
)
|
||||
|
||||
type ShadowsocksMulti struct {
|
||||
@@ -61,11 +62,13 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = service.UpdateUsersWithPasswords(common.MapIndexed(options.Users, func(index int, user option.ShadowsocksUser) int {
|
||||
return index
|
||||
}), common.Map(options.Users, func(user option.ShadowsocksUser) string {
|
||||
return user.Password
|
||||
}))
|
||||
if len(options.Users) > 0 {
|
||||
err = service.UpdateUsersWithPasswords(common.MapIndexed(options.Users, func(index int, user option.ShadowsocksUser) int {
|
||||
return index
|
||||
}), common.Map(options.Users, func(user option.ShadowsocksUser) string {
|
||||
return user.Password
|
||||
}))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -75,6 +78,29 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) Method() string {
|
||||
return h.service.Name()
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) Password() string {
|
||||
return h.service.Password()
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) UpdateUsers(users []string, uPSKs []string) error {
|
||||
err := h.service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user string) int {
|
||||
return index
|
||||
}), uPSKs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.users = common.Map(users, func(user string) option.ShadowsocksUser {
|
||||
return option.ShadowsocksUser{
|
||||
Name: user,
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*ShadowsocksRelay)(nil)
|
||||
_ adapter.InjectableInbound = (*ShadowsocksRelay)(nil)
|
||||
_ adapter.Inbound = (*ShadowsocksRelay)(nil)
|
||||
_ adapter.InjectableInbound = (*ShadowsocksRelay)(nil)
|
||||
_ adapter.ManagedShadowsocksServer = (*ShadowsocksRelay)(nil)
|
||||
)
|
||||
|
||||
type ShadowsocksRelay struct {
|
||||
@@ -71,6 +72,18 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) Method() string {
|
||||
return h.service.Name()
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) Password() string {
|
||||
return h.service.Password()
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) UpdateUsers(users []string, uPSKs []string) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ package inbound
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
@@ -27,7 +30,7 @@ type ShadowTLS struct {
|
||||
myInboundAdapter
|
||||
handshakeDialer N.Dialer
|
||||
handshakeAddr M.Socksaddr
|
||||
v2 bool
|
||||
version int
|
||||
password string
|
||||
fallbackAfter int
|
||||
}
|
||||
@@ -47,17 +50,18 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
handshakeAddr: options.Handshake.ServerOptions.Build(),
|
||||
password: options.Password,
|
||||
}
|
||||
inbound.version = options.Version
|
||||
switch options.Version {
|
||||
case 0:
|
||||
fallthrough
|
||||
case 1:
|
||||
case 2:
|
||||
inbound.v2 = true
|
||||
if options.FallbackAfter == nil {
|
||||
inbound.fallbackAfter = 2
|
||||
} else {
|
||||
inbound.fallbackAfter = *options.FallbackAfter
|
||||
}
|
||||
case 3:
|
||||
default:
|
||||
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
|
||||
}
|
||||
@@ -70,7 +74,8 @@ func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata a
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !s.v2 {
|
||||
switch s.version {
|
||||
case 1:
|
||||
var handshake task.Group
|
||||
handshake.Append("client handshake", func(ctx context.Context) error {
|
||||
return s.copyUntilHandshakeFinished(handshakeConn, conn)
|
||||
@@ -87,7 +92,7 @@ func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata a
|
||||
return err
|
||||
}
|
||||
return s.newConnection(ctx, conn, metadata)
|
||||
} else {
|
||||
case 2:
|
||||
hashConn := shadowtls.NewHashWriteConn(conn, s.password)
|
||||
go bufio.Copy(hashConn, handshakeConn)
|
||||
var request *buf.Buffer
|
||||
@@ -102,6 +107,97 @@ func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata a
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
fallthrough
|
||||
case 3:
|
||||
var clientHelloFrame *buf.Buffer
|
||||
clientHelloFrame, err = shadowtls.ExtractFrame(conn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read client handshake")
|
||||
}
|
||||
_, err = handshakeConn.Write(clientHelloFrame.Bytes())
|
||||
if err != nil {
|
||||
clientHelloFrame.Release()
|
||||
return E.Cause(err, "write client handshake")
|
||||
}
|
||||
err = shadowtls.VerifyClientHello(clientHelloFrame.Bytes(), s.password)
|
||||
if err != nil {
|
||||
s.logger.WarnContext(ctx, E.Cause(err, "client hello verify failed"))
|
||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
||||
}
|
||||
s.logger.TraceContext(ctx, "client hello verify success")
|
||||
clientHelloFrame.Release()
|
||||
|
||||
var serverHelloFrame *buf.Buffer
|
||||
serverHelloFrame, err = shadowtls.ExtractFrame(handshakeConn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read server handshake")
|
||||
}
|
||||
|
||||
_, err = conn.Write(serverHelloFrame.Bytes())
|
||||
if err != nil {
|
||||
serverHelloFrame.Release()
|
||||
return E.Cause(err, "write server handshake")
|
||||
}
|
||||
|
||||
serverRandom := shadowtls.ExtractServerRandom(serverHelloFrame.Bytes())
|
||||
|
||||
if serverRandom == nil {
|
||||
s.logger.WarnContext(ctx, "server random extract failed, will copy bidirectional")
|
||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
||||
}
|
||||
|
||||
if !shadowtls.IsServerHelloSupportTLS13(serverHelloFrame.Bytes()) {
|
||||
s.logger.WarnContext(ctx, "TLS 1.3 is not supported, will copy bidirectional")
|
||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
||||
}
|
||||
|
||||
serverHelloFrame.Release()
|
||||
s.logger.TraceContext(ctx, "client authenticated. server random extracted: ", hex.EncodeToString(serverRandom))
|
||||
|
||||
hmacWrite := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacWrite.Write(serverRandom)
|
||||
|
||||
hmacAdd := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacAdd.Write(serverRandom)
|
||||
hmacAdd.Write([]byte("S"))
|
||||
|
||||
hmacVerify := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacVerifyReset := func() {
|
||||
hmacVerify.Reset()
|
||||
hmacVerify.Write(serverRandom)
|
||||
hmacVerify.Write([]byte("C"))
|
||||
}
|
||||
|
||||
var clientFirstFrame *buf.Buffer
|
||||
var group task.Group
|
||||
var handshakeFinished bool
|
||||
group.Append("client handshake relay", func(ctx context.Context) error {
|
||||
clientFrame, cErr := shadowtls.CopyByFrameUntilHMACMatches(conn, handshakeConn, hmacVerify, hmacVerifyReset)
|
||||
if cErr == nil {
|
||||
clientFirstFrame = clientFrame
|
||||
handshakeFinished = true
|
||||
handshakeConn.Close()
|
||||
}
|
||||
return cErr
|
||||
})
|
||||
group.Append("server handshake relay", func(ctx context.Context) error {
|
||||
cErr := shadowtls.CopyByFrameWithModification(handshakeConn, conn, s.password, serverRandom, hmacWrite)
|
||||
if E.IsClosedOrCanceled(cErr) && handshakeFinished {
|
||||
return nil
|
||||
}
|
||||
return cErr
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
handshakeConn.Close()
|
||||
})
|
||||
err = group.Run(ctx)
|
||||
if err != nil {
|
||||
return E.Cause(err, "handshake relay")
|
||||
}
|
||||
|
||||
s.logger.TraceContext(ctx, "handshake relay finished")
|
||||
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewVerifiedConn(conn, hmacAdd, hmacVerify, nil), clientFirstFrame), metadata)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
return nil, err
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*trojanTransportHandler)(inbound))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
@@ -216,3 +216,21 @@ func (h *Trojan) newPacketConnection(ctx context.Context, conn N.PacketConn, met
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
var _ adapter.V2RayServerTransportHandler = (*trojanTransportHandler)(nil)
|
||||
|
||||
type trojanTransportHandler Trojan
|
||||
|
||||
func (t *trojanTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*Trojan)(t).newTransportConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *trojanTransportHandler) FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*Trojan)(t).fallbackConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -49,7 +49,11 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
ctx: ctx,
|
||||
users: options.Users,
|
||||
}
|
||||
service := vmess.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
var serviceOptions []vmess.ServiceOption
|
||||
if options.Transport != nil && options.Transport.Type != "" {
|
||||
serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
|
||||
}
|
||||
service := vmess.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), serviceOptions...)
|
||||
inbound.service = service
|
||||
err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.VMessUser) int {
|
||||
return index
|
||||
@@ -68,7 +72,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vmessTransportHandler)(inbound))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
@@ -179,3 +183,18 @@ func (h *VMess) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
|
||||
}
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
var _ adapter.V2RayServerTransportHandler = (*vmessTransportHandler)(nil)
|
||||
|
||||
type vmessTransportHandler VMess
|
||||
|
||||
func (t *vmessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*VMess)(t).newTransportConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *vmessTransportHandler) FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
5
include/dhcp.go
Normal file
5
include/dhcp.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build with_dhcp
|
||||
|
||||
package include
|
||||
|
||||
import _ "github.com/sagernet/sing-box/transport/dhcp"
|
||||
18
include/dhcp_stub.go
Normal file
18
include/dhcp_stub.go
Normal file
@@ -0,0 +1,18 @@
|
||||
//go:build !with_dhcp
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"dhcp"}, func(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
|
||||
})
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -24,7 +23,7 @@ func init() {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
})
|
||||
v2ray.RegisterQUICConstructor(
|
||||
func(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
func(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
},
|
||||
func(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
|
||||
|
||||
5
include/ssmapi.go
Normal file
5
include/ssmapi.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build with_ssm_api
|
||||
|
||||
package include
|
||||
|
||||
import _ "github.com/sagernet/sing-box/experimental/ssmapi"
|
||||
17
include/ssmapi_stub.go
Normal file
17
include/ssmapi_stub.go
Normal file
@@ -0,0 +1,17 @@
|
||||
//go:build !with_ssm_api
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/experimental"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func init() {
|
||||
experimental.RegisterSSMServerConstructor(func(router adapter.Router, logger log.Logger, options option.SSMAPIOptions) (adapter.SSMServer, error) {
|
||||
return nil, E.New(`SSM api is not included in this build, rebuild with -tags with_ssm_api`)
|
||||
})
|
||||
}
|
||||
@@ -77,32 +77,33 @@ func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
|
||||
}
|
||||
|
||||
type DefaultDNSRule struct {
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network string `json:"network,omitempty"`
|
||||
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol Listable[string] `json:"protocol,omitempty"`
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network string `json:"network,omitempty"`
|
||||
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol Listable[string] `json:"protocol,omitempty"`
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
}
|
||||
|
||||
func (r DefaultDNSRule) IsValid() bool {
|
||||
|
||||
@@ -3,4 +3,5 @@ package option
|
||||
type ExperimentalOptions struct {
|
||||
ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"`
|
||||
V2RayAPI *V2RayAPIOptions `json:"v2ray_api,omitempty"`
|
||||
SSMAPI *SSMAPIOptions `json:"ssm_api,omitempty"`
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ type ShadowsocksInboundOptions struct {
|
||||
Password string `json:"password"`
|
||||
Users []ShadowsocksUser `json:"users,omitempty"`
|
||||
Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
|
||||
Managed bool `json:"managed,omitempty"`
|
||||
}
|
||||
|
||||
type ShadowsocksUser struct {
|
||||
|
||||
@@ -8,6 +8,7 @@ type SSHOutboundOptions struct {
|
||||
PrivateKey string `json:"private_key,omitempty"`
|
||||
PrivateKeyPath string `json:"private_key_path,omitempty"`
|
||||
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
|
||||
HostKey Listable[string] `json:"host_key,omitempty"`
|
||||
HostKeyAlgorithms Listable[string] `json:"host_key_algorithms,omitempty"`
|
||||
ClientVersion string `json:"client_version,omitempty"`
|
||||
}
|
||||
|
||||
54
option/ssmapi.go
Normal file
54
option/ssmapi.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type SSMAPIOptions struct {
|
||||
Listen string `json:"listen,omitempty"`
|
||||
ListenPrefix string `json:"listen_prefix,omitempty"`
|
||||
Nodes []SSMNode `json:"nodes,omitempty"`
|
||||
}
|
||||
|
||||
type _SSMNode struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
ShadowsocksOptions SSMShadowsocksNode `json:"-"`
|
||||
}
|
||||
|
||||
type SSMNode _SSMNode
|
||||
|
||||
func (h SSMNode) MarshalJSON() ([]byte, error) {
|
||||
var v any
|
||||
switch h.Type {
|
||||
case C.TypeShadowsocks:
|
||||
v = h.ShadowsocksOptions
|
||||
default:
|
||||
return nil, E.New("unknown ssm node type: ", h.Type)
|
||||
}
|
||||
return MarshallObjects((_SSMNode)(h), v)
|
||||
}
|
||||
|
||||
func (h *SSMNode) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_SSMNode)(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var v any
|
||||
switch h.Type {
|
||||
case C.TypeShadowsocks:
|
||||
v = &h.ShadowsocksOptions
|
||||
default:
|
||||
return E.New("unknown ssm node type: ", h.Type)
|
||||
}
|
||||
return UnmarshallExcluded(data, (*_SSMNode)(h), v)
|
||||
}
|
||||
|
||||
type SSMShadowsocksNode struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
Tags []string `json:"tags"`
|
||||
Inbound string `json:"inbound"`
|
||||
}
|
||||
@@ -8,7 +8,10 @@ import (
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type ListenAddress netip.Addr
|
||||
@@ -187,3 +190,40 @@ func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
|
||||
func (p ListenPrefix) Build() netip.Prefix {
|
||||
return netip.Prefix(p)
|
||||
}
|
||||
|
||||
type DNSQueryType uint16
|
||||
|
||||
func (t DNSQueryType) MarshalJSON() ([]byte, error) {
|
||||
typeName, loaded := mDNS.TypeToString[uint16(t)]
|
||||
if loaded {
|
||||
return json.Marshal(typeName)
|
||||
}
|
||||
return json.Marshal(uint16(t))
|
||||
}
|
||||
|
||||
func (t *DNSQueryType) UnmarshalJSON(bytes []byte) error {
|
||||
var valueNumber uint16
|
||||
err := json.Unmarshal(bytes, &valueNumber)
|
||||
if err == nil {
|
||||
*t = DNSQueryType(valueNumber)
|
||||
return nil
|
||||
}
|
||||
var valueString string
|
||||
err = json.Unmarshal(bytes, &valueString)
|
||||
if err == nil {
|
||||
queryType, loaded := mDNS.StringToType[valueString]
|
||||
if loaded {
|
||||
*t = DNSQueryType(queryType)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return E.New("unknown DNS query type: ", string(bytes))
|
||||
}
|
||||
|
||||
func DNSQueryTypeToString(queryType uint16) string {
|
||||
typeName, loaded := mDNS.TypeToString[queryType]
|
||||
if loaded {
|
||||
return typeName
|
||||
}
|
||||
return F.ToString(queryType)
|
||||
}
|
||||
|
||||
@@ -9,4 +9,5 @@ type V2RayStatsServiceOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Inbounds []string `json:"inbounds,omitempty"`
|
||||
Outbounds []string `json:"outbounds,omitempty"`
|
||||
Users []string `json:"users,omitempty"`
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package outbound
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"math/rand"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
|
||||
@@ -2,6 +2,8 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
@@ -25,7 +27,7 @@ type ShadowTLS struct {
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
v2 bool
|
||||
version int
|
||||
password string
|
||||
}
|
||||
|
||||
@@ -45,6 +47,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
outbound.version = options.Version
|
||||
switch options.Version {
|
||||
case 0:
|
||||
fallthrough
|
||||
@@ -52,12 +55,18 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
options.TLS.MinVersion = "1.2"
|
||||
options.TLS.MaxVersion = "1.2"
|
||||
case 2:
|
||||
outbound.v2 = true
|
||||
case 3:
|
||||
options.TLS.MinVersion = "1.3"
|
||||
options.TLS.MaxVersion = "1.3"
|
||||
default:
|
||||
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
|
||||
}
|
||||
var err error
|
||||
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if options.Version != 3 {
|
||||
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
} else {
|
||||
outbound.tlsConfig, err = shadowtls.NewClientTLSConfig(options.Server, common.PtrValueOrDefault(options.TLS), options.Password)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -74,19 +83,42 @@ func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !s.v2 {
|
||||
switch s.version {
|
||||
default:
|
||||
fallthrough
|
||||
case 1:
|
||||
_, err = tls.ClientHandshake(ctx, conn, s.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
} else {
|
||||
case 2:
|
||||
hashConn := shadowtls.NewHashReadConn(conn, s.password)
|
||||
_, err = tls.ClientHandshake(ctx, hashConn, s.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return shadowtls.NewClientConn(hashConn), nil
|
||||
case 3:
|
||||
streamWrapper := shadowtls.NewStreamWrapper(conn, s.password)
|
||||
_, err = tls.ClientHandshake(ctx, streamWrapper, s.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, serverRandom, readHMAC := streamWrapper.Authorized()
|
||||
if !authorized {
|
||||
return nil, E.New("traffic hijacked or TLS1.3 is not supported")
|
||||
}
|
||||
|
||||
hmacAdd := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacAdd.Write(serverRandom)
|
||||
hmacAdd.Write([]byte("C"))
|
||||
|
||||
hmacVerify := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacVerify.Write(serverRandom)
|
||||
hmacVerify.Write([]byte("S"))
|
||||
|
||||
return shadowtls.NewVerifiedConn(conn, hmacAdd, hmacVerify, readHMAC), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
@@ -32,6 +34,7 @@ type SSH struct {
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
user string
|
||||
hostKey []ssh.PublicKey
|
||||
hostKeyAlgorithms []string
|
||||
clientVersion string
|
||||
authMethod []ssh.AuthMethod
|
||||
@@ -91,6 +94,15 @@ func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
}
|
||||
outbound.authMethod = append(outbound.authMethod, ssh.PublicKeys(signer))
|
||||
}
|
||||
if len(options.HostKey) > 0 {
|
||||
for _, hostKey := range options.HostKey {
|
||||
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(hostKey))
|
||||
if err != nil {
|
||||
return nil, E.New("parse host key ", key)
|
||||
}
|
||||
outbound.hostKey = append(outbound.hostKey, key)
|
||||
}
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
@@ -126,7 +138,16 @@ func (s *SSH) connect() (*ssh.Client, error) {
|
||||
ClientVersion: s.clientVersion,
|
||||
HostKeyAlgorithms: s.hostKeyAlgorithms,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
if len(s.hostKey) == 0 {
|
||||
return nil
|
||||
}
|
||||
serverKey := key.Marshal()
|
||||
for _, hostKey := range s.hostKey {
|
||||
if bytes.Equal(serverKey, hostKey.Marshal()) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return E.New("host key mismatch, server send ", key.Type(), " ", base64.StdEncoding.EncodeToString(serverKey))
|
||||
},
|
||||
}
|
||||
clientConn, chans, reqs, err := ssh.NewClientConn(conn, s.serverAddr.Addr.String(), config)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir "/var/lib/sing-box"
|
||||
mkdir -p /var/lib/sing-box
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -rf "/var/lib/sing-box"
|
||||
rm -rf /var/lib/sing-box
|
||||
@@ -5,8 +5,8 @@ After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/lib/sing-box
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/sing-box run -c /etc/sing-box/config.json
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
@@ -5,8 +5,8 @@ After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/lib/sing-box-%i
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/sing-box run -c /etc/sing-box/%i.json
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
@@ -14,6 +14,7 @@ go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_wireguar
|
||||
popd
|
||||
|
||||
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
|
||||
sudo mkdir -p /var/lib/sing-box
|
||||
sudo mkdir -p /usr/local/etc/sing-box
|
||||
sudo cp $PROJECT/release/config/config.json /usr/local/etc/sing-box/config.json
|
||||
sudo cp $DIR/sing-box.service /etc/systemd/system
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.19.3.linux-amd64.tar.gz
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.20.linux-amd64.tar.gz
|
||||
sudo rm -rf /usr/local/go
|
||||
sudo tar -C /usr/local -xzf go.tar.gz
|
||||
rm go.tar.gz
|
||||
@@ -5,8 +5,8 @@ After=network.target nss-lookup.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/lib/sing-box
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/local/bin/sing-box run -c /usr/local/etc/sing-box/config.json
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sudo systemctl stop sing-box
|
||||
sudo rm -rf /var/lib/sing-box
|
||||
sudo rm -rf /usr/local/bin/sing-box
|
||||
sudo rm -rf /usr/local/etc/sing-box
|
||||
sudo rm -rf /etc/systemd/system/sing-box.service
|
||||
|
||||
101
route/router.go
101
route/router.go
@@ -97,6 +97,7 @@ type Router struct {
|
||||
processSearcher process.Searcher
|
||||
clashServer adapter.ClashServer
|
||||
v2rayServer adapter.V2RayServer
|
||||
ssmServer adapter.SSMServer
|
||||
}
|
||||
|
||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) {
|
||||
@@ -159,6 +160,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||
transportTags[i] = tag
|
||||
transportTagMap[tag] = true
|
||||
}
|
||||
ctx = adapter.ContextWithRouter(ctx, router)
|
||||
for {
|
||||
lastLen := len(dummyTransportMap)
|
||||
for i, server := range dnsOptions.Servers {
|
||||
@@ -173,7 +175,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||
detour = dialer.NewDetour(router, server.Detour)
|
||||
}
|
||||
switch server.Address {
|
||||
case "local", "rcode":
|
||||
case "local":
|
||||
default:
|
||||
serverURL, _ := url.Parse(server.Address)
|
||||
var serverAddress string
|
||||
@@ -193,11 +195,15 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if notIpAddress != nil && (serverURL == nil || serverURL.Scheme != "rcode") {
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
} else if notIpAddress != nil {
|
||||
switch serverURL.Scheme {
|
||||
case "rcode", "dhcp":
|
||||
default:
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
}
|
||||
}
|
||||
}
|
||||
transport, err := dns.CreateTransport(ctx, logFactory.NewLogger(F.ToString("dns/transport[", i, "]")), detour, server.Address)
|
||||
transport, err := dns.CreateTransport(ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||
}
|
||||
@@ -375,10 +381,28 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) Inbound(tag string) (adapter.Inbound, bool) {
|
||||
inbound, loaded := r.inboundByTag[tag]
|
||||
return inbound, loaded
|
||||
}
|
||||
|
||||
func (r *Router) Outbounds() []adapter.Outbound {
|
||||
return r.outbounds
|
||||
}
|
||||
|
||||
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
|
||||
outbound, loaded := r.outboundByTag[tag]
|
||||
return outbound, loaded
|
||||
}
|
||||
|
||||
func (r *Router) DefaultOutbound(network string) adapter.Outbound {
|
||||
if network == N.NetworkTCP {
|
||||
return r.defaultOutboundForConnection
|
||||
} else {
|
||||
return r.defaultOutboundForPacketConnection
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) Start() error {
|
||||
if r.needGeoIPDatabase {
|
||||
err := r.prepareGeoIPDatabase()
|
||||
@@ -392,14 +416,20 @@ func (r *Router) Start() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, rule := range r.rules {
|
||||
err := rule.Start()
|
||||
if r.interfaceMonitor != nil {
|
||||
err := r.interfaceMonitor.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, rule := range r.dnsRules {
|
||||
err := rule.Start()
|
||||
if r.networkMonitor != nil {
|
||||
err := r.networkMonitor.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if r.packageManager != nil {
|
||||
err := r.packageManager.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -424,22 +454,22 @@ func (r *Router) Start() error {
|
||||
r.geositeCache = nil
|
||||
r.geositeReader = nil
|
||||
}
|
||||
if r.interfaceMonitor != nil {
|
||||
err := r.interfaceMonitor.Start()
|
||||
for i, rule := range r.rules {
|
||||
err := rule.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
return E.Cause(err, "initialize rule[", i, "]")
|
||||
}
|
||||
}
|
||||
if r.networkMonitor != nil {
|
||||
err := r.networkMonitor.Start()
|
||||
for i, rule := range r.dnsRules {
|
||||
err := rule.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
return E.Cause(err, "initialize DNS rule[", i, "]")
|
||||
}
|
||||
}
|
||||
if r.packageManager != nil {
|
||||
err := r.packageManager.Start()
|
||||
for i, transport := range r.transports {
|
||||
err := transport.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
return E.Cause(err, "initialize DNS server[", i, "]")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -458,6 +488,12 @@ func (r *Router) Close() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, transport := range r.transports {
|
||||
err := transport.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return common.Close(
|
||||
common.PtrOrNil(r.geoIPReader),
|
||||
r.interfaceMonitor,
|
||||
@@ -487,19 +523,6 @@ func (r *Router) LoadGeosite(code string) (adapter.Rule, error) {
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
|
||||
outbound, loaded := r.outboundByTag[tag]
|
||||
return outbound, loaded
|
||||
}
|
||||
|
||||
func (r *Router) DefaultOutbound(network string) adapter.Outbound {
|
||||
if network == N.NetworkTCP {
|
||||
return r.defaultOutboundForConnection
|
||||
} else {
|
||||
return r.defaultOutboundForPacketConnection
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if metadata.InboundDetour != "" {
|
||||
if metadata.LastInbound == metadata.InboundDetour {
|
||||
@@ -583,9 +606,12 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
}
|
||||
if r.v2rayServer != nil {
|
||||
if statsService := r.v2rayServer.StatsService(); statsService != nil {
|
||||
conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), conn)
|
||||
conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
|
||||
}
|
||||
}
|
||||
if r.ssmServer != nil {
|
||||
conn = r.ssmServer.RoutedConnection(metadata, conn)
|
||||
}
|
||||
return detour.NewConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -661,9 +687,12 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
}
|
||||
if r.v2rayServer != nil {
|
||||
if statsService := r.v2rayServer.StatsService(); statsService != nil {
|
||||
conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), conn)
|
||||
conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
|
||||
}
|
||||
}
|
||||
if r.ssmServer != nil {
|
||||
conn = r.ssmServer.RoutedPacketConnection(metadata, conn)
|
||||
}
|
||||
return detour.NewPacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -760,6 +789,14 @@ func (r *Router) SetV2RayServer(server adapter.V2RayServer) {
|
||||
r.v2rayServer = server
|
||||
}
|
||||
|
||||
func (r *Router) SSMServer() adapter.SSMServer {
|
||||
return r.ssmServer
|
||||
}
|
||||
|
||||
func (r *Router) SetSSMServer(server adapter.SSMServer) {
|
||||
r.ssmServer = server
|
||||
}
|
||||
|
||||
func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool {
|
||||
for _, rule := range rules {
|
||||
switch rule.Type {
|
||||
|
||||
@@ -50,7 +50,8 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
}
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
if len(message.Question) > 0 {
|
||||
switch message.Question[0].Qtype {
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case mDNS.TypeAAAA:
|
||||
|
||||
@@ -71,6 +71,11 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
return nil, E.New("invalid ip version: ", options.IPVersion)
|
||||
}
|
||||
}
|
||||
if len(options.QueryType) > 0 {
|
||||
item := NewQueryTypeItem(options.QueryType)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.Network != "" {
|
||||
switch options.Network {
|
||||
case N.NetworkTCP, N.NetworkUDP:
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*GeoIPItem)(nil)
|
||||
@@ -32,29 +34,50 @@ func NewGeoIPItem(router adapter.Router, logger log.ContextLogger, isSource bool
|
||||
}
|
||||
|
||||
func (r *GeoIPItem) Match(metadata *adapter.InboundContext) bool {
|
||||
var geoipCode string
|
||||
if r.isSource && metadata.SourceGeoIPCode != "" {
|
||||
geoipCode = metadata.SourceGeoIPCode
|
||||
} else if !r.isSource && metadata.GeoIPCode != "" {
|
||||
geoipCode = metadata.GeoIPCode
|
||||
}
|
||||
if geoipCode != "" {
|
||||
return r.codeMap[geoipCode]
|
||||
}
|
||||
var destination netip.Addr
|
||||
if r.isSource {
|
||||
destination = metadata.Source.Addr
|
||||
} else {
|
||||
destination = metadata.Destination.Addr
|
||||
}
|
||||
if destination.IsValid() {
|
||||
return r.match(metadata, destination)
|
||||
}
|
||||
for _, destinationAddress := range metadata.DestinationAddresses {
|
||||
if r.match(metadata, destinationAddress) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *GeoIPItem) match(metadata *adapter.InboundContext, destination netip.Addr) bool {
|
||||
var geoipCode string
|
||||
geoReader := r.router.GeoIPReader()
|
||||
if geoReader == nil {
|
||||
if geoReader != nil {
|
||||
geoipCode = geoReader.Lookup(destination)
|
||||
}
|
||||
if geoipCode == "" && !N.IsPublicAddr(destination) {
|
||||
geoipCode = "private"
|
||||
}
|
||||
if geoipCode == "" {
|
||||
return false
|
||||
}
|
||||
if r.isSource {
|
||||
if metadata.SourceGeoIPCode == "" {
|
||||
metadata.SourceGeoIPCode = geoReader.Lookup(metadata.Source.Addr)
|
||||
}
|
||||
return r.codeMap[metadata.SourceGeoIPCode]
|
||||
metadata.SourceGeoIPCode = geoipCode
|
||||
} else {
|
||||
if metadata.Destination.IsIP() {
|
||||
if metadata.GeoIPCode == "" {
|
||||
metadata.GeoIPCode = geoReader.Lookup(metadata.Destination.Addr)
|
||||
}
|
||||
return r.codeMap[metadata.GeoIPCode]
|
||||
}
|
||||
for _, address := range metadata.DestinationAddresses {
|
||||
if r.codeMap[geoReader.Lookup(address)] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
metadata.GeoIPCode = geoipCode
|
||||
}
|
||||
return r.codeMap[geoipCode]
|
||||
}
|
||||
|
||||
func (r *GeoIPItem) String() string {
|
||||
|
||||
47
route/rule_query_type.go
Normal file
47
route/rule_query_type.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*QueryTypeItem)(nil)
|
||||
|
||||
type QueryTypeItem struct {
|
||||
typeList []uint16
|
||||
typeMap map[uint16]bool
|
||||
}
|
||||
|
||||
func NewQueryTypeItem(typeList []option.DNSQueryType) *QueryTypeItem {
|
||||
rule := &QueryTypeItem{
|
||||
typeList: common.Map(typeList, func(it option.DNSQueryType) uint16 {
|
||||
return uint16(it)
|
||||
}),
|
||||
typeMap: make(map[uint16]bool),
|
||||
}
|
||||
for _, userId := range rule.typeList {
|
||||
rule.typeMap[userId] = true
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
func (r *QueryTypeItem) Match(metadata *adapter.InboundContext) bool {
|
||||
if metadata.QueryType == 0 {
|
||||
return false
|
||||
}
|
||||
return r.typeMap[metadata.QueryType]
|
||||
}
|
||||
|
||||
func (r *QueryTypeItem) String() string {
|
||||
var description string
|
||||
pLen := len(r.typeList)
|
||||
if pLen == 1 {
|
||||
description = "query_type=" + option.DNSQueryTypeToString(r.typeList[0])
|
||||
} else {
|
||||
description = "query_type=[" + strings.Join(common.Map(r.typeList, option.DNSQueryTypeToString), " ") + "]"
|
||||
}
|
||||
return description
|
||||
}
|
||||
61
test/go.mod
61
test/go.mod
@@ -9,81 +9,88 @@ replace github.com/sagernet/sing-box => ../
|
||||
require (
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid v4.3.1+incompatible
|
||||
github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
go.uber.org/goleak v1.2.0
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/net v0.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.11.12 // indirect
|
||||
github.com/Dreamacro/clash v1.13.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/caddyserver/certmagic v0.17.2 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/database64128/tfo-go/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.7 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/go-chi/cors v1.2.1 // indirect
|
||||
github.com/go-chi/render v1.0.2 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
github.com/marten-seemann/qpack v0.3.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
||||
github.com/mholt/acmez v1.0.4 // indirect
|
||||
github.com/mholt/acmez v1.1.0 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/refraction-networking/utls v1.2.0 // indirect
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/refraction-networking/utls v1.2.2 // indirect
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20221108053023-645bcc4f9b15 // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20221113031420-c6aaf2ea4b10 // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f // indirect
|
||||
github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.1 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb // indirect
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d // indirect
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect
|
||||
golang.org/x/crypto v0.6.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
google.golang.org/grpc v1.53.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.4.0 // indirect
|
||||
|
||||
279
test/go.sum
279
test/go.sum
@@ -1,34 +1,25 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Dreamacro/clash v1.11.12 h1:zJ+FUWPHWxhfNl5MK64oezFAPPyGth+SDhjuWEJ/jwM=
|
||||
github.com/Dreamacro/clash v1.11.12/go.mod h1:WiRGFHBrOUYP89GXJ9k4KCyZq5i485LWzc4FPsEPlMI=
|
||||
github.com/Dreamacro/clash v1.13.0 h1:gF0E0TluE1LCmuhhg0/bjqABYDmSnXkUjXjRhZxyrm8=
|
||||
github.com/Dreamacro/clash v1.13.0/go.mod h1:hf0RkWPsQ0e8oS8WVJBIRocY/1ILYzQQg9zeMwd8LsM=
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
||||
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c h1:K1VdSnBZiGapczwcUKnE1qcsMBclA84DUOD2NG/78VY=
|
||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||
github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg=
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -40,39 +31,23 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
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/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
|
||||
github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
@@ -82,45 +57,57 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
|
||||
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
|
||||
github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
|
||||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
|
||||
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g=
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0 h1:kUMoxMoQG3ogk/QWyKh3zibV7BKZ+xBpWil1cTylVqc=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
@@ -129,39 +116,44 @@ github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd918
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/refraction-networking/utls v1.2.0 h1:U5f8wkij2NVinfLuJdFP3gCMwIHs+EzvhxmYdXgiapo=
|
||||
github.com/refraction-networking/utls v1.2.0/go.mod h1:NPq+cVqzH7D1BeOkmOcb5O/8iVewAsiVt2x1/eO0hgQ=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
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/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/refraction-networking/utls v1.2.2 h1:uBE6V173CwG8MQrSBpNZHAix1fxOvuLKYyjFAu3uqo0=
|
||||
github.com/refraction-networking/utls v1.2.2/go.mod h1:L1goe44KvhnTfctUffM2isnJpSjPlYShrhXDeZaoYKw=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20221108053023-645bcc4f9b15 h1:l8RQTjz5LlGEFOc49dXAr14ORbj8mTW7nX88Rbm+FiY=
|
||||
github.com/sagernet/quic-go v0.0.0-20221108053023-645bcc4f9b15/go.mod h1:oWFbojDMm85/Jbm/fyWoo8Pux6dIssxGi3q1r+5642A=
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||
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-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo=
|
||||
github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
|
||||
github.com/sagernet/sing-dns v0.0.0-20221113031420-c6aaf2ea4b10 h1:K84AY2TxNX37ePYXVO6QTD/kgn9kDo4oGpTIn9PF5bo=
|
||||
github.com/sagernet/sing-dns v0.0.0-20221113031420-c6aaf2ea4b10/go.mod h1:VAvOT1pyryBIthTGRryFLXAsR1VRQZ05wolMYeQrr/E=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f h1:CXF+nErOb9f7qiHingSgTa2/lJAgmEFtAQ47oVwdRGU=
|
||||
github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 h1:z3kuD3hPNdEq7/wVy5lwE21f+8ZTazBtR81qswxJoCc=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13 h1:S/+YvJCEChwnckGhzqSrE/Q2m6aVWhkt1I4Pv2yCMVw=
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455 h1:VA/j2Jg+JURgKw2C1Dw2tpjfOZwbLXRy8PJRbJS/HEU=
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455/go.mod h1:nonvn66ja+UNrQl3jzJy0EFRn15QUaCFAVXTXf6TgJ4=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7 h1:Plup6oEiyLzY3HDqQ+QsUBzgBGdVmcsgf3t8h940z9U=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7/go.mod h1:O5LtOs8Ivw686FqLpO0Zu+A0ROVE15VeqEK3yDRRAms=
|
||||
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
||||
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM=
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
|
||||
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/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||
@@ -169,6 +161,8 @@ github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:eu
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
|
||||
github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -177,128 +171,116 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q=
|
||||
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f h1:Al51T6tzvuh3oiwX11vex3QgJ2XTedFPGmbEVh8cdoc=
|
||||
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
@@ -306,52 +288,23 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f h1:YORWxaStkWBnWgELOHTmDrqNlFXuVGEbhwbB5iK94bQ=
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
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.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
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-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
|
||||
@@ -17,22 +17,19 @@ import (
|
||||
|
||||
func TestShadowTLS(t *testing.T) {
|
||||
t.Run("v1", func(t *testing.T) {
|
||||
testShadowTLS(t, "")
|
||||
testShadowTLS(t, 1, "")
|
||||
})
|
||||
t.Run("v2", func(t *testing.T) {
|
||||
testShadowTLS(t, "hello")
|
||||
testShadowTLS(t, 2, "hello")
|
||||
})
|
||||
t.Run("v3", func(t *testing.T) {
|
||||
testShadowTLS(t, 3, "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func testShadowTLS(t *testing.T, password string) {
|
||||
func testShadowTLS(t *testing.T, version int, password string) {
|
||||
method := shadowaead_2022.List[0]
|
||||
ssPassword := mkBase64(t, 16)
|
||||
var version int
|
||||
if password != "" {
|
||||
version = 2
|
||||
} else {
|
||||
version = 1
|
||||
}
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
@@ -123,7 +120,7 @@ func testShadowTLS(t *testing.T, password string) {
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestShadowTLSv2Fallback(t *testing.T) {
|
||||
func TestShadowTLSFallback(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
@@ -139,7 +136,7 @@ func TestShadowTLSv2Fallback(t *testing.T) {
|
||||
ServerPort: 443,
|
||||
},
|
||||
},
|
||||
Version: 2,
|
||||
Version: 3,
|
||||
Password: "hello",
|
||||
},
|
||||
},
|
||||
@@ -167,7 +164,7 @@ func TestShadowTLSInbound(t *testing.T) {
|
||||
Image: ImageShadowTLS,
|
||||
Ports: []uint16{serverPort, otherPort},
|
||||
EntryPoint: "shadow-tls",
|
||||
Cmd: []string{"--threads", "1", "client", "--listen", "0.0.0.0:" + F.ToString(otherPort), "--server", "127.0.0.1:" + F.ToString(serverPort), "--sni", "google.com", "--password", password},
|
||||
Cmd: []string{"--v3", "--threads", "1", "client", "--listen", "0.0.0.0:" + F.ToString(otherPort), "--server", "127.0.0.1:" + F.ToString(serverPort), "--sni", "google.com", "--password", password},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
@@ -195,7 +192,7 @@ func TestShadowTLSInbound(t *testing.T) {
|
||||
ServerPort: 443,
|
||||
},
|
||||
},
|
||||
Version: 2,
|
||||
Version: 3,
|
||||
Password: password,
|
||||
},
|
||||
},
|
||||
@@ -225,9 +222,6 @@ func TestShadowTLSInbound(t *testing.T) {
|
||||
},
|
||||
Method: method,
|
||||
Password: password,
|
||||
MultiplexOptions: &option.MultiplexOptions{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -240,7 +234,7 @@ func TestShadowTLSInbound(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testTCP(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestShadowTLSOutbound(t *testing.T) {
|
||||
@@ -250,7 +244,8 @@ func TestShadowTLSOutbound(t *testing.T) {
|
||||
Image: ImageShadowTLS,
|
||||
Ports: []uint16{serverPort, otherPort},
|
||||
EntryPoint: "shadow-tls",
|
||||
Cmd: []string{"--threads", "1", "server", "--listen", "0.0.0.0:" + F.ToString(serverPort), "--server", "127.0.0.1:" + F.ToString(otherPort), "--tls", "google.com:443", "--password", "hello"},
|
||||
Cmd: []string{"--v3", "--threads", "1", "server", "--listen", "0.0.0.0:" + F.ToString(serverPort), "--server", "127.0.0.1:" + F.ToString(otherPort), "--tls", "google.com:443", "--password", "hello"},
|
||||
Env: []string{"RUST_LOG=trace"},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
@@ -285,9 +280,6 @@ func TestShadowTLSOutbound(t *testing.T) {
|
||||
DialerOptions: option.DialerOptions{
|
||||
Detour: "detour",
|
||||
},
|
||||
MultiplexOptions: &option.MultiplexOptions{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -302,7 +294,7 @@ func TestShadowTLSOutbound(t *testing.T) {
|
||||
Enabled: true,
|
||||
ServerName: "google.com",
|
||||
},
|
||||
Version: 2,
|
||||
Version: 3,
|
||||
Password: "hello",
|
||||
},
|
||||
},
|
||||
@@ -320,5 +312,5 @@ func TestShadowTLSOutbound(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testTCP(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
262
transport/dhcp/server.go
Normal file
262
transport/dhcp/server.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package dhcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"dhcp"}, NewTransport)
|
||||
}
|
||||
|
||||
type Transport struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger logger.Logger
|
||||
interfaceName string
|
||||
autoInterface bool
|
||||
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
|
||||
transports []dns.Transport
|
||||
updateAccess sync.Mutex
|
||||
updatedAt time.Time
|
||||
}
|
||||
|
||||
func NewTransport(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
linkURL, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if linkURL.Host == "" {
|
||||
return nil, E.New("missing interface name for DHCP")
|
||||
}
|
||||
router := adapter.RouterFromContext(ctx)
|
||||
if router == nil {
|
||||
return nil, E.New("missing router in context")
|
||||
}
|
||||
transport := &Transport{
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
interfaceName: linkURL.Host,
|
||||
autoInterface: linkURL.Host == "auto",
|
||||
}
|
||||
return transport, nil
|
||||
}
|
||||
|
||||
func (t *Transport) Start() error {
|
||||
err := t.fetchServers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.autoInterface {
|
||||
t.interfaceCallback = t.router.InterfaceMonitor().RegisterCallback(t.interfaceUpdated)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Close() error {
|
||||
if t.interfaceCallback != nil {
|
||||
t.router.InterfaceMonitor().UnregisterCallback(t.interfaceCallback)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Raw() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
err := t.fetchServers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(t.transports) == 0 {
|
||||
return nil, E.New("dhcp: empty DNS servers from response")
|
||||
}
|
||||
|
||||
var response *mDNS.Msg
|
||||
for _, transport := range t.transports {
|
||||
response, err = transport.Exchange(ctx, message)
|
||||
if err == nil {
|
||||
return response, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (t *Transport) fetchInterface() (*net.Interface, error) {
|
||||
interfaceName := t.interfaceName
|
||||
if t.autoInterface {
|
||||
if t.router.NetworkMonitor() == nil {
|
||||
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
|
||||
}
|
||||
interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
|
||||
}
|
||||
if interfaceName == "" {
|
||||
return nil, E.New("missing default interface")
|
||||
}
|
||||
return net.InterfaceByName(interfaceName)
|
||||
}
|
||||
|
||||
func (t *Transport) fetchServers() error {
|
||||
if time.Since(t.updatedAt) < C.DHCPTTL {
|
||||
return nil
|
||||
}
|
||||
t.updateAccess.Lock()
|
||||
defer t.updateAccess.Unlock()
|
||||
if time.Since(t.updatedAt) < C.DHCPTTL {
|
||||
return nil
|
||||
}
|
||||
return t.updateServers()
|
||||
}
|
||||
|
||||
func (t *Transport) updateServers() error {
|
||||
iface, err := t.fetchInterface()
|
||||
if err != nil {
|
||||
return E.Cause(err, "dhcp: prepare interface")
|
||||
}
|
||||
|
||||
t.logger.Info("dhcp: query DNS servers on ", iface.Name)
|
||||
fetchCtx, cancel := context.WithTimeout(t.ctx, C.DHCPTimeout)
|
||||
err = t.fetchServers0(fetchCtx, iface)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(t.transports) == 0 {
|
||||
return E.New("dhcp: empty DNS servers response")
|
||||
} else {
|
||||
t.updatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) interfaceUpdated(int) error {
|
||||
return t.updateServers()
|
||||
}
|
||||
|
||||
func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) error {
|
||||
var listener net.ListenConfig
|
||||
listener.Control = control.Append(listener.Control, control.BindToInterfaceFunc(t.router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||
return iface.Name, iface.Index
|
||||
}))
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
packetConn, err := listener.ListenPacket(t.ctx, "udp4", "0.0.0.0:68")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer packetConn.Close()
|
||||
|
||||
discovery, err := dhcpv4.NewDiscovery(iface.HardwareAddr, dhcpv4.WithBroadcast(true), dhcpv4.WithRequestedOptions(dhcpv4.OptionDomainNameServer))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = packetConn.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
return t.fetchServersResponse(iface, packetConn, discovery.TransactionID)
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
packetConn.Close()
|
||||
})
|
||||
return group.Run(ctx)
|
||||
}
|
||||
|
||||
func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.PacketConn, transactionID dhcpv4.TransactionID) error {
|
||||
_buffer := buf.StackNewSize(dhcpv4.MaxMessageSize)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
|
||||
for {
|
||||
_, _, err := buffer.ReadPacketFrom(packetConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dhcpPacket, err := dhcpv4.FromBytes(buffer.Bytes())
|
||||
if err != nil {
|
||||
t.logger.Trace("dhcp: parse DHCP response: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if dhcpPacket.MessageType() != dhcpv4.MessageTypeOffer {
|
||||
t.logger.Trace("dhcp: expected OFFER response, but got ", dhcpPacket.MessageType())
|
||||
continue
|
||||
}
|
||||
|
||||
if dhcpPacket.TransactionID != transactionID {
|
||||
t.logger.Trace("dhcp: expected transaction ID ", transactionID, ", but got ", dhcpPacket.TransactionID)
|
||||
continue
|
||||
}
|
||||
|
||||
dns := dhcpPacket.DNS()
|
||||
if len(dns) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var addrs []netip.Addr
|
||||
for _, ip := range dns {
|
||||
addr, _ := netip.AddrFromSlice(ip)
|
||||
addrs = append(addrs, addr.Unmap())
|
||||
}
|
||||
return t.recreateServers(iface, addrs)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Addr) error {
|
||||
if len(serverAddrs) > 0 {
|
||||
t.logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string {
|
||||
return it.String()
|
||||
}), ","), "]")
|
||||
}
|
||||
|
||||
serverDialer := dialer.NewDefault(t.router, option.DialerOptions{
|
||||
BindInterface: iface.Name,
|
||||
UDPFragmentDefault: true,
|
||||
})
|
||||
var transports []dns.Transport
|
||||
for _, serverAddr := range serverAddrs {
|
||||
serverTransport, err := dns.NewUDPTransport(t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transports = append(transports, serverTransport)
|
||||
}
|
||||
t.transports = transports
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
@@ -293,6 +293,10 @@ func ParseUDPMessage(packet []byte) (message UDPMessage, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if 6+int(hostLen) > len(packet) {
|
||||
err = E.New("invalid host length")
|
||||
return
|
||||
}
|
||||
message.Host = string(packet[6 : 6+hostLen])
|
||||
err = binary.Read(reader, binary.BigEndian, &message.Port)
|
||||
if err != nil {
|
||||
|
||||
294
transport/shadowtls/client_v3.go
Normal file
294
transport/shadowtls/client_v3.go
Normal file
@@ -0,0 +1,294 @@
|
||||
package shadowtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
tlsRandomSize = 32
|
||||
tlsHeaderSize = 5
|
||||
tlsSessionIDSize = 32
|
||||
|
||||
clientHello = 1
|
||||
serverHello = 2
|
||||
|
||||
changeCipherSpec = 20
|
||||
alert = 21
|
||||
handshake = 22
|
||||
applicationData = 23
|
||||
|
||||
serverRandomIndex = tlsHeaderSize + 1 + 3 + 2
|
||||
sessionIDLengthIndex = tlsHeaderSize + 1 + 3 + 2 + tlsRandomSize
|
||||
tlsHmacHeaderSize = tlsHeaderSize + hmacSize
|
||||
hmacSize = 4
|
||||
)
|
||||
|
||||
func generateSessionID(password string) func(clientHello []byte, sessionID []byte) error {
|
||||
return func(clientHello []byte, sessionID []byte) error {
|
||||
const sessionIDStart = 1 + 3 + 2 + tlsRandomSize + 1
|
||||
if len(clientHello) < sessionIDStart+tlsSessionIDSize {
|
||||
return E.New("unexpected client hello length")
|
||||
}
|
||||
_, err := rand.Read(sessionID[:tlsSessionIDSize-hmacSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hmacSHA1Hash := hmac.New(sha1.New, []byte(password))
|
||||
hmacSHA1Hash.Write(clientHello[:sessionIDStart])
|
||||
hmacSHA1Hash.Write(sessionID)
|
||||
hmacSHA1Hash.Write(clientHello[sessionIDStart+tlsSessionIDSize:])
|
||||
copy(sessionID[tlsSessionIDSize-hmacSize:], hmacSHA1Hash.Sum(nil)[:hmacSize])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type StreamWrapper struct {
|
||||
net.Conn
|
||||
password string
|
||||
buffer *buf.Buffer
|
||||
serverRandom []byte
|
||||
readHMAC hash.Hash
|
||||
readHMACKey []byte
|
||||
authorized bool
|
||||
}
|
||||
|
||||
func NewStreamWrapper(conn net.Conn, password string) *StreamWrapper {
|
||||
return &StreamWrapper{
|
||||
Conn: conn,
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *StreamWrapper) Authorized() (bool, []byte, hash.Hash) {
|
||||
return w.authorized, w.serverRandom, w.readHMAC
|
||||
}
|
||||
|
||||
func (w *StreamWrapper) Read(p []byte) (n int, err error) {
|
||||
if w.buffer != nil {
|
||||
if !w.buffer.IsEmpty() {
|
||||
return w.buffer.Read(p)
|
||||
}
|
||||
w.buffer.Release()
|
||||
w.buffer = nil
|
||||
}
|
||||
var tlsHeader [tlsHeaderSize]byte
|
||||
_, err = io.ReadFull(w.Conn, tlsHeader[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length := int(binary.BigEndian.Uint16(tlsHeader[3:tlsHeaderSize]))
|
||||
w.buffer = buf.NewSize(tlsHeaderSize + length)
|
||||
common.Must1(w.buffer.Write(tlsHeader[:]))
|
||||
_, err = w.buffer.ReadFullFrom(w.Conn, length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer := w.buffer.Bytes()
|
||||
switch tlsHeader[0] {
|
||||
case handshake:
|
||||
if len(buffer) > serverRandomIndex+tlsRandomSize && buffer[5] == serverHello {
|
||||
w.serverRandom = make([]byte, tlsRandomSize)
|
||||
copy(w.serverRandom, buffer[serverRandomIndex:serverRandomIndex+tlsRandomSize])
|
||||
w.readHMAC = hmac.New(sha1.New, []byte(w.password))
|
||||
w.readHMAC.Write(w.serverRandom)
|
||||
w.readHMACKey = kdf(w.password, w.serverRandom)
|
||||
}
|
||||
case applicationData:
|
||||
w.authorized = false
|
||||
if len(buffer) > tlsHmacHeaderSize && w.readHMAC != nil {
|
||||
w.readHMAC.Write(buffer[tlsHmacHeaderSize:])
|
||||
if hmac.Equal(w.readHMAC.Sum(nil)[:hmacSize], buffer[tlsHeaderSize:tlsHmacHeaderSize]) {
|
||||
xorSlice(buffer[tlsHmacHeaderSize:], w.readHMACKey)
|
||||
copy(buffer[hmacSize:], buffer[:tlsHeaderSize])
|
||||
binary.BigEndian.PutUint16(buffer[hmacSize+3:], uint16(len(buffer)-tlsHmacHeaderSize))
|
||||
w.buffer.Advance(hmacSize)
|
||||
w.authorized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return w.buffer.Read(p)
|
||||
}
|
||||
|
||||
func kdf(password string, serverRandom []byte) []byte {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(password))
|
||||
hasher.Write(serverRandom)
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
func xorSlice(data []byte, key []byte) {
|
||||
for i := range data {
|
||||
data[i] ^= key[i%len(key)]
|
||||
}
|
||||
}
|
||||
|
||||
var _ N.VectorisedWriter = (*VerifiedConn)(nil)
|
||||
|
||||
type VerifiedConn struct {
|
||||
net.Conn
|
||||
writer N.VectorisedWriter
|
||||
hmacAdd hash.Hash
|
||||
hmacVerify hash.Hash
|
||||
hmacIgnore hash.Hash
|
||||
|
||||
buffer *buf.Buffer
|
||||
}
|
||||
|
||||
func NewVerifiedConn(
|
||||
conn net.Conn,
|
||||
hmacAdd hash.Hash,
|
||||
hmacVerify hash.Hash,
|
||||
hmacIgnore hash.Hash,
|
||||
) *VerifiedConn {
|
||||
return &VerifiedConn{
|
||||
Conn: conn,
|
||||
writer: bufio.NewVectorisedWriter(conn),
|
||||
hmacAdd: hmacAdd,
|
||||
hmacVerify: hmacVerify,
|
||||
hmacIgnore: hmacIgnore,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *VerifiedConn) Read(b []byte) (n int, err error) {
|
||||
if c.buffer != nil {
|
||||
if !c.buffer.IsEmpty() {
|
||||
return c.buffer.Read(b)
|
||||
}
|
||||
c.buffer.Release()
|
||||
c.buffer = nil
|
||||
}
|
||||
for {
|
||||
var tlsHeader [tlsHeaderSize]byte
|
||||
_, err = io.ReadFull(c.Conn, tlsHeader[:])
|
||||
if err != nil {
|
||||
sendAlert(c.Conn)
|
||||
return
|
||||
}
|
||||
length := int(binary.BigEndian.Uint16(tlsHeader[3:tlsHeaderSize]))
|
||||
c.buffer = buf.NewSize(tlsHeaderSize + length)
|
||||
common.Must1(c.buffer.Write(tlsHeader[:]))
|
||||
_, err = c.buffer.ReadFullFrom(c.Conn, length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer := c.buffer.Bytes()
|
||||
switch buffer[0] {
|
||||
case alert:
|
||||
err = E.Cause(net.ErrClosed, "remote alert")
|
||||
return
|
||||
case applicationData:
|
||||
if c.hmacIgnore != nil {
|
||||
if verifyApplicationData(buffer, c.hmacIgnore, false) {
|
||||
c.buffer.Release()
|
||||
c.buffer = nil
|
||||
continue
|
||||
} else {
|
||||
c.hmacIgnore = nil
|
||||
}
|
||||
}
|
||||
if !verifyApplicationData(buffer, c.hmacVerify, true) {
|
||||
sendAlert(c.Conn)
|
||||
err = E.New("application data verification failed")
|
||||
return
|
||||
}
|
||||
c.buffer.Advance(tlsHmacHeaderSize)
|
||||
default:
|
||||
sendAlert(c.Conn)
|
||||
err = E.New("unexpected TLS record type: ", buffer[0])
|
||||
return
|
||||
}
|
||||
return c.buffer.Read(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *VerifiedConn) Write(p []byte) (n int, err error) {
|
||||
pTotal := len(p)
|
||||
for len(p) > 0 {
|
||||
var pWrite []byte
|
||||
if len(p) > 16384 {
|
||||
pWrite = p[:16384]
|
||||
p = p[16384:]
|
||||
} else {
|
||||
pWrite = p
|
||||
p = nil
|
||||
}
|
||||
_, err = c.write(pWrite)
|
||||
}
|
||||
if err == nil {
|
||||
n = pTotal
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *VerifiedConn) write(p []byte) (n int, err error) {
|
||||
var header [tlsHmacHeaderSize]byte
|
||||
header[0] = applicationData
|
||||
header[1] = 3
|
||||
header[2] = 3
|
||||
binary.BigEndian.PutUint16(header[3:tlsHeaderSize], hmacSize+uint16(len(p)))
|
||||
c.hmacAdd.Write(p)
|
||||
hmacHash := c.hmacAdd.Sum(nil)[:hmacSize]
|
||||
c.hmacAdd.Write(hmacHash)
|
||||
copy(header[tlsHeaderSize:], hmacHash)
|
||||
_, err = bufio.WriteVectorised(c.writer, [][]byte{common.Dup(header[:]), p})
|
||||
if err == nil {
|
||||
n = len(p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *VerifiedConn) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
var header [tlsHmacHeaderSize]byte
|
||||
header[0] = applicationData
|
||||
header[1] = 3
|
||||
header[2] = 3
|
||||
binary.BigEndian.PutUint16(header[3:tlsHeaderSize], hmacSize+uint16(buf.LenMulti(buffers)))
|
||||
for _, buffer := range buffers {
|
||||
c.hmacAdd.Write(buffer.Bytes())
|
||||
}
|
||||
c.hmacAdd.Write(c.hmacAdd.Sum(nil)[:hmacSize])
|
||||
copy(header[tlsHeaderSize:], c.hmacAdd.Sum(nil)[:hmacSize])
|
||||
return c.writer.WriteVectorised(append([]*buf.Buffer{buf.As(header[:])}, buffers...))
|
||||
}
|
||||
|
||||
func verifyApplicationData(frame []byte, hmac hash.Hash, update bool) bool {
|
||||
if frame[1] != 3 || frame[2] != 3 || len(frame) < tlsHmacHeaderSize {
|
||||
return false
|
||||
}
|
||||
hmac.Write(frame[tlsHmacHeaderSize:])
|
||||
hmacHash := hmac.Sum(nil)[:hmacSize]
|
||||
if update {
|
||||
hmac.Write(hmacHash)
|
||||
}
|
||||
return bytes.Equal(frame[tlsHeaderSize:tlsHeaderSize+hmacSize], hmacHash)
|
||||
}
|
||||
|
||||
func sendAlert(writer io.Writer) {
|
||||
const recordSize = 31
|
||||
record := [recordSize]byte{
|
||||
alert,
|
||||
3,
|
||||
3,
|
||||
0,
|
||||
recordSize - tlsHeaderSize,
|
||||
}
|
||||
_, err := rand.Read(record[tlsHeaderSize:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
writer.Write(record[:])
|
||||
}
|
||||
157
transport/shadowtls/config.go
Normal file
157
transport/shadowtls/config.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package shadowtls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
var _ tls.Config = (*ClientTLSConfig)(nil)
|
||||
|
||||
type ClientTLSConfig struct {
|
||||
config *sTLSConfig
|
||||
}
|
||||
|
||||
func NewClientTLSConfig(serverAddress string, options option.OutboundTLSOptions, password string) (*ClientTLSConfig, error) {
|
||||
if options.ECH != nil && options.ECH.Enabled {
|
||||
return nil, E.New("ECH is not supported in shadowtls v3")
|
||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||
return nil, E.New("UTLS is not supported in shadowtls v3")
|
||||
}
|
||||
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
} else if serverAddress != "" {
|
||||
if _, err := netip.ParseAddr(serverName); err != nil {
|
||||
serverName = serverAddress
|
||||
}
|
||||
}
|
||||
if serverName == "" && !options.Insecure {
|
||||
return nil, E.New("missing server_name or insecure=true")
|
||||
}
|
||||
|
||||
var tlsConfig sTLSConfig
|
||||
tlsConfig.SessionIDGenerator = generateSessionID(password)
|
||||
if options.DisableSNI {
|
||||
tlsConfig.ServerName = "127.0.0.1"
|
||||
} else {
|
||||
tlsConfig.ServerName = serverName
|
||||
}
|
||||
if options.Insecure {
|
||||
tlsConfig.InsecureSkipVerify = options.Insecure
|
||||
} else if options.DisableSNI {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
tlsConfig.VerifyConnection = func(state sTLSConnectionState) error {
|
||||
verifyOptions := x509.VerifyOptions{
|
||||
DNSName: serverName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
for _, cert := range state.PeerCertificates[1:] {
|
||||
verifyOptions.Intermediates.AddCert(cert)
|
||||
}
|
||||
_, err := state.PeerCertificates[0].Verify(verifyOptions)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(options.ALPN) > 0 {
|
||||
tlsConfig.NextProtos = options.ALPN
|
||||
}
|
||||
if options.MinVersion != "" {
|
||||
minVersion, err := tls.ParseTLSVersion(options.MinVersion)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse min_version")
|
||||
}
|
||||
tlsConfig.MinVersion = minVersion
|
||||
}
|
||||
if options.MaxVersion != "" {
|
||||
maxVersion, err := tls.ParseTLSVersion(options.MaxVersion)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse max_version")
|
||||
}
|
||||
tlsConfig.MaxVersion = maxVersion
|
||||
}
|
||||
if options.CipherSuites != nil {
|
||||
find:
|
||||
for _, cipherSuite := range options.CipherSuites {
|
||||
for _, tlsCipherSuite := range sTLSCipherSuites() {
|
||||
if cipherSuite == tlsCipherSuite.Name {
|
||||
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
|
||||
continue find
|
||||
}
|
||||
}
|
||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||
}
|
||||
}
|
||||
var certificate []byte
|
||||
if options.Certificate != "" {
|
||||
certificate = []byte(options.Certificate)
|
||||
} else if options.CertificatePath != "" {
|
||||
content, err := os.ReadFile(options.CertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read certificate")
|
||||
}
|
||||
certificate = content
|
||||
}
|
||||
if len(certificate) > 0 {
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(certificate) {
|
||||
return nil, E.New("failed to parse certificate:\n\n", certificate)
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
return &ClientTLSConfig{&tlsConfig}, nil
|
||||
}
|
||||
|
||||
func (c *ClientTLSConfig) ServerName() string {
|
||||
return c.config.ServerName
|
||||
}
|
||||
|
||||
func (c *ClientTLSConfig) SetServerName(serverName string) {
|
||||
c.config.ServerName = serverName
|
||||
}
|
||||
|
||||
func (c *ClientTLSConfig) NextProtos() []string {
|
||||
return c.config.NextProtos
|
||||
}
|
||||
|
||||
func (c *ClientTLSConfig) SetNextProtos(nextProto []string) {
|
||||
c.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (c *ClientTLSConfig) Config() (*tls.STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for ShadowTLS")
|
||||
}
|
||||
|
||||
func (c *ClientTLSConfig) Client(conn net.Conn) tls.Conn {
|
||||
return &shadowTLSConnWrapper{sTLSClient(conn, c.config)}
|
||||
}
|
||||
|
||||
func (c *ClientTLSConfig) Clone() tls.Config {
|
||||
return &ClientTLSConfig{c.config.Clone()}
|
||||
}
|
||||
|
||||
type shadowTLSConnWrapper struct {
|
||||
*sTLSConn
|
||||
}
|
||||
|
||||
func (c *shadowTLSConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
state := c.sTLSConn.ConnectionState()
|
||||
return tls.ConnectionState{
|
||||
Version: state.Version,
|
||||
HandshakeComplete: state.HandshakeComplete,
|
||||
DidResume: state.DidResume,
|
||||
CipherSuite: state.CipherSuite,
|
||||
NegotiatedProtocol: state.NegotiatedProtocol,
|
||||
ServerName: state.ServerName,
|
||||
PeerCertificates: state.PeerCertificates,
|
||||
VerifiedChains: state.VerifiedChains,
|
||||
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
|
||||
OCSPResponse: state.OCSPResponse,
|
||||
}
|
||||
}
|
||||
16
transport/shadowtls/config_119.go
Normal file
16
transport/shadowtls/config_119.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build !go1.20
|
||||
|
||||
package shadowtls
|
||||
|
||||
import sTLS "github.com/sagernet/sing-box/transport/shadowtls/tls_go119"
|
||||
|
||||
type (
|
||||
sTLSConfig = sTLS.Config
|
||||
sTLSConnectionState = sTLS.ConnectionState
|
||||
sTLSConn = sTLS.Conn
|
||||
)
|
||||
|
||||
var (
|
||||
sTLSCipherSuites = sTLS.CipherSuites
|
||||
sTLSClient = sTLS.Client
|
||||
)
|
||||
16
transport/shadowtls/config_120.go
Normal file
16
transport/shadowtls/config_120.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build go1.20
|
||||
|
||||
package shadowtls
|
||||
|
||||
import sTLS "github.com/sagernet/sing-box/transport/shadowtls/tls"
|
||||
|
||||
type (
|
||||
sTLSConfig = sTLS.Config
|
||||
sTLSConnectionState = sTLS.ConnectionState
|
||||
sTLSConn = sTLS.Conn
|
||||
)
|
||||
|
||||
var (
|
||||
sTLSCipherSuites = sTLS.CipherSuites
|
||||
sTLSClient = sTLS.Client
|
||||
)
|
||||
181
transport/shadowtls/server_v3.go
Normal file
181
transport/shadowtls/server_v3.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package shadowtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func ExtractFrame(conn net.Conn) (*buf.Buffer, error) {
|
||||
var tlsHeader [tlsHeaderSize]byte
|
||||
_, err := io.ReadFull(conn, tlsHeader[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := int(binary.BigEndian.Uint16(tlsHeader[3:]))
|
||||
buffer := buf.NewSize(tlsHeaderSize + length)
|
||||
common.Must1(buffer.Write(tlsHeader[:]))
|
||||
_, err = buffer.ReadFullFrom(conn, length)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
}
|
||||
return buffer, err
|
||||
}
|
||||
|
||||
func VerifyClientHello(frame []byte, password string) error {
|
||||
const minLen = tlsHeaderSize + 1 + 3 + 2 + tlsRandomSize + 1 + tlsSessionIDSize
|
||||
const hmacIndex = sessionIDLengthIndex + 1 + tlsSessionIDSize - hmacSize
|
||||
if len(frame) < minLen {
|
||||
return io.ErrUnexpectedEOF
|
||||
} else if frame[0] != handshake {
|
||||
return E.New("unexpected record type")
|
||||
} else if frame[5] != clientHello {
|
||||
return E.New("unexpected handshake type")
|
||||
} else if frame[sessionIDLengthIndex] != tlsSessionIDSize {
|
||||
return E.New("unexpected session id length")
|
||||
}
|
||||
hmacSHA1Hash := hmac.New(sha1.New, []byte(password))
|
||||
hmacSHA1Hash.Write(frame[tlsHeaderSize:hmacIndex])
|
||||
hmacSHA1Hash.Write(rw.ZeroBytes[:4])
|
||||
hmacSHA1Hash.Write(frame[hmacIndex+hmacSize:])
|
||||
if !hmac.Equal(frame[hmacIndex:hmacIndex+hmacSize], hmacSHA1Hash.Sum(nil)[:hmacSize]) {
|
||||
return E.New("hmac mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ExtractServerRandom(frame []byte) []byte {
|
||||
const minLen = tlsHeaderSize + 1 + 3 + 2 + tlsRandomSize
|
||||
|
||||
if len(frame) < minLen || frame[0] != handshake || frame[5] != serverHello {
|
||||
return nil
|
||||
}
|
||||
|
||||
serverRandom := make([]byte, tlsRandomSize)
|
||||
copy(serverRandom, frame[serverRandomIndex:serverRandomIndex+tlsRandomSize])
|
||||
return serverRandom
|
||||
}
|
||||
|
||||
func IsServerHelloSupportTLS13(frame []byte) bool {
|
||||
if len(frame) < sessionIDLengthIndex {
|
||||
return false
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(frame[sessionIDLengthIndex:])
|
||||
|
||||
var sessionIdLength uint8
|
||||
err := binary.Read(reader, binary.BigEndian, &sessionIdLength)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = io.CopyN(io.Discard, reader, int64(sessionIdLength))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err = io.CopyN(io.Discard, reader, 3)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var extensionListLength uint16
|
||||
err = binary.Read(reader, binary.BigEndian, &extensionListLength)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for i := uint16(0); i < extensionListLength; i++ {
|
||||
var extensionType uint16
|
||||
err = binary.Read(reader, binary.BigEndian, &extensionType)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var extensionLength uint16
|
||||
err = binary.Read(reader, binary.BigEndian, &extensionLength)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if extensionType != 43 {
|
||||
_, err = io.CopyN(io.Discard, reader, int64(extensionLength))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
if extensionLength != 2 {
|
||||
return false
|
||||
}
|
||||
var extensionValue uint16
|
||||
err = binary.Read(reader, binary.BigEndian, &extensionValue)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return extensionValue == 0x0304
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CopyByFrameUntilHMACMatches(conn net.Conn, handshakeConn net.Conn, hmacVerify hash.Hash, hmacReset func()) (*buf.Buffer, error) {
|
||||
for {
|
||||
frameBuffer, err := ExtractFrame(conn)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client record")
|
||||
}
|
||||
frame := frameBuffer.Bytes()
|
||||
if len(frame) > tlsHmacHeaderSize && frame[0] == applicationData {
|
||||
hmacReset()
|
||||
hmacVerify.Write(frame[tlsHmacHeaderSize:])
|
||||
hmacHash := hmacVerify.Sum(nil)[:4]
|
||||
if bytes.Equal(hmacHash, frame[tlsHeaderSize:tlsHmacHeaderSize]) {
|
||||
hmacReset()
|
||||
hmacVerify.Write(frame[tlsHmacHeaderSize:])
|
||||
hmacVerify.Write(frame[tlsHeaderSize:tlsHmacHeaderSize])
|
||||
frameBuffer.Advance(tlsHmacHeaderSize)
|
||||
return frameBuffer, nil
|
||||
}
|
||||
}
|
||||
_, err = handshakeConn.Write(frame)
|
||||
frameBuffer.Release()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "write clint frame")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CopyByFrameWithModification(conn net.Conn, handshakeConn net.Conn, password string, serverRandom []byte, hmacWrite hash.Hash) error {
|
||||
writeKey := kdf(password, serverRandom)
|
||||
writer := bufio.NewVectorisedWriter(handshakeConn)
|
||||
for {
|
||||
frameBuffer, err := ExtractFrame(conn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read server record")
|
||||
}
|
||||
frame := frameBuffer.Bytes()
|
||||
if frame[0] == applicationData {
|
||||
xorSlice(frame[tlsHeaderSize:], writeKey)
|
||||
hmacWrite.Write(frame[tlsHeaderSize:])
|
||||
binary.BigEndian.PutUint16(frame[3:], uint16(len(frame)-tlsHeaderSize+hmacSize))
|
||||
hmacHash := hmacWrite.Sum(nil)[:4]
|
||||
_, err = bufio.WriteVectorised(writer, [][]byte{frame[:tlsHeaderSize], hmacHash, frame[tlsHeaderSize:]})
|
||||
frameBuffer.Release()
|
||||
if err != nil {
|
||||
return E.Cause(err, "write modified server frame")
|
||||
}
|
||||
} else {
|
||||
_, err = handshakeConn.Write(frame)
|
||||
frameBuffer.Release()
|
||||
if err != nil {
|
||||
return E.Cause(err, "write server frame")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
transport/shadowtls/tls/README.md
Normal file
5
transport/shadowtls/tls/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# tls
|
||||
|
||||
crypto/tls fork for shadowtls v3
|
||||
|
||||
version: go1.20.0
|
||||
99
transport/shadowtls/tls/alert.go
Normal file
99
transport/shadowtls/tls/alert.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import "strconv"
|
||||
|
||||
type alert uint8
|
||||
|
||||
const (
|
||||
// alert level
|
||||
alertLevelWarning = 1
|
||||
alertLevelError = 2
|
||||
)
|
||||
|
||||
const (
|
||||
alertCloseNotify alert = 0
|
||||
alertUnexpectedMessage alert = 10
|
||||
alertBadRecordMAC alert = 20
|
||||
alertDecryptionFailed alert = 21
|
||||
alertRecordOverflow alert = 22
|
||||
alertDecompressionFailure alert = 30
|
||||
alertHandshakeFailure alert = 40
|
||||
alertBadCertificate alert = 42
|
||||
alertUnsupportedCertificate alert = 43
|
||||
alertCertificateRevoked alert = 44
|
||||
alertCertificateExpired alert = 45
|
||||
alertCertificateUnknown alert = 46
|
||||
alertIllegalParameter alert = 47
|
||||
alertUnknownCA alert = 48
|
||||
alertAccessDenied alert = 49
|
||||
alertDecodeError alert = 50
|
||||
alertDecryptError alert = 51
|
||||
alertExportRestriction alert = 60
|
||||
alertProtocolVersion alert = 70
|
||||
alertInsufficientSecurity alert = 71
|
||||
alertInternalError alert = 80
|
||||
alertInappropriateFallback alert = 86
|
||||
alertUserCanceled alert = 90
|
||||
alertNoRenegotiation alert = 100
|
||||
alertMissingExtension alert = 109
|
||||
alertUnsupportedExtension alert = 110
|
||||
alertCertificateUnobtainable alert = 111
|
||||
alertUnrecognizedName alert = 112
|
||||
alertBadCertificateStatusResponse alert = 113
|
||||
alertBadCertificateHashValue alert = 114
|
||||
alertUnknownPSKIdentity alert = 115
|
||||
alertCertificateRequired alert = 116
|
||||
alertNoApplicationProtocol alert = 120
|
||||
)
|
||||
|
||||
var alertText = map[alert]string{
|
||||
alertCloseNotify: "close notify",
|
||||
alertUnexpectedMessage: "unexpected message",
|
||||
alertBadRecordMAC: "bad record MAC",
|
||||
alertDecryptionFailed: "decryption failed",
|
||||
alertRecordOverflow: "record overflow",
|
||||
alertDecompressionFailure: "decompression failure",
|
||||
alertHandshakeFailure: "handshake failure",
|
||||
alertBadCertificate: "bad certificate",
|
||||
alertUnsupportedCertificate: "unsupported certificate",
|
||||
alertCertificateRevoked: "revoked certificate",
|
||||
alertCertificateExpired: "expired certificate",
|
||||
alertCertificateUnknown: "unknown certificate",
|
||||
alertIllegalParameter: "illegal parameter",
|
||||
alertUnknownCA: "unknown certificate authority",
|
||||
alertAccessDenied: "access denied",
|
||||
alertDecodeError: "error decoding message",
|
||||
alertDecryptError: "error decrypting message",
|
||||
alertExportRestriction: "export restriction",
|
||||
alertProtocolVersion: "protocol version not supported",
|
||||
alertInsufficientSecurity: "insufficient security level",
|
||||
alertInternalError: "internal error",
|
||||
alertInappropriateFallback: "inappropriate fallback",
|
||||
alertUserCanceled: "user canceled",
|
||||
alertNoRenegotiation: "no renegotiation",
|
||||
alertMissingExtension: "missing extension",
|
||||
alertUnsupportedExtension: "unsupported extension",
|
||||
alertCertificateUnobtainable: "certificate unobtainable",
|
||||
alertUnrecognizedName: "unrecognized name",
|
||||
alertBadCertificateStatusResponse: "bad certificate status response",
|
||||
alertBadCertificateHashValue: "bad certificate hash value",
|
||||
alertUnknownPSKIdentity: "unknown PSK identity",
|
||||
alertCertificateRequired: "certificate required",
|
||||
alertNoApplicationProtocol: "no application protocol",
|
||||
}
|
||||
|
||||
func (e alert) String() string {
|
||||
s, ok := alertText[e]
|
||||
if ok {
|
||||
return "tls: " + s
|
||||
}
|
||||
return "tls: alert(" + strconv.Itoa(int(e)) + ")"
|
||||
}
|
||||
|
||||
func (e alert) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
293
transport/shadowtls/tls/auth.go
Normal file
293
transport/shadowtls/tls/auth.go
Normal file
@@ -0,0 +1,293 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// verifyHandshakeSignature verifies a signature against pre-hashed
|
||||
// (if required) handshake contents.
|
||||
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
|
||||
switch sigType {
|
||||
case signatureECDSA:
|
||||
pubKey, ok := pubkey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
|
||||
}
|
||||
if !ecdsa.VerifyASN1(pubKey, signed, sig) {
|
||||
return errors.New("ECDSA verification failure")
|
||||
}
|
||||
case signatureEd25519:
|
||||
pubKey, ok := pubkey.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
|
||||
}
|
||||
if !ed25519.Verify(pubKey, signed, sig) {
|
||||
return errors.New("Ed25519 verification failure")
|
||||
}
|
||||
case signaturePKCS1v15:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
||||
}
|
||||
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
case signatureRSAPSS:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
||||
}
|
||||
signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
|
||||
if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("internal error: unknown signature type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
|
||||
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
|
||||
)
|
||||
|
||||
var signaturePadding = []byte{
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
}
|
||||
|
||||
// signedMessage returns the pre-hashed (if necessary) message to be signed by
|
||||
// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
|
||||
func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
|
||||
if sigHash == directSigning {
|
||||
b := &bytes.Buffer{}
|
||||
b.Write(signaturePadding)
|
||||
io.WriteString(b, context)
|
||||
b.Write(transcript.Sum(nil))
|
||||
return b.Bytes()
|
||||
}
|
||||
h := sigHash.New()
|
||||
h.Write(signaturePadding)
|
||||
io.WriteString(h, context)
|
||||
h.Write(transcript.Sum(nil))
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// typeAndHashFromSignatureScheme returns the corresponding signature type and
|
||||
// crypto.Hash for a given TLS SignatureScheme.
|
||||
func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
|
||||
switch signatureAlgorithm {
|
||||
case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
|
||||
sigType = signaturePKCS1v15
|
||||
case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
|
||||
sigType = signatureRSAPSS
|
||||
case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
|
||||
sigType = signatureECDSA
|
||||
case Ed25519:
|
||||
sigType = signatureEd25519
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
||||
}
|
||||
switch signatureAlgorithm {
|
||||
case PKCS1WithSHA1, ECDSAWithSHA1:
|
||||
hash = crypto.SHA1
|
||||
case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
|
||||
hash = crypto.SHA256
|
||||
case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
|
||||
hash = crypto.SHA384
|
||||
case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
|
||||
hash = crypto.SHA512
|
||||
case Ed25519:
|
||||
hash = directSigning
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
||||
}
|
||||
return sigType, hash, nil
|
||||
}
|
||||
|
||||
// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
|
||||
// a given public key used with TLS 1.0 and 1.1, before the introduction of
|
||||
// signature algorithm negotiation.
|
||||
func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
|
||||
switch pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
return signaturePKCS1v15, crypto.MD5SHA1, nil
|
||||
case *ecdsa.PublicKey:
|
||||
return signatureECDSA, crypto.SHA1, nil
|
||||
case ed25519.PublicKey:
|
||||
// RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
|
||||
// but it requires holding on to a handshake transcript to do a
|
||||
// full signature, and not even OpenSSL bothers with the
|
||||
// complexity, so we can't even test it properly.
|
||||
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
|
||||
}
|
||||
}
|
||||
|
||||
var rsaSignatureSchemes = []struct {
|
||||
scheme SignatureScheme
|
||||
minModulusBytes int
|
||||
maxVersion uint16
|
||||
}{
|
||||
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
|
||||
// emLen >= hLen + sLen + 2
|
||||
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
|
||||
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
|
||||
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
|
||||
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
|
||||
// emLen >= len(prefix) + hLen + 11
|
||||
// TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
|
||||
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
|
||||
}
|
||||
|
||||
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
|
||||
// for a given certificate, based on the public key and the protocol version,
|
||||
// and optionally filtered by its explicit SupportedSignatureAlgorithms.
|
||||
//
|
||||
// This function must be kept in sync with supportedSignatureAlgorithms.
|
||||
// FIPS filtering is applied in the caller, selectSignatureScheme.
|
||||
func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
|
||||
priv, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var sigAlgs []SignatureScheme
|
||||
switch pub := priv.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if version != VersionTLS13 {
|
||||
// In TLS 1.2 and earlier, ECDSA algorithms are not
|
||||
// constrained to a single curve.
|
||||
sigAlgs = []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512,
|
||||
ECDSAWithSHA1,
|
||||
}
|
||||
break
|
||||
}
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
|
||||
case elliptic.P384():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
|
||||
case elliptic.P521():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
size := pub.Size()
|
||||
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
|
||||
for _, candidate := range rsaSignatureSchemes {
|
||||
if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
|
||||
sigAlgs = append(sigAlgs, candidate.scheme)
|
||||
}
|
||||
}
|
||||
case ed25519.PublicKey:
|
||||
sigAlgs = []SignatureScheme{Ed25519}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if cert.SupportedSignatureAlgorithms != nil {
|
||||
var filteredSigAlgs []SignatureScheme
|
||||
for _, sigAlg := range sigAlgs {
|
||||
if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
|
||||
filteredSigAlgs = append(filteredSigAlgs, sigAlg)
|
||||
}
|
||||
}
|
||||
return filteredSigAlgs
|
||||
}
|
||||
return sigAlgs
|
||||
}
|
||||
|
||||
// selectSignatureScheme picks a SignatureScheme from the peer's preference list
|
||||
// that works with the selected certificate. It's only called for protocol
|
||||
// versions that support signature algorithms, so TLS 1.2 and 1.3.
|
||||
func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
|
||||
supportedAlgs := signatureSchemesForCertificate(vers, c)
|
||||
if len(supportedAlgs) == 0 {
|
||||
return 0, unsupportedCertificateError(c)
|
||||
}
|
||||
if len(peerAlgs) == 0 && vers == VersionTLS12 {
|
||||
// For TLS 1.2, if the client didn't send signature_algorithms then we
|
||||
// can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
|
||||
peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
|
||||
}
|
||||
// Pick signature scheme in the peer's preference order, as our
|
||||
// preference order is not configurable.
|
||||
for _, preferredAlg := range peerAlgs {
|
||||
if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, fipsSupportedSignatureAlgorithms) {
|
||||
continue
|
||||
}
|
||||
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
||||
return preferredAlg, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
|
||||
}
|
||||
|
||||
// unsupportedCertificateError returns a helpful error for certificates with
|
||||
// an unsupported private key.
|
||||
func unsupportedCertificateError(cert *Certificate) error {
|
||||
switch cert.PrivateKey.(type) {
|
||||
case rsa.PrivateKey, ecdsa.PrivateKey:
|
||||
return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
|
||||
cert.PrivateKey, cert.PrivateKey)
|
||||
case *ed25519.PrivateKey:
|
||||
return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
|
||||
}
|
||||
|
||||
signer, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
|
||||
cert.PrivateKey)
|
||||
}
|
||||
|
||||
switch pub := signer.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
case elliptic.P384():
|
||||
case elliptic.P521():
|
||||
default:
|
||||
return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
|
||||
case ed25519.PublicKey:
|
||||
default:
|
||||
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
|
||||
}
|
||||
|
||||
if cert.SupportedSignatureAlgorithms != nil {
|
||||
return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
|
||||
}
|
||||
|
||||
return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
|
||||
}
|
||||
98
transport/shadowtls/tls/boring.go
Normal file
98
transport/shadowtls/tls/boring.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build boringcrypto
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/internal/boring/fipstls"
|
||||
)
|
||||
|
||||
// needFIPS returns fipstls.Required(); it avoids a new import in common.go.
|
||||
func needFIPS() bool {
|
||||
return fipstls.Required()
|
||||
}
|
||||
|
||||
// fipsMinVersion replaces c.minVersion in FIPS-only mode.
|
||||
func fipsMinVersion(c *Config) uint16 {
|
||||
// FIPS requires TLS 1.2.
|
||||
return VersionTLS12
|
||||
}
|
||||
|
||||
// fipsMaxVersion replaces c.maxVersion in FIPS-only mode.
|
||||
func fipsMaxVersion(c *Config) uint16 {
|
||||
// FIPS requires TLS 1.2.
|
||||
return VersionTLS12
|
||||
}
|
||||
|
||||
// default defaultFIPSCurvePreferences is the FIPS-allowed curves,
|
||||
// in preference order (most preferable first).
|
||||
var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
|
||||
|
||||
// fipsCurvePreferences replaces c.curvePreferences in FIPS-only mode.
|
||||
func fipsCurvePreferences(c *Config) []CurveID {
|
||||
if c == nil || len(c.CurvePreferences) == 0 {
|
||||
return defaultFIPSCurvePreferences
|
||||
}
|
||||
var list []CurveID
|
||||
for _, id := range c.CurvePreferences {
|
||||
for _, allowed := range defaultFIPSCurvePreferences {
|
||||
if id == allowed {
|
||||
list = append(list, id)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// defaultCipherSuitesFIPS are the FIPS-allowed cipher suites.
|
||||
var defaultCipherSuitesFIPS = []uint16{
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
}
|
||||
|
||||
// fipsCipherSuites replaces c.cipherSuites in FIPS-only mode.
|
||||
func fipsCipherSuites(c *Config) []uint16 {
|
||||
if c == nil || c.CipherSuites == nil {
|
||||
return defaultCipherSuitesFIPS
|
||||
}
|
||||
list := make([]uint16, 0, len(defaultCipherSuitesFIPS))
|
||||
for _, id := range c.CipherSuites {
|
||||
for _, allowed := range defaultCipherSuitesFIPS {
|
||||
if id == allowed {
|
||||
list = append(list, id)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// fipsSupportedSignatureAlgorithms currently are a subset of
|
||||
// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1.
|
||||
var fipsSupportedSignatureAlgorithms = []SignatureScheme{
|
||||
PSSWithSHA256,
|
||||
PSSWithSHA384,
|
||||
PSSWithSHA512,
|
||||
PKCS1WithSHA256,
|
||||
ECDSAWithP256AndSHA256,
|
||||
PKCS1WithSHA384,
|
||||
ECDSAWithP384AndSHA384,
|
||||
PKCS1WithSHA512,
|
||||
ECDSAWithP521AndSHA512,
|
||||
}
|
||||
|
||||
// supportedSignatureAlgorithms returns the supported signature algorithms.
|
||||
func supportedSignatureAlgorithms() []SignatureScheme {
|
||||
if !needFIPS() {
|
||||
return defaultSupportedSignatureAlgorithms
|
||||
}
|
||||
return fipsSupportedSignatureAlgorithms
|
||||
}
|
||||
95
transport/shadowtls/tls/cache.go
Normal file
95
transport/shadowtls/tls/cache.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type cacheEntry struct {
|
||||
refs atomic.Int64
|
||||
cert *x509.Certificate
|
||||
}
|
||||
|
||||
// certCache implements an intern table for reference counted x509.Certificates,
|
||||
// implemented in a similar fashion to BoringSSL's CRYPTO_BUFFER_POOL. This
|
||||
// allows for a single x509.Certificate to be kept in memory and referenced from
|
||||
// multiple Conns. Returned references should not be mutated by callers. Certificates
|
||||
// are still safe to use after they are removed from the cache.
|
||||
//
|
||||
// Certificates are returned wrapped in a activeCert struct that should be held by
|
||||
// the caller. When references to the activeCert are freed, the number of references
|
||||
// to the certificate in the cache is decremented. Once the number of references
|
||||
// reaches zero, the entry is evicted from the cache.
|
||||
//
|
||||
// The main difference between this implementation and CRYPTO_BUFFER_POOL is that
|
||||
// CRYPTO_BUFFER_POOL is a more generic structure which supports blobs of data,
|
||||
// rather than specific structures. Since we only care about x509.Certificates,
|
||||
// certCache is implemented as a specific cache, rather than a generic one.
|
||||
//
|
||||
// See https://boringssl.googlesource.com/boringssl/+/master/include/openssl/pool.h
|
||||
// and https://boringssl.googlesource.com/boringssl/+/master/crypto/pool/pool.c
|
||||
// for the BoringSSL reference.
|
||||
type certCache struct {
|
||||
sync.Map
|
||||
}
|
||||
|
||||
var clientCertCache = new(certCache)
|
||||
|
||||
// activeCert is a handle to a certificate held in the cache. Once there are
|
||||
// no alive activeCerts for a given certificate, the certificate is removed
|
||||
// from the cache by a finalizer.
|
||||
type activeCert struct {
|
||||
cert *x509.Certificate
|
||||
}
|
||||
|
||||
// active increments the number of references to the entry, wraps the
|
||||
// certificate in the entry in a activeCert, and sets the finalizer.
|
||||
//
|
||||
// Note that there is a race between active and the finalizer set on the
|
||||
// returned activeCert, triggered if active is called after the ref count is
|
||||
// decremented such that refs may be > 0 when evict is called. We consider this
|
||||
// safe, since the caller holding an activeCert for an entry that is no longer
|
||||
// in the cache is fine, with the only side effect being the memory overhead of
|
||||
// there being more than one distinct reference to a certificate alive at once.
|
||||
func (cc *certCache) active(e *cacheEntry) *activeCert {
|
||||
e.refs.Add(1)
|
||||
a := &activeCert{e.cert}
|
||||
runtime.SetFinalizer(a, func(_ *activeCert) {
|
||||
if e.refs.Add(-1) == 0 {
|
||||
cc.evict(e)
|
||||
}
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// evict removes a cacheEntry from the cache.
|
||||
func (cc *certCache) evict(e *cacheEntry) {
|
||||
cc.Delete(string(e.cert.Raw))
|
||||
}
|
||||
|
||||
// newCert returns a x509.Certificate parsed from der. If there is already a copy
|
||||
// of the certificate in the cache, a reference to the existing certificate will
|
||||
// be returned. Otherwise, a fresh certificate will be added to the cache, and
|
||||
// the reference returned. The returned reference should not be mutated.
|
||||
func (cc *certCache) newCert(der []byte) (*activeCert, error) {
|
||||
if entry, ok := cc.Load(string(der)); ok {
|
||||
return cc.active(entry.(*cacheEntry)), nil
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(der)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entry := &cacheEntry{cert: cert}
|
||||
if entry, loaded := cc.LoadOrStore(string(der), entry); loaded {
|
||||
return cc.active(entry.(*cacheEntry)), nil
|
||||
}
|
||||
return cc.active(entry), nil
|
||||
}
|
||||
701
transport/shadowtls/tls/cipher_suites.go
Normal file
701
transport/shadowtls/tls/cipher_suites.go
Normal file
@@ -0,0 +1,701 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"hash"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
||||
// accept and expose cipher suite IDs instead of this type.
|
||||
type CipherSuite struct {
|
||||
ID uint16
|
||||
Name string
|
||||
|
||||
// Supported versions is the list of TLS protocol versions that can
|
||||
// negotiate this cipher suite.
|
||||
SupportedVersions []uint16
|
||||
|
||||
// Insecure is true if the cipher suite has known security issues
|
||||
// due to its primitives, design, or implementation.
|
||||
Insecure bool
|
||||
}
|
||||
|
||||
var (
|
||||
supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
|
||||
supportedOnlyTLS12 = []uint16{VersionTLS12}
|
||||
supportedOnlyTLS13 = []uint16{VersionTLS13}
|
||||
)
|
||||
|
||||
// CipherSuites returns a list of cipher suites currently implemented by this
|
||||
// package, excluding those with security issues, which are returned by
|
||||
// InsecureCipherSuites.
|
||||
//
|
||||
// The list is sorted by ID. Note that the default cipher suites selected by
|
||||
// this package might depend on logic that can't be captured by a static list,
|
||||
// and might not match those returned by this function.
|
||||
func CipherSuites() []*CipherSuite {
|
||||
return []*CipherSuite{
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
|
||||
{TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
|
||||
{TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
|
||||
{TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
|
||||
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||
}
|
||||
}
|
||||
|
||||
// InsecureCipherSuites returns a list of cipher suites currently implemented by
|
||||
// this package and which have security issues.
|
||||
//
|
||||
// Most applications should not use the cipher suites in this list, and should
|
||||
// only use those returned by CipherSuites.
|
||||
func InsecureCipherSuites() []*CipherSuite {
|
||||
// This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
|
||||
// cipherSuitesPreferenceOrder for details.
|
||||
return []*CipherSuite{
|
||||
{TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
}
|
||||
}
|
||||
|
||||
// CipherSuiteName returns the standard name for the passed cipher suite ID
|
||||
// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
|
||||
// of the ID value if the cipher suite is not implemented by this package.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
for _, c := range CipherSuites() {
|
||||
if c.ID == id {
|
||||
return c.Name
|
||||
}
|
||||
}
|
||||
for _, c := range InsecureCipherSuites() {
|
||||
if c.ID == id {
|
||||
return c.Name
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("0x%04X", id)
|
||||
}
|
||||
|
||||
const (
|
||||
// suiteECDHE indicates that the cipher suite involves elliptic curve
|
||||
// Diffie-Hellman. This means that it should only be selected when the
|
||||
// client indicates that it supports ECC with a curve and point format
|
||||
// that we're happy with.
|
||||
suiteECDHE = 1 << iota
|
||||
// suiteECSign indicates that the cipher suite involves an ECDSA or
|
||||
// EdDSA signature and therefore may only be selected when the server's
|
||||
// certificate is ECDSA or EdDSA. If this is not set then the cipher suite
|
||||
// is RSA based.
|
||||
suiteECSign
|
||||
// suiteTLS12 indicates that the cipher suite should only be advertised
|
||||
// and accepted when using TLS 1.2.
|
||||
suiteTLS12
|
||||
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
|
||||
// handshake hash.
|
||||
suiteSHA384
|
||||
)
|
||||
|
||||
// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
|
||||
// mechanism, as well as the cipher+MAC pair or the AEAD.
|
||||
type cipherSuite struct {
|
||||
id uint16
|
||||
// the lengths, in bytes, of the key material needed for each component.
|
||||
keyLen int
|
||||
macLen int
|
||||
ivLen int
|
||||
ka func(version uint16) keyAgreement
|
||||
// flags is a bitmask of the suite* values, above.
|
||||
flags int
|
||||
cipher func(key, iv []byte, isRead bool) any
|
||||
mac func(key []byte) hash.Hash
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
}
|
||||
|
||||
var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
|
||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
|
||||
}
|
||||
|
||||
// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
|
||||
// is also in supportedIDs and passes the ok filter.
|
||||
func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
|
||||
for _, id := range ids {
|
||||
candidate := cipherSuiteByID(id)
|
||||
if candidate == nil || !ok(candidate) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, suppID := range supportedIDs {
|
||||
if id == suppID {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
|
||||
// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
|
||||
type cipherSuiteTLS13 struct {
|
||||
id uint16
|
||||
keyLen int
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
|
||||
{TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
|
||||
{TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
|
||||
{TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
|
||||
}
|
||||
|
||||
// cipherSuitesPreferenceOrder is the order in which we'll select (on the
|
||||
// server) or advertise (on the client) TLS 1.0–1.2 cipher suites.
|
||||
//
|
||||
// Cipher suites are filtered but not reordered based on the application and
|
||||
// peer's preferences, meaning we'll never select a suite lower in this list if
|
||||
// any higher one is available. This makes it more defensible to keep weaker
|
||||
// cipher suites enabled, especially on the server side where we get the last
|
||||
// word, since there are no known downgrade attacks on cipher suites selection.
|
||||
//
|
||||
// The list is sorted by applying the following priority rules, stopping at the
|
||||
// first (most important) applicable one:
|
||||
//
|
||||
// - Anything else comes before RC4
|
||||
//
|
||||
// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
|
||||
//
|
||||
// - Anything else comes before CBC_SHA256
|
||||
//
|
||||
// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
|
||||
// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
|
||||
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
||||
//
|
||||
// - Anything else comes before 3DES
|
||||
//
|
||||
// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
|
||||
// birthday attacks. See https://sweet32.info.
|
||||
//
|
||||
// - ECDHE comes before anything else
|
||||
//
|
||||
// Once we got the broken stuff out of the way, the most important
|
||||
// property a cipher suite can have is forward secrecy. We don't
|
||||
// implement FFDHE, so that means ECDHE.
|
||||
//
|
||||
// - AEADs come before CBC ciphers
|
||||
//
|
||||
// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
|
||||
// are fundamentally fragile, and suffered from an endless sequence of
|
||||
// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
|
||||
// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
|
||||
// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
|
||||
//
|
||||
// - AES comes before ChaCha20
|
||||
//
|
||||
// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
|
||||
// than ChaCha20Poly1305.
|
||||
//
|
||||
// When AES hardware is not available, AES-128-GCM is one or more of: much
|
||||
// slower, way more complex, and less safe (because not constant time)
|
||||
// than ChaCha20Poly1305.
|
||||
//
|
||||
// We use this list if we think both peers have AES hardware, and
|
||||
// cipherSuitesPreferenceOrderNoAES otherwise.
|
||||
//
|
||||
// - AES-128 comes before AES-256
|
||||
//
|
||||
// The only potential advantages of AES-256 are better multi-target
|
||||
// margins, and hypothetical post-quantum properties. Neither apply to
|
||||
// TLS, and AES-256 is slower due to its four extra rounds (which don't
|
||||
// contribute to the advantages above).
|
||||
//
|
||||
// - ECDSA comes before RSA
|
||||
//
|
||||
// The relative order of ECDSA and RSA cipher suites doesn't matter,
|
||||
// as they depend on the certificate. Pick one to get a stable order.
|
||||
var cipherSuitesPreferenceOrder = []uint16{
|
||||
// AEADs w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
|
||||
// CBC w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
|
||||
// AEADs w/o ECDHE
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
|
||||
// CBC w/o ECDHE
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
|
||||
// 3DES
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
|
||||
// CBC_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
|
||||
// RC4
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
var cipherSuitesPreferenceOrderNoAES = []uint16{
|
||||
// ChaCha20Poly1305
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
|
||||
// AES-GCM w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
|
||||
// The rest of cipherSuitesPreferenceOrder.
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
// disabledCipherSuites are not used unless explicitly listed in
|
||||
// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder.
|
||||
var disabledCipherSuites = []uint16{
|
||||
// CBC_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
|
||||
// RC4
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites)
|
||||
defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen]
|
||||
)
|
||||
|
||||
// defaultCipherSuitesTLS13 is also the preference order, since there are no
|
||||
// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
|
||||
// cipherSuitesPreferenceOrder applies.
|
||||
var defaultCipherSuitesTLS13 = []uint16{
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
|
||||
var defaultCipherSuitesTLS13NoAES = []uint16{
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
}
|
||||
|
||||
var (
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
||||
|
||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
||||
)
|
||||
|
||||
var aesgcmCiphers = map[uint16]bool{
|
||||
// TLS 1.2
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
|
||||
// TLS 1.3
|
||||
TLS_AES_128_GCM_SHA256: true,
|
||||
TLS_AES_256_GCM_SHA384: true,
|
||||
}
|
||||
|
||||
var nonAESGCMAEADCiphers = map[uint16]bool{
|
||||
// TLS 1.2
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true,
|
||||
// TLS 1.3
|
||||
TLS_CHACHA20_POLY1305_SHA256: true,
|
||||
}
|
||||
|
||||
// aesgcmPreferred returns whether the first known cipher in the preference list
|
||||
// is an AES-GCM cipher, implying the peer has hardware support for it.
|
||||
func aesgcmPreferred(ciphers []uint16) bool {
|
||||
for _, cID := range ciphers {
|
||||
if c := cipherSuiteByID(cID); c != nil {
|
||||
return aesgcmCiphers[cID]
|
||||
}
|
||||
if c := cipherSuiteTLS13ByID(cID); c != nil {
|
||||
return aesgcmCiphers[cID]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cipherRC4(key, iv []byte, isRead bool) any {
|
||||
cipher, _ := rc4.NewCipher(key)
|
||||
return cipher
|
||||
}
|
||||
|
||||
func cipher3DES(key, iv []byte, isRead bool) any {
|
||||
block, _ := des.NewTripleDESCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
func cipherAES(key, iv []byte, isRead bool) any {
|
||||
block, _ := aes.NewCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
// macSHA1 returns a SHA-1 based constant time MAC.
|
||||
func macSHA1(key []byte) hash.Hash {
|
||||
h := sha1.New
|
||||
// The BoringCrypto SHA1 does not have a constant-time
|
||||
// checksum function, so don't try to use it.
|
||||
// if !boring.Enabled {
|
||||
h = newConstantTimeHash(h)
|
||||
//}
|
||||
return hmac.New(h, key)
|
||||
}
|
||||
|
||||
// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
|
||||
// is currently only used in disabled-by-default cipher suites.
|
||||
func macSHA256(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}
|
||||
|
||||
type aead interface {
|
||||
cipher.AEAD
|
||||
|
||||
// explicitNonceLen returns the number of bytes of explicit nonce
|
||||
// included in each record. This is eight for older AEADs and
|
||||
// zero for modern ones.
|
||||
explicitNonceLen() int
|
||||
}
|
||||
|
||||
const (
|
||||
aeadNonceLength = 12
|
||||
noncePrefixLength = 4
|
||||
)
|
||||
|
||||
// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
|
||||
// each call.
|
||||
type prefixNonceAEAD struct {
|
||||
// nonce contains the fixed part of the nonce in the first four bytes.
|
||||
nonce [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
|
||||
func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
|
||||
|
||||
func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
|
||||
}
|
||||
|
||||
// xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
|
||||
// before each call.
|
||||
type xorNonceAEAD struct {
|
||||
nonceMask [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
|
||||
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
|
||||
|
||||
func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func aeadAESGCM(key, noncePrefix []byte) aead {
|
||||
if len(noncePrefix) != noncePrefixLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var aead cipher.AEAD
|
||||
//if boring.Enabled {
|
||||
// aead, err = boring.NewGCMTLS(aes)
|
||||
//} else {
|
||||
// boring.Unreachable()
|
||||
aead, err = cipher.NewGCM(aes)
|
||||
//}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &prefixNonceAEAD{aead: aead}
|
||||
copy(ret.nonce[:], noncePrefix)
|
||||
return ret
|
||||
}
|
||||
|
||||
func aeadAESGCMTLS13(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
type constantTimeHash interface {
|
||||
hash.Hash
|
||||
ConstantTimeSum(b []byte) []byte
|
||||
}
|
||||
|
||||
// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
|
||||
// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
|
||||
type cthWrapper struct {
|
||||
h constantTimeHash
|
||||
}
|
||||
|
||||
func (c *cthWrapper) Size() int { return c.h.Size() }
|
||||
func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
|
||||
func (c *cthWrapper) Reset() { c.h.Reset() }
|
||||
func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
|
||||
func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
|
||||
|
||||
func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
|
||||
// boring.Unreachable()
|
||||
return func() hash.Hash {
|
||||
return &cthWrapper{h().(constantTimeHash)}
|
||||
}
|
||||
}
|
||||
|
||||
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
|
||||
func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
|
||||
h.Reset()
|
||||
h.Write(seq)
|
||||
h.Write(header)
|
||||
h.Write(data)
|
||||
res := h.Sum(out)
|
||||
if extra != nil {
|
||||
h.Write(extra)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func rsaKA(version uint16) keyAgreement {
|
||||
return rsaKeyAgreement{}
|
||||
}
|
||||
|
||||
func ecdheECDSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: false,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func ecdheRSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: true,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
// mutualCipherSuite returns a cipherSuite given a list of supported
|
||||
// ciphersuites and the id requested by the peer.
|
||||
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteByID(id uint16) *cipherSuite {
|
||||
for _, cipherSuite := range cipherSuites {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteTLS13ByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
|
||||
for _, cipherSuite := range cipherSuitesTLS13 {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A list of cipher suite IDs that are, or have been, implemented by this
|
||||
// package.
|
||||
//
|
||||
// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
|
||||
const (
|
||||
// TLS 1.0 - 1.2 cipher suites.
|
||||
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
|
||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
|
||||
|
||||
// TLS 1.3 cipher suites.
|
||||
TLS_AES_128_GCM_SHA256 uint16 = 0x1301
|
||||
TLS_AES_256_GCM_SHA384 uint16 = 0x1302
|
||||
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
|
||||
|
||||
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
|
||||
// that the client is doing version fallback. See RFC 7507.
|
||||
TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||
|
||||
// Legacy names for the corresponding cipher suites with the correct _SHA256
|
||||
// suffix, retained for backward compatibility.
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
)
|
||||
1513
transport/shadowtls/tls/common.go
Normal file
1513
transport/shadowtls/tls/common.go
Normal file
File diff suppressed because it is too large
Load Diff
116
transport/shadowtls/tls/common_string.go
Normal file
116
transport/shadowtls/tls/common_string.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Code generated by "stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT.
|
||||
|
||||
package tls
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[PKCS1WithSHA256-1025]
|
||||
_ = x[PKCS1WithSHA384-1281]
|
||||
_ = x[PKCS1WithSHA512-1537]
|
||||
_ = x[PSSWithSHA256-2052]
|
||||
_ = x[PSSWithSHA384-2053]
|
||||
_ = x[PSSWithSHA512-2054]
|
||||
_ = x[ECDSAWithP256AndSHA256-1027]
|
||||
_ = x[ECDSAWithP384AndSHA384-1283]
|
||||
_ = x[ECDSAWithP521AndSHA512-1539]
|
||||
_ = x[Ed25519-2055]
|
||||
_ = x[PKCS1WithSHA1-513]
|
||||
_ = x[ECDSAWithSHA1-515]
|
||||
}
|
||||
|
||||
const (
|
||||
_SignatureScheme_name_0 = "PKCS1WithSHA1"
|
||||
_SignatureScheme_name_1 = "ECDSAWithSHA1"
|
||||
_SignatureScheme_name_2 = "PKCS1WithSHA256"
|
||||
_SignatureScheme_name_3 = "ECDSAWithP256AndSHA256"
|
||||
_SignatureScheme_name_4 = "PKCS1WithSHA384"
|
||||
_SignatureScheme_name_5 = "ECDSAWithP384AndSHA384"
|
||||
_SignatureScheme_name_6 = "PKCS1WithSHA512"
|
||||
_SignatureScheme_name_7 = "ECDSAWithP521AndSHA512"
|
||||
_SignatureScheme_name_8 = "PSSWithSHA256PSSWithSHA384PSSWithSHA512Ed25519"
|
||||
)
|
||||
|
||||
var (
|
||||
_SignatureScheme_index_8 = [...]uint8{0, 13, 26, 39, 46}
|
||||
)
|
||||
|
||||
func (i SignatureScheme) String() string {
|
||||
switch {
|
||||
case i == 513:
|
||||
return _SignatureScheme_name_0
|
||||
case i == 515:
|
||||
return _SignatureScheme_name_1
|
||||
case i == 1025:
|
||||
return _SignatureScheme_name_2
|
||||
case i == 1027:
|
||||
return _SignatureScheme_name_3
|
||||
case i == 1281:
|
||||
return _SignatureScheme_name_4
|
||||
case i == 1283:
|
||||
return _SignatureScheme_name_5
|
||||
case i == 1537:
|
||||
return _SignatureScheme_name_6
|
||||
case i == 1539:
|
||||
return _SignatureScheme_name_7
|
||||
case 2052 <= i && i <= 2055:
|
||||
i -= 2052
|
||||
return _SignatureScheme_name_8[_SignatureScheme_index_8[i]:_SignatureScheme_index_8[i+1]]
|
||||
default:
|
||||
return "SignatureScheme(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[CurveP256-23]
|
||||
_ = x[CurveP384-24]
|
||||
_ = x[CurveP521-25]
|
||||
_ = x[X25519-29]
|
||||
}
|
||||
|
||||
const (
|
||||
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
|
||||
_CurveID_name_1 = "X25519"
|
||||
)
|
||||
|
||||
var (
|
||||
_CurveID_index_0 = [...]uint8{0, 9, 18, 27}
|
||||
)
|
||||
|
||||
func (i CurveID) String() string {
|
||||
switch {
|
||||
case 23 <= i && i <= 25:
|
||||
i -= 23
|
||||
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
|
||||
case i == 29:
|
||||
return _CurveID_name_1
|
||||
default:
|
||||
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[NoClientCert-0]
|
||||
_ = x[RequestClientCert-1]
|
||||
_ = x[RequireAnyClientCert-2]
|
||||
_ = x[VerifyClientCertIfGiven-3]
|
||||
_ = x[RequireAndVerifyClientCert-4]
|
||||
}
|
||||
|
||||
const _ClientAuthType_name = "NoClientCertRequestClientCertRequireAnyClientCertVerifyClientCertIfGivenRequireAndVerifyClientCert"
|
||||
|
||||
var _ClientAuthType_index = [...]uint8{0, 12, 29, 49, 72, 98}
|
||||
|
||||
func (i ClientAuthType) String() string {
|
||||
if i < 0 || i >= ClientAuthType(len(_ClientAuthType_index)-1) {
|
||||
return "ClientAuthType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _ClientAuthType_name[_ClientAuthType_index[i]:_ClientAuthType_index[i+1]]
|
||||
}
|
||||
1540
transport/shadowtls/tls/conn.go
Normal file
1540
transport/shadowtls/tls/conn.go
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user