mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
Compare commits
60 Commits
v1.1-beta1
...
v1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ebbd58b00 | ||
|
|
d0095fd0f4 | ||
|
|
66d8d563eb | ||
|
|
4bf96c7eb5 | ||
|
|
f687c25fa9 | ||
|
|
a92412ecac | ||
|
|
8dcafa5b33 | ||
|
|
7a02cb83a7 | ||
|
|
51ce672076 | ||
|
|
7734afc40c | ||
|
|
ee3cd49aa5 | ||
|
|
bf20ff84b5 | ||
|
|
c58302554c | ||
|
|
05ed88aba8 | ||
|
|
9f5cc0442b | ||
|
|
2641a43ad8 | ||
|
|
4a6ab5e9fd | ||
|
|
d1fe17a4db | ||
|
|
7c910165ef | ||
|
|
8c1fddcf8d | ||
|
|
01b4769852 | ||
|
|
a401828ed5 | ||
|
|
ffd54eef6c | ||
|
|
c16e4316d6 | ||
|
|
8b7fe20b7f | ||
|
|
696c1065b6 | ||
|
|
5d690f4147 | ||
|
|
f906641a82 | ||
|
|
89913dfa8c | ||
|
|
468778f67f | ||
|
|
22a22aebe2 | ||
|
|
a2d2ec9b45 | ||
|
|
2695b3516e | ||
|
|
3a9ef8fac0 | ||
|
|
ebad363201 | ||
|
|
11076d52cd | ||
|
|
5eb132063e | ||
|
|
13ab5d3348 | ||
|
|
ce1ddc400f | ||
|
|
2c9d25e853 | ||
|
|
3d76777760 | ||
|
|
24f4dfea04 | ||
|
|
2fc1a0a9dd | ||
|
|
617aba84e4 | ||
|
|
5510c474c7 | ||
|
|
eb2e8a0b40 | ||
|
|
972491c19d | ||
|
|
7358ca4a52 | ||
|
|
61c274045a | ||
|
|
f205140b04 | ||
|
|
1db8e03c86 | ||
|
|
2ecf86c2bc | ||
|
|
999a847e86 | ||
|
|
1f63ce5dee | ||
|
|
0ad1bbea11 | ||
|
|
b2cd78d279 | ||
|
|
d5bb58a0b4 | ||
|
|
7f84936050 | ||
|
|
6adfea0a72 | ||
|
|
10f213bf3d |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -12,7 +12,7 @@ body:
|
||||
required: true
|
||||
- label: Yes, I've searched similar issues on GitHub and didn't find any.
|
||||
required: true
|
||||
- label: Yes, I've included all information below (version, config, log, etc).
|
||||
- label: Yes, I've included all information below (version, **FULL** config, **FULL** log, etc).
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
|
||||
34
.github/workflows/test.yml
vendored
34
.github/workflows/test.yml
vendored
@@ -1,34 +0,0 @@
|
||||
name: Test build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- dev-next
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Debug build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: make test
|
||||
@@ -14,6 +14,7 @@ builds:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_clash_api
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
|
||||
@@ -14,7 +14,6 @@ RUN set -ex \
|
||||
./cmd/sing-box
|
||||
FROM alpine AS dist
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf
|
||||
RUN set -ex \
|
||||
&& apk upgrade \
|
||||
&& apk add bash tzdata ca-certificates \
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_clash_api
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
|
||||
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
|
||||
MAIN = ./cmd/sing-box
|
||||
|
||||
@@ -60,3 +60,7 @@ type DNSRule interface {
|
||||
Rule
|
||||
DisableCache() bool
|
||||
}
|
||||
|
||||
type InterfaceUpdateListener interface {
|
||||
InterfaceUpdated() error
|
||||
}
|
||||
|
||||
3
box.go
3
box.go
@@ -97,8 +97,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
|
||||
router, err := route.NewRouter(
|
||||
ctx,
|
||||
logFactory.NewLogger("router"),
|
||||
logFactory.NewLogger("dns"),
|
||||
logFactory,
|
||||
common.PtrValueOrDefault(options.Route),
|
||||
common.PtrValueOrDefault(options.DNS),
|
||||
options.Inbounds,
|
||||
|
||||
@@ -69,6 +69,20 @@ func create() (*box.Box, context.CancelFunc, error) {
|
||||
cancel()
|
||||
return nil, nil, E.Cause(err, "create service")
|
||||
}
|
||||
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
||||
defer func() {
|
||||
signal.Stop(osSignals)
|
||||
close(osSignals)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, loaded := <-osSignals
|
||||
if loaded {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
err = instance.Start()
|
||||
if err != nil {
|
||||
cancel()
|
||||
@@ -80,6 +94,7 @@ func create() (*box.Box, context.CancelFunc, error) {
|
||||
func run() error {
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
||||
defer signal.Stop(osSignals)
|
||||
for {
|
||||
instance, cancel, err := create()
|
||||
if err != nil {
|
||||
|
||||
@@ -3,7 +3,6 @@ package dialer
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -54,10 +53,13 @@ var warnTFOOnUnsupportedPlatform = warning.New(
|
||||
)
|
||||
|
||||
type DefaultDialer struct {
|
||||
dialer tfo.Dialer
|
||||
udpDialer net.Dialer
|
||||
dialer4 tfo.Dialer
|
||||
dialer6 tfo.Dialer
|
||||
udpDialer4 net.Dialer
|
||||
udpDialer6 net.Dialer
|
||||
udpListener net.ListenConfig
|
||||
bindUDPAddr string
|
||||
udpAddr4 string
|
||||
udpAddr6 string
|
||||
}
|
||||
|
||||
func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDialer {
|
||||
@@ -120,22 +122,37 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
|
||||
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
|
||||
}
|
||||
var bindUDPAddr string
|
||||
udpDialer := dialer
|
||||
var bindAddress netip.Addr
|
||||
if options.BindAddress != nil {
|
||||
bindAddress = options.BindAddress.Build()
|
||||
var (
|
||||
dialer4 = dialer
|
||||
udpDialer4 = dialer
|
||||
udpAddr4 string
|
||||
)
|
||||
if options.Inet4BindAddress != nil {
|
||||
bindAddr := options.Inet4BindAddress.Build()
|
||||
dialer4.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()}
|
||||
udpDialer4.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
|
||||
udpAddr4 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||
}
|
||||
if bindAddress.IsValid() {
|
||||
dialer.LocalAddr = &net.TCPAddr{
|
||||
IP: bindAddress.AsSlice(),
|
||||
}
|
||||
udpDialer.LocalAddr = &net.UDPAddr{
|
||||
IP: bindAddress.AsSlice(),
|
||||
}
|
||||
bindUDPAddr = M.SocksaddrFrom(bindAddress, 0).String()
|
||||
var (
|
||||
dialer6 = dialer
|
||||
udpDialer6 = dialer
|
||||
udpAddr6 string
|
||||
)
|
||||
if options.Inet6BindAddress != nil {
|
||||
bindAddr := options.Inet6BindAddress.Build()
|
||||
dialer6.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()}
|
||||
udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
|
||||
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||
}
|
||||
return &DefaultDialer{
|
||||
tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen},
|
||||
tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen},
|
||||
udpDialer4,
|
||||
udpDialer6,
|
||||
listener,
|
||||
udpAddr4,
|
||||
udpAddr6,
|
||||
}
|
||||
return &DefaultDialer{tfo.Dialer{Dialer: dialer, DisableTFO: !options.TCPFastOpen}, udpDialer, listener, bindUDPAddr}
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) {
|
||||
@@ -144,11 +161,23 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
}
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkUDP:
|
||||
return d.udpDialer.DialContext(ctx, network, address.String())
|
||||
if !address.IsIPv6() {
|
||||
return d.udpDialer4.DialContext(ctx, network, address.String())
|
||||
} else {
|
||||
return d.udpDialer6.DialContext(ctx, network, address.String())
|
||||
}
|
||||
}
|
||||
if !address.IsIPv6() {
|
||||
return DialSlowContext(&d.dialer4, ctx, network, address)
|
||||
} else {
|
||||
return DialSlowContext(&d.dialer6, ctx, network, address)
|
||||
}
|
||||
return DialSlowContext(&d.dialer, ctx, network, address)
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.bindUDPAddr)
|
||||
if !destination.IsIPv6() {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)
|
||||
} else {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +329,23 @@ func (c *ClientPacketConn) Write(b []byte) (n int, err error) {
|
||||
return c.ExtendedConn.Write(b)
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) ReadBuffer(buffer *buf.Buffer) (err error) {
|
||||
if !c.responseRead {
|
||||
err = c.readResponse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
if !c.requestWrite {
|
||||
defer buffer.Release()
|
||||
@@ -343,6 +360,11 @@ func (c *ClientPacketConn) FrontHeadroom() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
err = c.ReadBuffer(buffer)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
return c.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func ParseProtocol(name string) (Protocol, error) {
|
||||
func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
|
||||
switch p {
|
||||
case ProtocolSMux:
|
||||
session, err := smux.Server(conn, nil)
|
||||
session, err := smux.Server(conn, smuxConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
|
||||
func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
|
||||
switch p {
|
||||
case ProtocolSMux:
|
||||
session, err := smux.Client(conn, nil)
|
||||
session, err := smux.Client(conn, smuxConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -70,6 +70,12 @@ func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func smuxConfig() *smux.Config {
|
||||
config := smux.DefaultConfig()
|
||||
config.KeepAliveDisabled = true
|
||||
return config
|
||||
}
|
||||
|
||||
func yaMuxConfig() *yamux.Config {
|
||||
config := yamux.DefaultConfig()
|
||||
config.LogOutput = io.Discard
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"encoding/binary"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
@@ -29,6 +31,22 @@ func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, so
|
||||
return &Info{ProcessPath: processName, UserId: -1}, nil
|
||||
}
|
||||
|
||||
var structSize = func() int {
|
||||
value, _ := syscall.Sysctl("kern.osrelease")
|
||||
major, _, _ := strings.Cut(value, ".")
|
||||
n, _ := strconv.ParseInt(major, 10, 64)
|
||||
switch true {
|
||||
case n >= 22:
|
||||
return 408
|
||||
default:
|
||||
// from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
|
||||
// size/offset are round up (aligned) to 8 bytes in darwin
|
||||
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
|
||||
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
|
||||
return 384
|
||||
}
|
||||
}()
|
||||
|
||||
func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
||||
var spath string
|
||||
switch network {
|
||||
@@ -53,7 +71,7 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
||||
// size/offset are round up (aligned) to 8 bytes in darwin
|
||||
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
|
||||
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
|
||||
itemSize := 384
|
||||
itemSize := structSize
|
||||
if network == N.NetworkTCP {
|
||||
// rup8(sizeof(xtcpcb_n))
|
||||
itemSize += 208
|
||||
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
)
|
||||
|
||||
func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||
if !options.Enabled {
|
||||
return dialer, nil
|
||||
}
|
||||
config, err := NewClient(router, serverAddress, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -23,12 +26,15 @@ func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress
|
||||
}
|
||||
|
||||
func NewClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
if options.ECH != nil && options.ECH.Enabled {
|
||||
return newECHClient(router, serverAddress, options)
|
||||
return NewECHClient(router, serverAddress, options)
|
||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||
return newUTLSClient(router, serverAddress, options)
|
||||
return NewUTLSClient(router, serverAddress, options)
|
||||
} else {
|
||||
return newStdClient(serverAddress, options)
|
||||
return NewSTDClient(serverAddress, options)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,13 @@ type (
|
||||
)
|
||||
|
||||
type Config interface {
|
||||
ServerName() string
|
||||
SetServerName(serverName string)
|
||||
NextProtos() []string
|
||||
SetNextProtos(nextProto []string)
|
||||
Config() (*STDConfig, error)
|
||||
Client(conn net.Conn) Conn
|
||||
Clone() Config
|
||||
}
|
||||
|
||||
type ServerConfig interface {
|
||||
|
||||
@@ -20,26 +20,40 @@ import (
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type echClientConfig struct {
|
||||
type ECHClientConfig struct {
|
||||
config *cftls.Config
|
||||
}
|
||||
|
||||
func (e *echClientConfig) NextProtos() []string {
|
||||
func (e *ECHClientConfig) ServerName() string {
|
||||
return e.config.ServerName
|
||||
}
|
||||
|
||||
func (e *ECHClientConfig) SetServerName(serverName string) {
|
||||
e.config.ServerName = serverName
|
||||
}
|
||||
|
||||
func (e *ECHClientConfig) NextProtos() []string {
|
||||
return e.config.NextProtos
|
||||
}
|
||||
|
||||
func (e *echClientConfig) SetNextProtos(nextProto []string) {
|
||||
func (e *ECHClientConfig) SetNextProtos(nextProto []string) {
|
||||
e.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (e *echClientConfig) Config() (*STDConfig, error) {
|
||||
func (e *ECHClientConfig) Config() (*STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for ECH")
|
||||
}
|
||||
|
||||
func (e *echClientConfig) Client(conn net.Conn) Conn {
|
||||
func (e *ECHClientConfig) Client(conn net.Conn) Conn {
|
||||
return &echConnWrapper{cftls.Client(conn, e.config)}
|
||||
}
|
||||
|
||||
func (e *ECHClientConfig) Clone() Config {
|
||||
return &ECHClientConfig{
|
||||
config: e.config.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
type echConnWrapper struct {
|
||||
*cftls.Conn
|
||||
}
|
||||
@@ -62,7 +76,7 @@ func (c *echConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
}
|
||||
}
|
||||
|
||||
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func NewECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
@@ -162,7 +176,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
|
||||
} else {
|
||||
tlsConfig.GetClientECHConfigs = fetchECHClientConfig(router)
|
||||
}
|
||||
return &echClientConfig{&tlsConfig}, nil
|
||||
return &ECHClientConfig{&tlsConfig}, nil
|
||||
}
|
||||
|
||||
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||
|
||||
@@ -8,6 +8,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func NewECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
return nil, E.New(`ECH is not included in this build, rebuild with -tags with_ech`)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
)
|
||||
|
||||
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
return newSTDServer(ctx, logger, options)
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
return NewSTDServer(ctx, logger, options)
|
||||
}
|
||||
|
||||
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
||||
|
||||
@@ -11,11 +11,39 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type stdClientConfig struct {
|
||||
type STDClientConfig struct {
|
||||
config *tls.Config
|
||||
}
|
||||
|
||||
func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func (s *STDClientConfig) ServerName() string {
|
||||
return s.config.ServerName
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) SetServerName(serverName string) {
|
||||
s.config.ServerName = serverName
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) NextProtos() []string {
|
||||
return s.config.NextProtos
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) SetNextProtos(nextProto []string) {
|
||||
s.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) Config() (*STDConfig, error) {
|
||||
return s.config, nil
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, s.config)
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) Clone() Config {
|
||||
return &STDClientConfig{s.config.Clone()}
|
||||
}
|
||||
|
||||
func NewSTDClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
@@ -96,21 +124,5 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
return &stdClientConfig{&tlsConfig}, nil
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) NextProtos() []string {
|
||||
return s.config.NextProtos
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) SetNextProtos(nextProto []string) {
|
||||
s.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) Config() (*STDConfig, error) {
|
||||
return s.config, nil
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, s.config)
|
||||
return &STDClientConfig{&tlsConfig}, nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
var errInsecureUnused = E.New("tls: insecure unused")
|
||||
|
||||
type STDServerConfig struct {
|
||||
config *tls.Config
|
||||
logger log.Logger
|
||||
@@ -26,6 +28,14 @@ type STDServerConfig struct {
|
||||
watcher *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) ServerName() string {
|
||||
return c.config.ServerName
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) SetServerName(serverName string) {
|
||||
c.config.ServerName = serverName
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) NextProtos() []string {
|
||||
return c.config.NextProtos
|
||||
}
|
||||
@@ -34,9 +44,119 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
||||
c.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
var errInsecureUnused = E.New("tls: insecure unused")
|
||||
func (c *STDServerConfig) Config() (*STDConfig, error) {
|
||||
return c.config, nil
|
||||
}
|
||||
|
||||
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
func (c *STDServerConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, c.config)
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Server(conn net.Conn) Conn {
|
||||
return tls.Server(conn, c.config)
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Clone() Config {
|
||||
return &STDServerConfig{
|
||||
config: c.config.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Start() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Start()
|
||||
} else {
|
||||
if c.certificatePath == "" && c.keyPath == "" {
|
||||
return nil
|
||||
}
|
||||
err := c.startWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) startWatcher() error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.certificatePath != "" {
|
||||
err = watcher.Add(c.certificatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
err = watcher.Add(c.keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.watcher = watcher
|
||||
go c.loopUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) loopUpdate() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-c.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if event.Op&fsnotify.Write != fsnotify.Write {
|
||||
continue
|
||||
}
|
||||
err := c.reloadKeyPair()
|
||||
if err != nil {
|
||||
c.logger.Error(E.Cause(err, "reload TLS key pair"))
|
||||
}
|
||||
case err, ok := <-c.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.logger.Error(E.Cause(err, "fsnotify error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) reloadKeyPair() error {
|
||||
if c.certificatePath != "" {
|
||||
certificate, err := os.ReadFile(c.certificatePath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload certificate from ", c.certificatePath)
|
||||
}
|
||||
c.certificate = certificate
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
key, err := os.ReadFile(c.keyPath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key from ", c.keyPath)
|
||||
}
|
||||
c.key = key
|
||||
}
|
||||
keyPair, err := tls.X509KeyPair(c.certificate, c.key)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key pair")
|
||||
}
|
||||
c.config.Certificates = []tls.Certificate{keyPair}
|
||||
c.logger.Info("reloaded TLS certificate")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Close() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Close()
|
||||
}
|
||||
if c.watcher != nil {
|
||||
return c.watcher.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -136,109 +256,3 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
keyPath: options.KeyPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Config() (*STDConfig, error) {
|
||||
return c.config, nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, c.config)
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Server(conn net.Conn) Conn {
|
||||
return tls.Server(conn, c.config)
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Start() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Start()
|
||||
} else {
|
||||
if c.certificatePath == "" && c.keyPath == "" {
|
||||
return nil
|
||||
}
|
||||
err := c.startWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) startWatcher() error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.certificatePath != "" {
|
||||
err = watcher.Add(c.certificatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
err = watcher.Add(c.keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.watcher = watcher
|
||||
go c.loopUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) loopUpdate() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-c.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if event.Op&fsnotify.Write != fsnotify.Write {
|
||||
continue
|
||||
}
|
||||
err := c.reloadKeyPair()
|
||||
if err != nil {
|
||||
c.logger.Error(E.Cause(err, "reload TLS key pair"))
|
||||
}
|
||||
case err, ok := <-c.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.logger.Error(E.Cause(err, "fsnotify error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) reloadKeyPair() error {
|
||||
if c.certificatePath != "" {
|
||||
certificate, err := os.ReadFile(c.certificatePath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload certificate from ", c.certificatePath)
|
||||
}
|
||||
c.certificate = certificate
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
key, err := os.ReadFile(c.keyPath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key from ", c.keyPath)
|
||||
}
|
||||
c.key = key
|
||||
}
|
||||
keyPair, err := tls.X509KeyPair(c.certificate, c.key)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key pair")
|
||||
}
|
||||
c.config.Certificates = []tls.Certificate{keyPair}
|
||||
c.logger.Info("reloaded TLS certificate")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Close() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Close()
|
||||
}
|
||||
if c.watcher != nil {
|
||||
return c.watcher.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,27 +14,34 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"context"
|
||||
)
|
||||
|
||||
type utlsClientConfig struct {
|
||||
type UTLSClientConfig struct {
|
||||
config *utls.Config
|
||||
id utls.ClientHelloID
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) NextProtos() []string {
|
||||
func (e *UTLSClientConfig) ServerName() string {
|
||||
return e.config.ServerName
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) SetServerName(serverName string) {
|
||||
e.config.ServerName = serverName
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) NextProtos() []string {
|
||||
return e.config.NextProtos
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) SetNextProtos(nextProto []string) {
|
||||
func (e *UTLSClientConfig) SetNextProtos(nextProto []string) {
|
||||
e.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) Config() (*STDConfig, error) {
|
||||
func (e *UTLSClientConfig) Config() (*STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for uTLS")
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) Client(conn net.Conn) Conn {
|
||||
func (e *UTLSClientConfig) Client(conn net.Conn) Conn {
|
||||
return &utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}
|
||||
}
|
||||
|
||||
@@ -42,10 +49,6 @@ type utlsConnWrapper struct {
|
||||
*utls.UConn
|
||||
}
|
||||
|
||||
func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error {
|
||||
return c.UConn.HandshakeContext(ctx)
|
||||
}
|
||||
|
||||
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
state := c.Conn.ConnectionState()
|
||||
return tls.ConnectionState{
|
||||
@@ -64,7 +67,14 @@ func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
}
|
||||
}
|
||||
|
||||
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func (e *UTLSClientConfig) Clone() Config {
|
||||
return &UTLSClientConfig{
|
||||
config: e.config.Clone(),
|
||||
id: e.id,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
@@ -157,5 +167,5 @@ func newUTLSClient(router adapter.Router, serverAddress string, options option.O
|
||||
default:
|
||||
return nil, E.New("unknown uTLS fingerprint: ", options.UTLS.Fingerprint)
|
||||
}
|
||||
return &utlsClientConfig{&tlsConfig, id}, nil
|
||||
return &UTLSClientConfig{&tlsConfig, id}, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
|
||||
}
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
package trafficcontrol
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type Manager[U comparable] struct {
|
||||
access sync.Mutex
|
||||
users map[U]*Traffic
|
||||
}
|
||||
|
||||
type Traffic struct {
|
||||
Upload uint64
|
||||
Download uint64
|
||||
}
|
||||
|
||||
func NewManager[U comparable]() *Manager[U] {
|
||||
return &Manager[U]{
|
||||
users: make(map[U]*Traffic),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager[U]) Reset() {
|
||||
m.users = make(map[U]*Traffic)
|
||||
}
|
||||
|
||||
func (m *Manager[U]) TrackConnection(user U, conn net.Conn) net.Conn {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
var traffic *Traffic
|
||||
if t, loaded := m.users[user]; loaded {
|
||||
traffic = t
|
||||
} else {
|
||||
traffic = new(Traffic)
|
||||
m.users[user] = traffic
|
||||
}
|
||||
return &TrackConn{conn, traffic}
|
||||
}
|
||||
|
||||
func (m *Manager[U]) TrackPacketConnection(user U, conn N.PacketConn) N.PacketConn {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
var traffic *Traffic
|
||||
if t, loaded := m.users[user]; loaded {
|
||||
traffic = t
|
||||
} else {
|
||||
traffic = new(Traffic)
|
||||
m.users[user] = traffic
|
||||
}
|
||||
return &TrackPacketConn{conn, traffic}
|
||||
}
|
||||
|
||||
func (m *Manager[U]) ReadTraffics() map[U]Traffic {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
|
||||
trafficMap := make(map[U]Traffic)
|
||||
for user, traffic := range m.users {
|
||||
upload := atomic.SwapUint64(&traffic.Upload, 0)
|
||||
download := atomic.SwapUint64(&traffic.Download, 0)
|
||||
if upload == 0 && download == 0 {
|
||||
continue
|
||||
}
|
||||
trafficMap[user] = Traffic{
|
||||
Upload: upload,
|
||||
Download: download,
|
||||
}
|
||||
}
|
||||
return trafficMap
|
||||
}
|
||||
|
||||
type TrackConn struct {
|
||||
net.Conn
|
||||
*Traffic
|
||||
}
|
||||
|
||||
func (c *TrackConn) Read(p []byte) (n int, err error) {
|
||||
n, err = c.Conn.Read(p)
|
||||
if n > 0 {
|
||||
atomic.AddUint64(&c.Upload, uint64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TrackConn) Write(p []byte) (n int, err error) {
|
||||
n, err = c.Conn.Write(p)
|
||||
if n > 0 {
|
||||
atomic.AddUint64(&c.Download, uint64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TrackConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
n, err = bufio.Copy(w, c.Conn)
|
||||
if n > 0 {
|
||||
atomic.AddUint64(&c.Upload, uint64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TrackConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
n, err = bufio.Copy(c.Conn, r)
|
||||
if n > 0 {
|
||||
atomic.AddUint64(&c.Download, uint64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TrackConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
type TrackPacketConn struct {
|
||||
N.PacketConn
|
||||
*Traffic
|
||||
}
|
||||
|
||||
func (c *TrackPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
destination, err := c.PacketConn.ReadPacket(buffer)
|
||||
if err == nil {
|
||||
atomic.AddUint64(&c.Upload, uint64(buffer.Len()))
|
||||
}
|
||||
return destination, err
|
||||
}
|
||||
|
||||
func (c *TrackPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
n := buffer.Len()
|
||||
err := c.PacketConn.WritePacket(buffer, destination)
|
||||
if err == nil {
|
||||
atomic.AddUint64(&c.Download, uint64(n))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *TrackPacketConn) Upstream() any {
|
||||
return c.PacketConn
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
package constant
|
||||
|
||||
var Version = "1.1-beta12"
|
||||
var Version = "1.1"
|
||||
|
||||
@@ -1,3 +1,107 @@
|
||||
#### 1.1
|
||||
|
||||
* Fix close clash cache
|
||||
|
||||
Important changes since 1.0:
|
||||
|
||||
* Add support for use with android VPNService
|
||||
* Add tun support for WireGuard outbound
|
||||
* Add system tun stack
|
||||
* Add comment filter for config
|
||||
* Add option for allow optional proxy protocol header
|
||||
* Add Clash mode and persistence support
|
||||
* Add TLS ECH and uTLS support for outbound TLS options
|
||||
* Add internal simple-obfs and v2ray-plugin
|
||||
* Add ShadowsocksR outbound
|
||||
* Add VLESS outbound and XUDP
|
||||
* Skip wait for hysteria tcp handshake response
|
||||
* Add v2ray mux support for all inbound
|
||||
* Add XUDP support for VMess
|
||||
* Improve websocket writer
|
||||
* Refine tproxy write back
|
||||
* Fix DNS leak caused by
|
||||
Windows' ordinary multihomed DNS resolution behavior
|
||||
* Add sniff_timeout listen option
|
||||
* Add custom route support for tun
|
||||
* Add option for custom wireguard reserved bytes
|
||||
* Split bind_address into ipv4 and ipv6
|
||||
* Add ShadowTLS v1 and v2 support
|
||||
|
||||
#### 1.1-rc1
|
||||
|
||||
* Fix TLS config for h2 server
|
||||
* Fix crash when input bad method in shadowsocks multi-user inbound
|
||||
* Fix listen UDP
|
||||
* Fix check invalid packet on macOS
|
||||
|
||||
#### 1.1-beta18
|
||||
|
||||
* Enhance defense against active probe for shadowtls server **1**
|
||||
|
||||
**1**:
|
||||
|
||||
The `fallback_after` option has been removed.
|
||||
|
||||
#### 1.1-beta17
|
||||
|
||||
* Fix shadowtls server **1**
|
||||
|
||||
*1*:
|
||||
|
||||
Added [fallback_after](/configuration/inbound/shadowtls#fallback_after) option.
|
||||
|
||||
#### 1.0.7
|
||||
|
||||
* Add support for new x/h2 deadline
|
||||
* Fix copy pipe
|
||||
* Fix decrypt xplus packet
|
||||
* Fix macOS Ventura process name match
|
||||
* Fix smux keepalive
|
||||
* Fix vmess request buffer
|
||||
* Fix h2c transport
|
||||
* Fix tor geoip
|
||||
* Fix udp connect for mux client
|
||||
* Fix default dns transport strategy
|
||||
|
||||
#### 1.1-beta16
|
||||
|
||||
* Improve shadowtls server
|
||||
* Fix default dns transport strategy
|
||||
* Update uTLS to v1.2.0
|
||||
|
||||
#### 1.1-beta15
|
||||
|
||||
* Add support for new x/h2 deadline
|
||||
* Fix udp connect for mux client
|
||||
* Fix dns buffer
|
||||
* Fix quic dns retry
|
||||
* Fix create TLS config
|
||||
* Fix websocket alpn
|
||||
* Fix tor geoip
|
||||
|
||||
#### 1.1-beta14
|
||||
|
||||
* Add multi-user support for hysteria inbound **1**
|
||||
* Add custom tls client support for std grpc
|
||||
* Fix smux keep alive
|
||||
* Fix vmess request buffer
|
||||
* Fix default local DNS server behavior
|
||||
* Fix h2c transport
|
||||
|
||||
*1*:
|
||||
|
||||
The `auth` and `auth_str` fields have been replaced by the `users` field.
|
||||
|
||||
#### 1.1-beta13
|
||||
|
||||
* Add custom worker count option for WireGuard outbound
|
||||
* Split bind_address into ipv4 and ipv6
|
||||
* Move WFP manipulation to strict route
|
||||
* Fix WireGuard outbound panic when close
|
||||
* Fix macOS Ventura process name match
|
||||
* Fix QUIC connection migration by @HyNetwork
|
||||
* Fix handling QUIC client SNI by @HyNetwork
|
||||
|
||||
#### 1.1-beta12
|
||||
|
||||
* Fix uTLS config
|
||||
@@ -58,7 +162,8 @@ The `strict_route` on windows is removed.
|
||||
|
||||
**2**:
|
||||
|
||||
See [ShadowTLS inbound](/configuration/inbound/shadowtls#version) and [ShadowTLS outbound](/configuration/outbound/shadowtls#version)
|
||||
See [ShadowTLS inbound](/configuration/inbound/shadowtls#version)
|
||||
and [ShadowTLS outbound](/configuration/outbound/shadowtls#version)
|
||||
|
||||
#### 1.1-beta8
|
||||
|
||||
|
||||
@@ -12,8 +12,15 @@
|
||||
"down": "100 Mbps",
|
||||
"down_mbps": 100,
|
||||
"obfs": "fuck me till the daylight",
|
||||
"auth": "",
|
||||
"auth_str": "password",
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"auth": "",
|
||||
"auth_str": "password"
|
||||
}
|
||||
],
|
||||
|
||||
"recv_window_conn": 0,
|
||||
"recv_window_client": 0,
|
||||
"max_conn_client": 0,
|
||||
@@ -61,11 +68,19 @@ Supported units (case sensitive, b = bits, B = bytes, 8b=1B):
|
||||
|
||||
Obfuscated password.
|
||||
|
||||
#### auth
|
||||
#### users
|
||||
|
||||
Hysteria users
|
||||
|
||||
#### users.auth
|
||||
|
||||
==Required if `auth_str` is empty==
|
||||
|
||||
Authentication password, in base64.
|
||||
|
||||
#### auth_str
|
||||
#### users.auth_str
|
||||
|
||||
==Required if `auth` is empty==
|
||||
|
||||
Authentication password.
|
||||
|
||||
|
||||
@@ -12,8 +12,15 @@
|
||||
"down": "100 Mbps",
|
||||
"down_mbps": 100,
|
||||
"obfs": "fuck me till the daylight",
|
||||
"auth": "",
|
||||
"auth_str": "password",
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"auth": "",
|
||||
"auth_str": "password"
|
||||
}
|
||||
],
|
||||
|
||||
"recv_window_conn": 0,
|
||||
"recv_window_client": 0,
|
||||
"max_conn_client": 0,
|
||||
@@ -61,11 +68,19 @@
|
||||
|
||||
混淆密码。
|
||||
|
||||
#### auth
|
||||
#### users
|
||||
|
||||
Hysteria 用户
|
||||
|
||||
#### users.auth
|
||||
|
||||
==与 auth_str 必填一个==
|
||||
|
||||
base64 编码的认证密码。
|
||||
|
||||
#### auth_str
|
||||
#### users.auth_str
|
||||
|
||||
==与 auth 必填一个==
|
||||
|
||||
认证密码。
|
||||
|
||||
|
||||
@@ -93,16 +93,23 @@ Set the default route to the Tun.
|
||||
|
||||
#### strict_route
|
||||
|
||||
*In Linux*:
|
||||
|
||||
Enforce strict routing rules when `auto_route` is enabled:
|
||||
|
||||
*In Linux*:
|
||||
|
||||
* Let unsupported network unreachable
|
||||
* Route all connections to tun
|
||||
|
||||
It prevents address leaks and makes DNS hijacking work on Android and Linux with systemd-resolved, but your device will
|
||||
not be accessible by others.
|
||||
|
||||
*In Windows*:
|
||||
|
||||
* Add firewall rules to prevent DNS leak caused by
|
||||
Windows' [ordinary multihomed DNS resolution behavior](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29)
|
||||
|
||||
It may prevent some applications (such as VirtualBox) from working properly in certain situations.
|
||||
|
||||
#### inet4_route_address
|
||||
|
||||
Use custom routes instead of default when `auto_route` is enabled.
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
{
|
||||
"type": "tun",
|
||||
"tag": "tun-in",
|
||||
|
||||
"interface_name": "tun0",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||
@@ -47,8 +46,8 @@
|
||||
"exclude_package": [
|
||||
"com.android.captiveportallogin"
|
||||
],
|
||||
|
||||
... // 监听字段
|
||||
...
|
||||
// 监听字段
|
||||
}
|
||||
```
|
||||
|
||||
@@ -94,15 +93,23 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
#### strict_route
|
||||
|
||||
*在 Linux 中*:
|
||||
|
||||
启用 `auto_route` 时执行严格的路由规则。
|
||||
|
||||
*在 Linux 中*:
|
||||
|
||||
* 让不支持的网络无法到达
|
||||
* 将所有连接路由到 tun
|
||||
|
||||
它可以防止地址泄漏,并使 DNS 劫持在 Android 和使用 systemd-resolved 的 Linux 上工作,但你的设备将无法其他设备被访问。
|
||||
|
||||
*在 Windows 中*:
|
||||
|
||||
* 添加防火墙规则以阻止 Windows
|
||||
的 [普通多宿主 DNS 解析行为](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29)
|
||||
造成的 DNS 泄露
|
||||
|
||||
它可能会使某些应用程序(如 VirtualBox)在某些情况下无法正常工作。
|
||||
|
||||
#### inet4_route_address
|
||||
|
||||
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"peer_public_key": "Z1XXLsKYkYxuiYjJIkRvtIKFepCYHTgON+GwPq7SOV4=",
|
||||
"pre_shared_key": "31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=",
|
||||
"reserved": [0, 0, 0],
|
||||
"workers": 4,
|
||||
"mtu": 1408,
|
||||
"network": "tcp",
|
||||
|
||||
@@ -88,9 +89,17 @@ WireGuard pre-shared key.
|
||||
|
||||
WireGuard reserved field bytes.
|
||||
|
||||
#### workers
|
||||
|
||||
WireGuard worker count.
|
||||
|
||||
CPU count is used by default.
|
||||
|
||||
#### mtu
|
||||
|
||||
WireGuard MTU. 1408 will be used if empty.
|
||||
WireGuard MTU.
|
||||
|
||||
1408 will be used if empty.
|
||||
|
||||
#### network
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"peer_public_key": "Z1XXLsKYkYxuiYjJIkRvtIKFepCYHTgON+GwPq7SOV4=",
|
||||
"pre_shared_key": "31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=",
|
||||
"reserved": [0, 0, 0],
|
||||
"workers": 4,
|
||||
"mtu": 1408,
|
||||
"network": "tcp",
|
||||
|
||||
@@ -90,9 +91,17 @@ WireGuard 预共享密钥。
|
||||
|
||||
WireGuard 保留字段字节。
|
||||
|
||||
#### workers
|
||||
|
||||
WireGuard worker 数量。
|
||||
|
||||
默认使用 CPU 数量。
|
||||
|
||||
#### mtu
|
||||
|
||||
WireGuard MTU。 默认1408。
|
||||
WireGuard MTU。
|
||||
|
||||
默认使用 1408。
|
||||
|
||||
#### network
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
{
|
||||
"detour": "upstream-out",
|
||||
"bind_interface": "en0",
|
||||
"bind_address": "0.0.0.0",
|
||||
"inet4_bind_address": "0.0.0.0",
|
||||
"inet6_bind_address": "::",
|
||||
"routing_mark": 1234,
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
@@ -17,9 +18,9 @@
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Available Context |
|
||||
|---------------------------------------------------------------------------------------------------------------------|-------------------|
|
||||
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set |
|
||||
| Field | Available Context |
|
||||
|----------------------------------------------------------------------------------------------------------------------|-------------------|
|
||||
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set |
|
||||
|
||||
#### detour
|
||||
|
||||
@@ -29,9 +30,13 @@ The tag of the upstream outbound.
|
||||
|
||||
The network interface to bind to.
|
||||
|
||||
#### bind_address
|
||||
#### inet4_bind_address
|
||||
|
||||
The address to bind to.
|
||||
The IPv4 address to bind to.
|
||||
|
||||
#### inet6_bind_address
|
||||
|
||||
The IPv6 address to bind to.
|
||||
|
||||
#### routing_mark
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
{
|
||||
"detour": "upstream-out",
|
||||
"bind_interface": "en0",
|
||||
"bind_address": "0.0.0.0",
|
||||
"inet4_bind_address": "0.0.0.0",
|
||||
"inet6_bind_address": "::",
|
||||
"routing_mark": 1234,
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
@@ -17,9 +18,9 @@
|
||||
|
||||
### 字段
|
||||
|
||||
| 字段 | 可用上下文 |
|
||||
|---------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` 未设置 |
|
||||
| 字段 | 可用上下文 |
|
||||
|----------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` 未设置 |
|
||||
|
||||
|
||||
#### detour
|
||||
@@ -32,9 +33,13 @@
|
||||
|
||||
要绑定到的网络接口。
|
||||
|
||||
#### bind_address
|
||||
#### inet4_bind_address
|
||||
|
||||
要绑定的地址。
|
||||
要绑定的 IPv4 地址。
|
||||
|
||||
#### inet6_bind_address
|
||||
|
||||
要绑定的 IPv6 地址。
|
||||
|
||||
#### routing_mark
|
||||
|
||||
|
||||
52
docs/examples/clash-api.md
Normal file
52
docs/examples/clash-api.md
Normal file
@@ -0,0 +1,52 @@
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
"domain": [
|
||||
"clash.razord.top",
|
||||
"yacd.haishan.me"
|
||||
],
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"clash_mode": "direct",
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
},
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "selector",
|
||||
"tag": "default",
|
||||
"outbounds": [
|
||||
"proxy-a",
|
||||
"proxy-b"
|
||||
]
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"clash_mode": "direct",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"domain": [
|
||||
"clash.razord.top",
|
||||
"yacd.haishan.me"
|
||||
],
|
||||
"outbound": "direct"
|
||||
}
|
||||
],
|
||||
"final": "default"
|
||||
},
|
||||
"experimental": {
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"store_selected": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@@ -3,7 +3,8 @@
|
||||
Configuration examples for sing-box.
|
||||
|
||||
* [Linux Server Installation](./linux-server-installation)
|
||||
* [Shadowsocks Server](./ss-server)
|
||||
* [Shadowsocks Client](./ss-client)
|
||||
* [Shadowsocks Tun](./ss-tun)
|
||||
* [DNS Hijack](./dns-hijack.md)
|
||||
* [Tun](./tun)
|
||||
* [DNS Hijack](./dns-hijack.md)
|
||||
* [Shadowsocks](./shadowsocks)
|
||||
* [ShadowTLS](./shadowtls)
|
||||
* [Clash API](./clash-api)
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
sing-box 的配置示例。
|
||||
|
||||
* [Linux 服务器安装](./linux-server-installation)
|
||||
* [Shadowsocks 服务器](./ss-server)
|
||||
* [Shadowsocks 客户端](./ss-client)
|
||||
* [Shadowsocks Tun](./ss-tun)
|
||||
* [DNS 劫持](./dns-hijack.md)
|
||||
* [Tun](./tun)
|
||||
* [DNS 劫持](./dns-hijack.md)
|
||||
* [Shadowsocks](./shadowsocks)
|
||||
* [ShadowTLS](./shadowtls)
|
||||
* [Clash API](./clash-api)
|
||||
|
||||
157
docs/examples/shadowsocks.md
Normal file
157
docs/examples/shadowsocks.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Shadowsocks
|
||||
|
||||
## Single User
|
||||
|
||||
#### Server
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"listen": "::",
|
||||
"listen_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Client
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"listen": "::",
|
||||
"listen_port": 2080
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Multiple Users
|
||||
|
||||
#### Server
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"listen": "::",
|
||||
"listen_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"password": "BXYxVUXJ9NgF7c7KPLQjkg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Client
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"listen": "::",
|
||||
"listen_port": 2080
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==:BXYxVUXJ9NgF7c7KPLQjkg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Relay
|
||||
|
||||
#### Server
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"listen": "::",
|
||||
"listen_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Relay
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"listen": "::",
|
||||
"listen_port": 8081,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "BXYxVUXJ9NgF7c7KPLQjkg==",
|
||||
"destinations": [
|
||||
{
|
||||
"name": "my_server",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Client
|
||||
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"listen": "::",
|
||||
"listen_port": 2080
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8081,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==:BXYxVUXJ9NgF7c7KPLQjkg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
@@ -7,6 +7,8 @@
|
||||
"type": "shadowtls",
|
||||
"listen": "::",
|
||||
"listen_port": 4443,
|
||||
"version": 2,
|
||||
"password": "fuck me till the daylight",
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443
|
||||
@@ -45,6 +47,8 @@
|
||||
"tag": "shadowtls-out",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 4443,
|
||||
"version": 2,
|
||||
"password": "fuck me till the daylight",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "google.com"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "mixed",
|
||||
"listen": "::",
|
||||
"listen_port": 2080
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"server": "::",
|
||||
"server_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
@@ -1,13 +0,0 @@
|
||||
```json
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"listen": "::",
|
||||
"listen_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -10,9 +10,18 @@
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"detour": "direct"
|
||||
},
|
||||
{
|
||||
"tag": "block",
|
||||
"address": "rcode://success"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"geosite": "category-ads-all",
|
||||
"server": "block",
|
||||
"disable_cache": true
|
||||
},
|
||||
{
|
||||
"domain": "mydomain.com",
|
||||
"geosite": "cn",
|
||||
@@ -26,6 +35,7 @@
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"auto_route": true,
|
||||
"strict_route": false,
|
||||
"sniff": true
|
||||
}
|
||||
],
|
||||
@@ -58,13 +68,16 @@
|
||||
"outbound": "dns-out"
|
||||
},
|
||||
{
|
||||
"geosite": "category-ads-all",
|
||||
"outbound": "block"
|
||||
"geosite": "cn",
|
||||
"geoip": [
|
||||
"private",
|
||||
"cn"
|
||||
],
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"geosite": "cn",
|
||||
"geoip": "cn",
|
||||
"outbound": "direct"
|
||||
"geosite": "category-ads-all",
|
||||
"outbound": "block"
|
||||
}
|
||||
],
|
||||
"auto_detect_interface": true
|
||||
@@ -7,11 +7,11 @@ the public internet.
|
||||
|
||||
##### on Android
|
||||
|
||||
`auto-route` cannot automatically hijack DNS requests when Android's `Private DNS` is enabled.
|
||||
`auto-route` cannot automatically hijack DNS requests when Android's `Private DNS` enabled or `strict_route` disabled.
|
||||
|
||||
##### on Linux
|
||||
|
||||
`auto-route` cannot automatically hijack DNS requests with `systemd-resolved` enabled, you can switch to NetworkManager.
|
||||
`auto-route` cannot automatically hijack DNS requests with `systemd-resolved` enabled and `strict_route` disabled.
|
||||
|
||||
#### System proxy
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
##### Android
|
||||
|
||||
`auto-route` 无法自动劫持 DNS 请求如果 `私人 DNS` 开启.
|
||||
`auto-route` 无法自动劫持 DNS 请求如果 `私人 DNS` 开启或 `strict_route` 禁用。
|
||||
|
||||
##### Linux
|
||||
|
||||
`auto-route` 无法自动劫持 DNS 请求如果 `systemd-resolved` 开启, 您可以切换到 NetworkManager.
|
||||
`auto-route` 无法自动劫持 DNS 请求如果 `systemd-resolved` 开启且 `strict_route` 禁用。
|
||||
|
||||
#### 系统代理
|
||||
|
||||
|
||||
@@ -59,3 +59,7 @@ func (c *CacheFile) StoreSelected(group, selected string) error {
|
||||
return bucket.Put([]byte(group), []byte(selected))
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) Close() error {
|
||||
return c.DB.Close()
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ type Server struct {
|
||||
trafficManager *trafficontrol.Manager
|
||||
urlTestHistory *urltest.HistoryStorage
|
||||
tcpListener net.Listener
|
||||
directIO bool
|
||||
mode string
|
||||
storeSelected bool
|
||||
cacheFile adapter.ClashCacheFile
|
||||
@@ -61,7 +60,6 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
},
|
||||
trafficManager: trafficManager,
|
||||
urlTestHistory: urltest.NewHistoryStorage(),
|
||||
directIO: options.DirectIO,
|
||||
mode: strings.ToLower(options.DefaultMode),
|
||||
}
|
||||
if server.mode == "" {
|
||||
@@ -156,7 +154,7 @@ func (s *Server) HistoryStorage() *urltest.HistoryStorage {
|
||||
}
|
||||
|
||||
func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) {
|
||||
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule, s.directIO)
|
||||
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule)
|
||||
return tracker, tracker
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func (tt *tcpTracker) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule, directIO bool) *tcpTracker {
|
||||
func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *tcpTracker {
|
||||
uuid, _ := uuid.NewV4()
|
||||
|
||||
var chain []string
|
||||
@@ -107,7 +107,7 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
|
||||
}, func(n int64) {
|
||||
download.Add(n)
|
||||
manager.PushDownloaded(n)
|
||||
}, directIO),
|
||||
}),
|
||||
manager: manager,
|
||||
trackerInfo: &trackerInfo{
|
||||
UUID: uuid,
|
||||
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
func New(conn net.Conn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64, direct bool) *Conn {
|
||||
func New(conn net.Conn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *Conn {
|
||||
return &Conn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
|
||||
}
|
||||
|
||||
func NewHook(conn net.Conn, readCounter func(n int64), writeCounter func(n int64), direct bool) *HookConn {
|
||||
func NewHook(conn net.Conn, readCounter func(n int64), writeCounter func(n int64)) *HookConn {
|
||||
return &HookConn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ var (
|
||||
|
||||
type StatsService struct {
|
||||
createdAt time.Time
|
||||
directIO bool
|
||||
inbounds map[string]bool
|
||||
outbounds map[string]bool
|
||||
access sync.Mutex
|
||||
@@ -50,7 +49,6 @@ func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
|
||||
}
|
||||
return &StatsService{
|
||||
createdAt: time.Now(),
|
||||
directIO: options.DirectIO,
|
||||
inbounds: inbounds,
|
||||
outbounds: outbounds,
|
||||
counters: make(map[string]*atomic.Int64),
|
||||
@@ -75,7 +73,7 @@ func (s *StatsService) RoutedConnection(inbound string, outbound string, conn ne
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
|
||||
}
|
||||
s.access.Unlock()
|
||||
return trackerconn.New(conn, readCounter, writeCounter, s.directIO)
|
||||
return trackerconn.New(conn, readCounter, writeCounter)
|
||||
}
|
||||
|
||||
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn {
|
||||
|
||||
36
go.mod
36
go.mod
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.11.8
|
||||
github.com/Dreamacro/clash v1.12.0
|
||||
github.com/caddyserver/certmagic v0.17.2
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/database64128/tfo-go/v2 v2.0.2
|
||||
@@ -13,33 +13,34 @@ require (
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/gofrs/uuid v4.3.0+incompatible
|
||||
github.com/gofrs/uuid v4.3.1+incompatible
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.0.4
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/refraction-networking/utls v1.1.5
|
||||
github.com/refraction-networking/utls v1.2.0
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||
github.com/sagernet/quic-go v0.0.0-20221031051350-29d8bb1c8127
|
||||
github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4
|
||||
github.com/sagernet/sing-dns v0.0.0-20221031055845-7de76401d403
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/sagernet/sing-tun v0.0.0-20221028015259-ea5c35f62f07
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685
|
||||
github.com/sagernet/quic-go v0.0.0-20221108053023-645bcc4f9b15
|
||||
github.com/sagernet/sing v0.1.0
|
||||
github.com/sagernet/sing-dns v0.1.0
|
||||
github.com/sagernet/sing-shadowsocks v0.1.0
|
||||
github.com/sagernet/sing-tun v0.1.1-0.20221128044455-b22d9eb41b74
|
||||
github.com/sagernet/sing-vmess v0.1.0
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||
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
|
||||
github.com/stretchr/testify v1.8.1
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.uber.org/atomic v1.10.0
|
||||
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab
|
||||
golang.org/x/crypto v0.1.0
|
||||
golang.org/x/net v0.1.0
|
||||
golang.org/x/sys v0.1.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
|
||||
google.golang.org/grpc v1.50.1
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a
|
||||
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f
|
||||
golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e
|
||||
golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669
|
||||
google.golang.org/grpc v1.51.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||
)
|
||||
@@ -54,12 +55,14 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
@@ -68,13 +71,12 @@ require (
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // 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
|
||||
)
|
||||
|
||||
80
go.sum
80
go.sum
@@ -3,8 +3,8 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8=
|
||||
github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ=
|
||||
github.com/Dreamacro/clash v1.12.0 h1:Fv10zwTtEo4jN1V+fLK+WEOulFAxlZFPfFQGWmbMDrk=
|
||||
github.com/Dreamacro/clash v1.12.0/go.mod h1:KXZNe2ZS9Z7zZYCFENEW8J9OgyrYrwlr/Gj9ZpzcDVU=
|
||||
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=
|
||||
@@ -22,6 +22,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
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/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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=
|
||||
@@ -47,8 +48,8 @@ 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.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
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/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=
|
||||
@@ -74,23 +75,23 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.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/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
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.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
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/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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
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=
|
||||
@@ -105,6 +106,8 @@ github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
|
||||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0 h1:kUMoxMoQG3ogk/QWyKh3zibV7BKZ+xBpWil1cTylVqc=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
@@ -116,8 +119,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/refraction-networking/utls v1.1.5 h1:JtrojoNhbUQkBqEg05sP3gDgDj6hIEAAVKbI9lx4n6w=
|
||||
github.com/refraction-networking/utls v1.1.5/go.mod h1:jRQxtYi7nkq1p28HF2lwOH5zQm9aC8rpK0O9lIIzGh8=
|
||||
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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||
@@ -128,24 +131,26 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c
|
||||
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-20221031051350-29d8bb1c8127 h1:rraPfWlUy2cdZ61FLXRCFbL0lb7oocScbr4Ac0rIzTU=
|
||||
github.com/sagernet/quic-go v0.0.0-20221031051350-29d8bb1c8127/go.mod h1:oWFbojDMm85/Jbm/fyWoo8Pux6dIssxGi3q1r+5642A=
|
||||
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/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-20221031055845-7de76401d403 h1:kKDO97rx+JVJ4HI1hTWOnCCI6um5clK1LfnIto2DY4M=
|
||||
github.com/sagernet/sing-dns v0.0.0-20221031055845-7de76401d403/go.mod h1:cyL9DHbBZ0Xlt/8VD0i6yeiDayH0KzWGNQb8MYhhz7g=
|
||||
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-20221028015259-ea5c35f62f07 h1:zupkkVVFWv0QsLPjxEzlzXlLfDk1hUujK8ctJSIKFCI=
|
||||
github.com/sagernet/sing-tun v0.0.0-20221028015259-ea5c35f62f07/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 h1:AZzFNRR/ZwMTceUQ1b/mxx6oyKqmFymdMn/yleJmoVM=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
|
||||
github.com/sagernet/sing v0.1.0 h1:FGmaP2BVPYO2IyC/3R1DaQa/zr+kOKHRgWqrmOF+Gu8=
|
||||
github.com/sagernet/sing v0.1.0/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
|
||||
github.com/sagernet/sing-dns v0.1.0 h1:mV8y86KDIXCALTbUxQp/n/eSiSzCDuYaf+EbPndI06U=
|
||||
github.com/sagernet/sing-dns v0.1.0/go.mod h1:IXw6t1F25YvzmgCgV2kKySf4XCEKkUJnmLvKCd7jFEc=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.0 h1:cDmmOkA11fzVdhyCZQEeI3ozQz+59rj8+rqPb91xux4=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.0/go.mod h1:O5LtOs8Ivw686FqLpO0Zu+A0ROVE15VeqEK3yDRRAms=
|
||||
github.com/sagernet/sing-tun v0.1.1-0.20221128044455-b22d9eb41b74 h1:T6U4VNWwxQjr80I6RcQA1lTrWgwQ0vMq1UsnlzeqgxI=
|
||||
github.com/sagernet/sing-tun v0.1.1-0.20221128044455-b22d9eb41b74/go.mod h1:btUIxuI5vfzUcEDFVrG48aHM2stzg4jcI7mFQeEsei4=
|
||||
github.com/sagernet/sing-vmess v0.1.0 h1:x0tYBJRbVi7zVXpMEW45eApGpXIDs9ub3raglouAKMo=
|
||||
github.com/sagernet/sing-vmess v0.1.0/go.mod h1:4lwj6EHrUlgRnKhbmtboGbt+wtl5+tHMv96Ez8LZArw=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||
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/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=
|
||||
@@ -183,9 +188,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a h1:diz9pEYuTIuLMJLs3rGDkeaTsNyRs6duYdFyPAxzE/U=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/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=
|
||||
@@ -210,11 +214,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e h1:IVOjWZQH/57UDcpX19vSmMz8w3ohroOMWohn8qWpRkg=
|
||||
golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e/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=
|
||||
@@ -222,7 +224,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-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.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||
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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -238,14 +240,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk=
|
||||
golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/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.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
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=
|
||||
@@ -270,10 +271,6 @@ 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=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -289,8 +286,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.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.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
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=
|
||||
@@ -306,8 +303,9 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
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/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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
@@ -16,9 +15,13 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/hysteria"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Hysteria)(nil)
|
||||
@@ -27,7 +30,8 @@ type Hysteria struct {
|
||||
myInboundAdapter
|
||||
quicConfig *quic.Config
|
||||
tlsConfig tls.ServerConfig
|
||||
authKey []byte
|
||||
authKey []string
|
||||
authUser []string
|
||||
xplusKey []byte
|
||||
sendBPS uint64
|
||||
recvBPS uint64
|
||||
@@ -60,12 +64,16 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if quicConfig.MaxIncomingStreams == 0 {
|
||||
quicConfig.MaxIncomingStreams = hysteria.DefaultMaxIncomingStreams
|
||||
}
|
||||
var auth []byte
|
||||
if len(options.Auth) > 0 {
|
||||
auth = options.Auth
|
||||
} else {
|
||||
auth = []byte(options.AuthString)
|
||||
}
|
||||
authKey := common.Map(options.Users, func(it option.HysteriaUser) string {
|
||||
if len(it.Auth) > 0 {
|
||||
return string(it.Auth)
|
||||
} else {
|
||||
return it.AuthString
|
||||
}
|
||||
})
|
||||
authUser := common.Map(options.Users, func(it option.HysteriaUser) string {
|
||||
return it.Name
|
||||
})
|
||||
var xplus []byte
|
||||
if options.Obfs != "" {
|
||||
xplus = []byte(options.Obfs)
|
||||
@@ -104,7 +112,8 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
quicConfig: quicConfig,
|
||||
authKey: auth,
|
||||
authKey: authKey,
|
||||
authUser: authUser,
|
||||
xplusKey: xplus,
|
||||
sendBPS: up,
|
||||
recvBPS: down,
|
||||
@@ -158,7 +167,6 @@ func (h *Hysteria) acceptLoop() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", conn.RemoteAddr())
|
||||
go func() {
|
||||
hErr := h.accept(ctx, conn)
|
||||
if hErr != nil {
|
||||
@@ -178,12 +186,21 @@ func (h *Hysteria) accept(ctx context.Context, conn quic.Connection) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(clientHello.Auth, h.authKey) {
|
||||
userIndex := slices.Index(h.authKey, string(clientHello.Auth))
|
||||
if userIndex == -1 {
|
||||
err = hysteria.WriteServerHello(controlStream, hysteria.ServerHello{
|
||||
Message: "wrong password",
|
||||
})
|
||||
return E.Errors(E.New("wrong password: ", string(clientHello.Auth)), err)
|
||||
}
|
||||
user := h.authUser[userIndex]
|
||||
if user == "" {
|
||||
user = F.ToString(userIndex)
|
||||
} else {
|
||||
ctx = auth.ContextWithUser(ctx, user)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection from ", conn.RemoteAddr())
|
||||
h.logger.DebugContext(ctx, "peer send speed: ", clientHello.SendBPS/1024/1024, " MBps, peer recv speed: ", clientHello.RecvBPS/1024/1024, " MBps")
|
||||
if clientHello.SendBPS == 0 || clientHello.RecvBPS == 0 {
|
||||
return E.New("invalid rate from client")
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
default:
|
||||
err = E.New("shadowsocks: unsupported method: ", options.Method)
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
inbound.packetUpstream = inbound.service
|
||||
return inbound, err
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func (h *ShadowsocksMulti) createHandler() http.Handler {
|
||||
router := chi.NewRouter()
|
||||
router.Get("/", h.handleHello)
|
||||
router.Put("/users", h.handleUpdateUsers)
|
||||
router.Get("/traffics", h.handleReadTraffics)
|
||||
return router
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) handleHello(writer http.ResponseWriter, request *http.Request) {
|
||||
render.JSON(writer, request, render.M{
|
||||
"server": "sing-box",
|
||||
"version": C.Version,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) handleUpdateUsers(writer http.ResponseWriter, request *http.Request) {
|
||||
var users []option.ShadowsocksUser
|
||||
err := readRequest(request, &users)
|
||||
if err != nil {
|
||||
h.newError(E.Cause(err, "controller: update users: parse request"))
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
writer.Write([]byte(F.ToString(err)))
|
||||
return
|
||||
}
|
||||
users = append([]option.ShadowsocksUser{{
|
||||
Name: "control",
|
||||
Password: h.users[0].Password,
|
||||
}}, users...)
|
||||
err = h.service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user option.ShadowsocksUser) int {
|
||||
return index
|
||||
}), common.Map(users, func(user option.ShadowsocksUser) string {
|
||||
return user.Password
|
||||
}))
|
||||
if err != nil {
|
||||
h.newError(E.Cause(err, "controller: update users"))
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
writer.Write([]byte(F.ToString(err)))
|
||||
return
|
||||
}
|
||||
h.users = users
|
||||
h.trafficManager.Reset()
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
h.logger.Info("controller: updated ", len(users)-1, " users")
|
||||
}
|
||||
|
||||
type ShadowsocksUserTraffic struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Upload uint64 `json:"upload,omitempty"`
|
||||
Download uint64 `json:"download,omitempty"`
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) handleReadTraffics(writer http.ResponseWriter, request *http.Request) {
|
||||
h.logger.Debug("controller: traffics sent")
|
||||
trafficMap := h.trafficManager.ReadTraffics()
|
||||
if len(trafficMap) == 0 {
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
traffics := make([]ShadowsocksUserTraffic, 0, len(trafficMap))
|
||||
for user, traffic := range trafficMap {
|
||||
traffics = append(traffics, ShadowsocksUserTraffic{
|
||||
Name: h.users[user].Name,
|
||||
Upload: traffic.Upload,
|
||||
Download: traffic.Download,
|
||||
})
|
||||
}
|
||||
render.JSON(writer, request, traffics)
|
||||
}
|
||||
|
||||
func readRequest(request *http.Request, v any) error {
|
||||
defer request.Body.Close()
|
||||
content, err := io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(content, v)
|
||||
}
|
||||
@@ -3,12 +3,9 @@ package inbound
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/pipelistener"
|
||||
"github.com/sagernet/sing-box/common/trafficcontrol"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -28,12 +25,8 @@ var (
|
||||
|
||||
type ShadowsocksMulti struct {
|
||||
myInboundAdapter
|
||||
service *shadowaead_2022.MultiService[int]
|
||||
users []option.ShadowsocksUser
|
||||
controlEnabled bool
|
||||
controller *http.Server
|
||||
controllerPipe *pipelistener.Listener
|
||||
trafficManager *trafficcontrol.Manager[int]
|
||||
service *shadowaead_2022.MultiService[int]
|
||||
users []option.ShadowsocksUser
|
||||
}
|
||||
|
||||
func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) {
|
||||
@@ -56,26 +49,19 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
} else {
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
if !common.Contains(shadowaead_2022.List, options.Method) {
|
||||
return nil, E.New("unsupported method: " + options.Method)
|
||||
}
|
||||
service, err := shadowaead_2022.NewMultiServiceWithPassword[int](
|
||||
options.Method,
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
)
|
||||
users := options.Users
|
||||
if options.ControlPassword != "" {
|
||||
inbound.controlEnabled = true
|
||||
users = append([]option.ShadowsocksUser{{
|
||||
Name: "control",
|
||||
Password: options.ControlPassword,
|
||||
}}, users...)
|
||||
inbound.controller = &http.Server{Handler: inbound.createHandler()}
|
||||
inbound.trafficManager = trafficcontrol.NewManager[int]()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user option.ShadowsocksUser) int {
|
||||
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
|
||||
@@ -85,30 +71,10 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
}
|
||||
inbound.service = service
|
||||
inbound.packetUpstream = service
|
||||
inbound.users = users
|
||||
inbound.users = options.Users
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) Start() error {
|
||||
if h.controlEnabled {
|
||||
h.controllerPipe = pipelistener.New(16)
|
||||
go func() {
|
||||
err := h.controller.Serve(h.controllerPipe)
|
||||
if err != nil {
|
||||
h.newError(E.Cause(err, "controller serve error"))
|
||||
}
|
||||
}()
|
||||
}
|
||||
return h.myInboundAdapter.Start()
|
||||
}
|
||||
|
||||
func (h *ShadowsocksMulti) Close() error {
|
||||
if h.controlEnabled {
|
||||
h.controllerPipe.Close()
|
||||
}
|
||||
return h.myInboundAdapter.Close()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
@@ -126,11 +92,6 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
if userIndex == 0 && h.controlEnabled {
|
||||
h.logger.InfoContext(ctx, "inbound control connection")
|
||||
h.controllerPipe.Serve(conn)
|
||||
return nil
|
||||
}
|
||||
user := h.users[userIndex].Name
|
||||
if user == "" {
|
||||
user = F.ToString(userIndex)
|
||||
|
||||
@@ -29,6 +29,7 @@ type ShadowTLS struct {
|
||||
handshakeAddr M.Socksaddr
|
||||
v2 bool
|
||||
password string
|
||||
fallbackAfter int
|
||||
}
|
||||
|
||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
||||
@@ -52,6 +53,11 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
case 1:
|
||||
case 2:
|
||||
inbound.v2 = true
|
||||
if options.FallbackAfter == nil {
|
||||
inbound.fallbackAfter = 2
|
||||
} else {
|
||||
inbound.fallbackAfter = *options.FallbackAfter
|
||||
}
|
||||
default:
|
||||
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
|
||||
}
|
||||
@@ -85,7 +91,7 @@ func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata a
|
||||
hashConn := shadowtls.NewHashWriteConn(conn, s.password)
|
||||
go bufio.Copy(hashConn, handshakeConn)
|
||||
var request *buf.Buffer
|
||||
request, err = s.copyUntilHandshakeFinishedV2(handshakeConn, conn, hashConn)
|
||||
request, err = s.copyUntilHandshakeFinishedV2(ctx, handshakeConn, conn, hashConn, s.fallbackAfter)
|
||||
if err == nil {
|
||||
handshakeConn.Close()
|
||||
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewConn(conn), request), metadata)
|
||||
@@ -129,7 +135,7 @@ func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) copyUntilHandshakeFinishedV2(dst net.Conn, src io.Reader, hash *shadowtls.HashWriteConn) (*buf.Buffer, error) {
|
||||
func (s *ShadowTLS) copyUntilHandshakeFinishedV2(ctx context.Context, dst net.Conn, src io.Reader, hash *shadowtls.HashWriteConn, fallbackAfter int) (*buf.Buffer, error) {
|
||||
const applicationData = 0x17
|
||||
var tlsHdr [5]byte
|
||||
var applicationDataCount int
|
||||
@@ -146,9 +152,17 @@ func (s *ShadowTLS) copyUntilHandshakeFinishedV2(dst net.Conn, src io.Reader, ha
|
||||
data.Release()
|
||||
return nil, err
|
||||
}
|
||||
if length >= 8 && bytes.Equal(data.To(8), hash.Sum()) {
|
||||
data.Advance(8)
|
||||
return data, nil
|
||||
if hash.HasContent() && length >= 8 {
|
||||
checksum := hash.Sum()
|
||||
if bytes.Equal(data.To(8), checksum) {
|
||||
s.logger.TraceContext(ctx, "match current hashcode")
|
||||
data.Advance(8)
|
||||
return data, nil
|
||||
} else if hash.LastSum() != nil && bytes.Equal(data.To(8), hash.LastSum()) {
|
||||
s.logger.TraceContext(ctx, "match last hashcode")
|
||||
data.Advance(8)
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), data))
|
||||
data.Release()
|
||||
@@ -159,7 +173,7 @@ func (s *ShadowTLS) copyUntilHandshakeFinishedV2(dst net.Conn, src io.Reader, ha
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if applicationDataCount > 3 {
|
||||
if applicationDataCount > fallbackAfter {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/trojan"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
@@ -17,7 +18,6 @@ import (
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/trojan"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -157,7 +157,7 @@ func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
return err
|
||||
}
|
||||
}
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
return h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
@@ -19,11 +20,11 @@ import (
|
||||
const WithQUIC = false
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"quic", "h3"}, func(ctx context.Context, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
dns.RegisterTransport([]string{"quic", "h3"}, func(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
})
|
||||
v2ray.RegisterQUICConstructor(
|
||||
func(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
func(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler, errorHandler E.Handler) (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) {
|
||||
|
||||
11
mkdocs.yml
11
mkdocs.yml
@@ -97,11 +97,11 @@ nav:
|
||||
- Examples:
|
||||
- examples/index.md
|
||||
- Linux Server Installation: examples/linux-server-installation.md
|
||||
- Shadowsocks Server: examples/ss-server.md
|
||||
- Shadowsocks Client: examples/ss-client.md
|
||||
- Shadowsocks Tun: examples/ss-tun.md
|
||||
- ShadowTLS: examples/shadowtls.md
|
||||
- Tun: examples/tun.md
|
||||
- DNS Hijack: examples/dns-hijack.md
|
||||
- Shadowsocks: examples/shadowsocks.md
|
||||
- ShadowTLS: examples/shadowtls.md
|
||||
- Clash API: examples/clash-api.md
|
||||
- Contributing:
|
||||
- contributing/index.md
|
||||
- Developing:
|
||||
@@ -168,7 +168,4 @@ plugins:
|
||||
Known Issues: 已知问题
|
||||
Examples: 示例
|
||||
Linux Server Installation: Linux 服务器安装
|
||||
Shadowsocks Server: Shadowsocks 服务器
|
||||
Shadowsocks Client: Shadowsocks 客户端
|
||||
Shadowsocks Tun: Shadowsocks Tun
|
||||
DNS Hijack: DNS 劫持
|
||||
@@ -4,7 +4,6 @@ type ClashAPIOptions struct {
|
||||
ExternalController string `json:"external_controller,omitempty"`
|
||||
ExternalUI string `json:"external_ui,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
DirectIO bool `json:"direct_io,omitempty"`
|
||||
DefaultMode string `json:"default_mode,omitempty"`
|
||||
StoreSelected bool `json:"store_selected,omitempty"`
|
||||
CacheFile string `json:"cache_file,omitempty"`
|
||||
|
||||
@@ -7,8 +7,7 @@ type HysteriaInboundOptions struct {
|
||||
Down string `json:"down,omitempty"`
|
||||
DownMbps int `json:"down_mbps,omitempty"`
|
||||
Obfs string `json:"obfs,omitempty"`
|
||||
Auth []byte `json:"auth,omitempty"`
|
||||
AuthString string `json:"auth_str,omitempty"`
|
||||
Users []HysteriaUser `json:"users,omitempty"`
|
||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||
ReceiveWindowClient uint64 `json:"recv_window_client,omitempty"`
|
||||
MaxConnClient int `json:"max_conn_client,omitempty"`
|
||||
@@ -16,6 +15,12 @@ type HysteriaInboundOptions struct {
|
||||
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
type HysteriaUser struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Auth []byte `json:"auth,omitempty"`
|
||||
AuthString string `json:"auth_str,omitempty"`
|
||||
}
|
||||
|
||||
type HysteriaOutboundOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
|
||||
@@ -122,7 +122,8 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
|
||||
type DialerOptions struct {
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
BindAddress *ListenAddress `json:"bind_address,omitempty"`
|
||||
Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"`
|
||||
Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark int `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
|
||||
@@ -2,12 +2,11 @@ package option
|
||||
|
||||
type ShadowsocksInboundOptions struct {
|
||||
ListenOptions
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
ControlPassword string `json:"control_password,omitempty"`
|
||||
Users []ShadowsocksUser `json:"users,omitempty"`
|
||||
Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Users []ShadowsocksUser `json:"users,omitempty"`
|
||||
Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
|
||||
}
|
||||
|
||||
type ShadowsocksUser struct {
|
||||
|
||||
@@ -2,9 +2,10 @@ package option
|
||||
|
||||
type ShadowTLSInboundOptions struct {
|
||||
ListenOptions
|
||||
Version int `json:"version,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Handshake ShadowTLSHandshakeOptions `json:"handshake"`
|
||||
Version int `json:"version,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
FallbackAfter *int `json:"fallback_after,omitempty"`
|
||||
Handshake ShadowTLSHandshakeOptions `json:"handshake"`
|
||||
}
|
||||
|
||||
type ShadowTLSHandshakeOptions struct {
|
||||
|
||||
@@ -7,7 +7,6 @@ type V2RayAPIOptions struct {
|
||||
|
||||
type V2RayStatsServiceOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
DirectIO bool `json:"direct_io,omitempty"`
|
||||
Inbounds []string `json:"inbounds,omitempty"`
|
||||
Outbounds []string `json:"outbounds,omitempty"`
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ type WireGuardOutboundOptions struct {
|
||||
PeerPublicKey string `json:"peer_public_key"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
Reserved []uint8 `json:"reserved,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
@@ -78,12 +78,6 @@ func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metad
|
||||
}
|
||||
|
||||
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
switch metadata.Protocol {
|
||||
case C.ProtocolQUIC, C.ProtocolDNS:
|
||||
if !metadata.Destination.Addr.IsUnspecified() {
|
||||
return connectPacketConnection(ctx, this, conn, metadata)
|
||||
}
|
||||
}
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var outConn net.PacketConn
|
||||
var err error
|
||||
@@ -98,29 +92,12 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
|
||||
switch metadata.Protocol {
|
||||
case C.ProtocolSTUN:
|
||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout)
|
||||
}
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
|
||||
}
|
||||
|
||||
func connectPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var outConn net.Conn
|
||||
var err error
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
|
||||
} else {
|
||||
outConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
|
||||
}
|
||||
if err != nil {
|
||||
return N.HandshakeFailure(conn, err)
|
||||
}
|
||||
switch metadata.Protocol {
|
||||
case C.ProtocolQUIC:
|
||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.QUICTimeout)
|
||||
case C.ProtocolDNS:
|
||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
|
||||
}
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewUnbindPacketConn(outConn))
|
||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn))
|
||||
}
|
||||
|
||||
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {
|
||||
|
||||
@@ -23,7 +23,10 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*Hysteria)(nil)
|
||||
var (
|
||||
_ adapter.Outbound = (*Hysteria)(nil)
|
||||
_ adapter.InterfaceUpdateListener = (*Hysteria)(nil)
|
||||
)
|
||||
|
||||
type Hysteria struct {
|
||||
myOutboundAdapter
|
||||
@@ -236,6 +239,11 @@ func (h *Hysteria) udpRecvLoop(conn quic.Connection) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hysteria) InterfaceUpdated() error {
|
||||
h.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Hysteria) Close() error {
|
||||
h.connAccess.Lock()
|
||||
defer h.connAccess.Unlock()
|
||||
|
||||
@@ -21,7 +21,10 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*SSH)(nil)
|
||||
var (
|
||||
_ adapter.Outbound = (*SSH)(nil)
|
||||
_ adapter.InterfaceUpdateListener = (*SSH)(nil)
|
||||
)
|
||||
|
||||
type SSH struct {
|
||||
myOutboundAdapter
|
||||
@@ -149,6 +152,11 @@ func (s *SSH) connect() (*ssh.Client, error) {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (s *SSH) InterfaceUpdated() error {
|
||||
common.Close(s.clientConn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSH) Close() error {
|
||||
return common.Close(s.clientConn)
|
||||
}
|
||||
|
||||
@@ -41,9 +41,18 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
startConf := newConfig()
|
||||
startConf.DataDir = os.ExpandEnv(options.DataDirectory)
|
||||
startConf.TempDataDirBase = os.TempDir()
|
||||
startConf.ExtraArgs = options.ExtraArgs
|
||||
if options.DataDirectory != "" {
|
||||
dataDirAbs, _ := filepath.Abs(startConf.DataDir)
|
||||
if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.FileExists(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
|
||||
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
|
||||
}
|
||||
if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.FileExists(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
|
||||
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
|
||||
}
|
||||
}
|
||||
if options.ExecutablePath != "" {
|
||||
startConf.ExePath = options.ExecutablePath
|
||||
startConf.ExtraArgs = options.ExtraArgs
|
||||
startConf.ProcessCreator = nil
|
||||
startConf.UseEmbeddedControlConn = false
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/trojan"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/protocol/trojan"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*Trojan)(nil)
|
||||
|
||||
@@ -22,11 +22,13 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"github.com/sagernet/wireguard-go/device"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*WireGuard)(nil)
|
||||
var (
|
||||
_ adapter.Outbound = (*WireGuard)(nil)
|
||||
_ adapter.InterfaceUpdateListener = (*WireGuard)(nil)
|
||||
)
|
||||
|
||||
type WireGuard struct {
|
||||
myOutboundAdapter
|
||||
@@ -121,7 +123,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
Errorf: func(format string, args ...interface{}) {
|
||||
logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
|
||||
},
|
||||
})
|
||||
}, options.Workers)
|
||||
if debug.Enabled {
|
||||
logger.Trace("created wireguard ipc conf: \n", ipcConf)
|
||||
}
|
||||
@@ -134,6 +136,11 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (w *WireGuard) InterfaceUpdated() error {
|
||||
w.bind.Reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WireGuard) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
switch network {
|
||||
case N.NetworkTCP:
|
||||
|
||||
@@ -4,6 +4,7 @@ Documentation=https://sing-box.sagernet.org
|
||||
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
|
||||
ExecStart=/usr/bin/sing-box run -c /etc/sing-box/config.json
|
||||
|
||||
@@ -4,6 +4,7 @@ Documentation=https://sing-box.sagernet.org
|
||||
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
|
||||
ExecStart=/usr/bin/sing-box run -c /etc/sing-box/%i.json
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.19.2.linux-amd64.tar.gz
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.19.3.linux-amd64.tar.gz
|
||||
sudo rm -rf /usr/local/go
|
||||
sudo tar -C /usr/local -xzf go.tar.gz
|
||||
rm go.tar.gz
|
||||
@@ -16,4 +16,3 @@ popd
|
||||
sudo systemctl stop sing-box
|
||||
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
|
||||
sudo systemctl start sing-box
|
||||
sudo journalctl -u sing-box --output cat -f
|
||||
|
||||
@@ -4,6 +4,7 @@ Documentation=https://sing-box.sagernet.org
|
||||
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
|
||||
ExecStart=/usr/local/bin/sing-box run -c /usr/local/etc/sing-box/config.json
|
||||
|
||||
@@ -99,7 +99,7 @@ type Router struct {
|
||||
v2rayServer adapter.V2RayServer
|
||||
}
|
||||
|
||||
func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) {
|
||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) {
|
||||
if options.DefaultInterface != "" {
|
||||
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
||||
}
|
||||
@@ -112,8 +112,8 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
|
||||
router := &Router{
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
dnsLogger: dnsLogger,
|
||||
logger: logFactory.NewLogger("router"),
|
||||
dnsLogger: logFactory.NewLogger("dns"),
|
||||
outboundByTag: make(map[string]adapter.Outbound),
|
||||
rules: make([]adapter.Rule, 0, len(options.Rules)),
|
||||
dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)),
|
||||
@@ -130,14 +130,14 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
defaultMark: options.DefaultMark,
|
||||
}
|
||||
for i, ruleOptions := range options.Rules {
|
||||
routeRule, err := NewRule(router, logger, ruleOptions)
|
||||
routeRule, err := NewRule(router, router.logger, ruleOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse rule[", i, "]")
|
||||
}
|
||||
router.rules = append(router.rules, routeRule)
|
||||
}
|
||||
for i, dnsRuleOptions := range dnsOptions.Rules {
|
||||
dnsRule, err := NewDNSRule(router, logger, dnsRuleOptions)
|
||||
dnsRule, err := NewDNSRule(router, router.logger, dnsRuleOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns rule[", i, "]")
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
}
|
||||
}
|
||||
transport, err := dns.CreateTransport(ctx, detour, server.Address)
|
||||
transport, err := dns.CreateTransport(ctx, logFactory.NewLogger(F.ToString("dns/transport[", i, "]")), detour, server.Address)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
}
|
||||
if defaultTransport == nil {
|
||||
if len(transports) == 0 {
|
||||
transports = append(transports, dns.NewLocalTransport(dialer.NewRouter(router)))
|
||||
transports = append(transports, dns.NewLocalTransport(N.SystemDialer))
|
||||
}
|
||||
defaultTransport = transports[0]
|
||||
}
|
||||
@@ -262,20 +262,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
if err != nil {
|
||||
return nil, E.New("auto_detect_interface unsupported on current platform")
|
||||
}
|
||||
interfaceMonitor.RegisterCallback(func(event int) error {
|
||||
if C.IsAndroid {
|
||||
var vpnStatus string
|
||||
if router.interfaceMonitor.AndroidVPNEnabled() {
|
||||
vpnStatus = "enabled"
|
||||
} else {
|
||||
vpnStatus = "disabled"
|
||||
}
|
||||
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
|
||||
} else {
|
||||
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
|
||||
@@ -292,12 +279,12 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
}
|
||||
if needFindProcess {
|
||||
searcher, err := process.NewSearcher(process.Config{
|
||||
Logger: logger,
|
||||
Logger: logFactory.NewLogger("router/process"),
|
||||
PackageManager: router.packageManager,
|
||||
})
|
||||
if err != nil {
|
||||
if err != os.ErrInvalid {
|
||||
logger.Warn(E.Cause(err, "create process searcher"))
|
||||
router.logger.Warn(E.Cause(err, "create process searcher"))
|
||||
}
|
||||
} else {
|
||||
router.processSearcher = searcher
|
||||
@@ -1014,3 +1001,28 @@ func (r *Router) NewError(ctx context.Context, err error) {
|
||||
}
|
||||
r.logger.ErrorContext(ctx, err)
|
||||
}
|
||||
|
||||
func (r *Router) notifyNetworkUpdate(int) error {
|
||||
if C.IsAndroid {
|
||||
var vpnStatus string
|
||||
if r.interfaceMonitor.AndroidVPNEnabled() {
|
||||
vpnStatus = "enabled"
|
||||
} else {
|
||||
vpnStatus = "disabled"
|
||||
}
|
||||
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
|
||||
} else {
|
||||
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
}
|
||||
|
||||
for _, outbound := range r.outbounds {
|
||||
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
|
||||
if isListener {
|
||||
err := listener.InterfaceUpdated()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -37,7 +37,11 @@ func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport,
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
||||
}
|
||||
}
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
|
||||
return ctx, r.defaultTransport, domainStrategy
|
||||
} else {
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
||||
35
test/go.mod
35
test/go.mod
@@ -9,20 +9,18 @@ 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.0+incompatible
|
||||
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/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
go.uber.org/goleak v1.2.0
|
||||
golang.org/x/net v0.1.0
|
||||
golang.org/x/net v0.2.0
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../../sing
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.11.8 // indirect
|
||||
github.com/Dreamacro/clash v1.11.12 // 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
|
||||
@@ -41,7 +39,7 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // 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
|
||||
@@ -50,7 +48,7 @@ require (
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
||||
github.com/mholt/acmez v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
@@ -58,17 +56,18 @@ require (
|
||||
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.1.5 // indirect
|
||||
github.com/refraction-networking/utls v1.2.0 // indirect
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // 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-20221031051350-29d8bb1c8127 // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20221031051512-3dfca3ccef42 // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20221028015259-ea5c35f62f07 // indirect
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 // 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/smux v0.0.0-20220831015742-e0f1988e3195 // 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/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
@@ -76,20 +75,20 @@ require (
|
||||
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.1.0 // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b // indirect
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.3.0 // indirect
|
||||
gotest.tools/v3 v3.4.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../../sing
|
||||
|
||||
78
test/go.sum
78
test/go.sum
@@ -3,10 +3,9 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8=
|
||||
github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ=
|
||||
github.com/Dreamacro/clash v1.11.12 h1:zJ+FUWPHWxhfNl5MK64oezFAPPyGth+SDhjuWEJ/jwM=
|
||||
github.com/Dreamacro/clash v1.11.12/go.mod h1:WiRGFHBrOUYP89GXJ9k4KCyZq5i485LWzc4FPsEPlMI=
|
||||
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=
|
||||
@@ -25,7 +24,6 @@ github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c/go.mod h1:+CauB
|
||||
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/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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=
|
||||
@@ -57,8 +55,8 @@ 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.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
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/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=
|
||||
@@ -86,15 +84,15 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.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/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
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.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
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/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=
|
||||
@@ -117,8 +115,8 @@ github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
|
||||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
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=
|
||||
@@ -137,8 +135,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/refraction-networking/utls v1.1.5 h1:JtrojoNhbUQkBqEg05sP3gDgDj6hIEAAVKbI9lx4n6w=
|
||||
github.com/refraction-networking/utls v1.1.5/go.mod h1:jRQxtYi7nkq1p28HF2lwOH5zQm9aC8rpK0O9lIIzGh8=
|
||||
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=
|
||||
@@ -148,28 +146,29 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c
|
||||
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-20221031051350-29d8bb1c8127 h1:rraPfWlUy2cdZ61FLXRCFbL0lb7oocScbr4Ac0rIzTU=
|
||||
github.com/sagernet/quic-go v0.0.0-20221031051350-29d8bb1c8127/go.mod h1:oWFbojDMm85/Jbm/fyWoo8Pux6dIssxGi3q1r+5642A=
|
||||
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/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-20221031051512-3dfca3ccef42 h1:Rp0x9hx2lj4QxTC0cZcJDxxzjn5Zz0q8tgwmZ5aFO0M=
|
||||
github.com/sagernet/sing-dns v0.0.0-20221031051512-3dfca3ccef42/go.mod h1:cyL9DHbBZ0Xlt/8VD0i6yeiDayH0KzWGNQb8MYhhz7g=
|
||||
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-20221028015259-ea5c35f62f07 h1:zupkkVVFWv0QsLPjxEzlzXlLfDk1hUujK8ctJSIKFCI=
|
||||
github.com/sagernet/sing-tun v0.0.0-20221028015259-ea5c35f62f07/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 h1:AZzFNRR/ZwMTceUQ1b/mxx6oyKqmFymdMn/yleJmoVM=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
|
||||
github.com/sagernet/sing-tun v0.0.0-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/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||
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/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/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
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=
|
||||
@@ -209,9 +208,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
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=
|
||||
@@ -241,11 +239,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
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=
|
||||
@@ -255,7 +251,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||
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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -272,19 +268,17 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.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.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
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=
|
||||
@@ -299,7 +293,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
||||
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-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
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=
|
||||
@@ -313,10 +306,6 @@ 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=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -332,8 +321,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.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.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
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=
|
||||
@@ -358,9 +347,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
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=
|
||||
|
||||
61
test/http_test.go
Normal file
61
test/http_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func TestHTTPSelf(t *testing.T) {
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeHTTP,
|
||||
Tag: "http-out",
|
||||
HTTPOptions: option.HTTPOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "http-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testTCP(t, clientPort, testPort)
|
||||
}
|
||||
@@ -29,10 +29,12 @@ func TestHysteriaSelf(t *testing.T) {
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
UpMbps: 100,
|
||||
DownMbps: 100,
|
||||
AuthString: "password",
|
||||
Obfs: "fuck me till the daylight",
|
||||
UpMbps: 100,
|
||||
DownMbps: 100,
|
||||
Users: []option.HysteriaUser{{
|
||||
AuthString: "password",
|
||||
}},
|
||||
Obfs: "fuck me till the daylight",
|
||||
TLS: &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
@@ -91,10 +93,12 @@ func TestHysteriaInbound(t *testing.T) {
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
UpMbps: 100,
|
||||
DownMbps: 100,
|
||||
AuthString: "password",
|
||||
Obfs: "fuck me till the daylight",
|
||||
UpMbps: 100,
|
||||
DownMbps: 100,
|
||||
Users: []option.HysteriaUser{{
|
||||
AuthString: "password",
|
||||
}},
|
||||
Obfs: "fuck me till the daylight",
|
||||
TLS: &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
func AppendRandBytes(b *bytes.Buffer, length int) {
|
||||
b.ReadFrom(io.LimitReader(rand.Reader, int64(length)))
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
)
|
||||
|
||||
const HmacSHA1Len = 10
|
||||
|
||||
func HmacMD5(key, data []byte) []byte {
|
||||
hmacMD5 := hmac.New(md5.New, key)
|
||||
hmacMD5.Write(data)
|
||||
return hmacMD5.Sum(nil)
|
||||
}
|
||||
|
||||
func HmacSHA1(key, data []byte) []byte {
|
||||
hmacSHA1 := hmac.New(sha1.New, key)
|
||||
hmacSHA1.Write(data)
|
||||
return hmacSHA1.Sum(nil)
|
||||
}
|
||||
|
||||
func MD5Sum(b []byte) []byte {
|
||||
h := md5.New()
|
||||
h.Write(b)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func SHA1Sum(b []byte) []byte {
|
||||
h := sha1.New()
|
||||
h.Write(b)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
// XorShift128Plus - a pseudorandom number generator
|
||||
type XorShift128Plus struct {
|
||||
s [2]uint64
|
||||
}
|
||||
|
||||
func (r *XorShift128Plus) Next() uint64 {
|
||||
x := r.s[0]
|
||||
y := r.s[1]
|
||||
r.s[0] = y
|
||||
x ^= x << 23
|
||||
x ^= y ^ (x >> 17) ^ (y >> 26)
|
||||
r.s[1] = x
|
||||
return x + y
|
||||
}
|
||||
|
||||
func (r *XorShift128Plus) InitFromBin(bin []byte) {
|
||||
var full []byte
|
||||
if len(bin) < 16 {
|
||||
full := pool.Get(16)[:0]
|
||||
defer pool.Put(full)
|
||||
full = append(full, bin...)
|
||||
for len(full) < 16 {
|
||||
full = append(full, 0)
|
||||
}
|
||||
} else {
|
||||
full = bin
|
||||
}
|
||||
r.s[0] = binary.LittleEndian.Uint64(full[:8])
|
||||
r.s[1] = binary.LittleEndian.Uint64(full[8:16])
|
||||
}
|
||||
|
||||
func (r *XorShift128Plus) InitFromBinAndLength(bin []byte, length int) {
|
||||
var full []byte
|
||||
if len(bin) < 16 {
|
||||
full := pool.Get(16)[:0]
|
||||
defer pool.Put(full)
|
||||
full = append(full, bin...)
|
||||
for len(full) < 16 {
|
||||
full = append(full, 0)
|
||||
}
|
||||
}
|
||||
full = bin
|
||||
binary.LittleEndian.PutUint16(full, uint16(length))
|
||||
r.s[0] = binary.LittleEndian.Uint64(full[:8])
|
||||
r.s[1] = binary.LittleEndian.Uint64(full[8:16])
|
||||
for i := 0; i < 4; i++ {
|
||||
r.Next()
|
||||
}
|
||||
}
|
||||
@@ -34,19 +34,25 @@ func (c *HashReadConn) Sum() []byte {
|
||||
|
||||
type HashWriteConn struct {
|
||||
net.Conn
|
||||
hmac hash.Hash
|
||||
hmac hash.Hash
|
||||
hasContent bool
|
||||
lastSum []byte
|
||||
}
|
||||
|
||||
func NewHashWriteConn(conn net.Conn, password string) *HashWriteConn {
|
||||
return &HashWriteConn{
|
||||
conn,
|
||||
hmac.New(sha1.New, []byte(password)),
|
||||
Conn: conn,
|
||||
hmac: hmac.New(sha1.New, []byte(password)),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HashWriteConn) Write(p []byte) (n int, err error) {
|
||||
if c.hmac != nil {
|
||||
if c.hasContent {
|
||||
c.lastSum = c.Sum()
|
||||
}
|
||||
c.hmac.Write(p)
|
||||
c.hasContent = true
|
||||
}
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
@@ -55,6 +61,14 @@ func (c *HashWriteConn) Sum() []byte {
|
||||
return c.hmac.Sum(nil)[:8]
|
||||
}
|
||||
|
||||
func (c *HashWriteConn) LastSum() []byte {
|
||||
return c.lastSum
|
||||
}
|
||||
|
||||
func (c *HashWriteConn) Fallback() {
|
||||
c.hmac = nil
|
||||
}
|
||||
|
||||
func (c *HashWriteConn) HasContent() bool {
|
||||
return c.hasContent
|
||||
}
|
||||
|
||||
66
transport/trojan/mux.go
Normal file
66
transport/trojan/mux.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package trojan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
"github.com/sagernet/smux"
|
||||
)
|
||||
|
||||
func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) error {
|
||||
session, err := smux.Server(conn, smuxConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
var stream net.Conn
|
||||
for {
|
||||
stream, err = session.AcceptStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go newMuxConnection(ctx, stream, metadata, handler)
|
||||
}
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
session.Close()
|
||||
})
|
||||
return group.Run(ctx)
|
||||
}
|
||||
|
||||
func newMuxConnection(ctx context.Context, stream net.Conn, metadata M.Metadata, handler Handler) {
|
||||
err := newMuxConnection0(ctx, stream, metadata, handler)
|
||||
if err != nil {
|
||||
handler.NewError(ctx, E.Cause(err, "process trojan-go multiplex connection"))
|
||||
}
|
||||
}
|
||||
|
||||
func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata, handler Handler) error {
|
||||
command, err := rw.ReadByte(stream)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read command")
|
||||
}
|
||||
metadata.Destination, err = M.SocksaddrSerializer.ReadAddrPort(stream)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read destination")
|
||||
}
|
||||
switch command {
|
||||
case CommandTCP:
|
||||
return handler.NewConnection(ctx, stream, metadata)
|
||||
case CommandUDP:
|
||||
return handler.NewPacketConnection(ctx, &PacketConn{stream}, metadata)
|
||||
default:
|
||||
return E.New("unknown command ", command)
|
||||
}
|
||||
}
|
||||
|
||||
func smuxConfig() *smux.Config {
|
||||
config := smux.DefaultConfig()
|
||||
config.KeepAliveDisabled = true
|
||||
return config
|
||||
}
|
||||
313
transport/trojan/protocol.go
Normal file
313
transport/trojan/protocol.go
Normal file
@@ -0,0 +1,313 @@
|
||||
package trojan
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
const (
|
||||
KeyLength = 56
|
||||
CommandTCP = 1
|
||||
CommandUDP = 3
|
||||
CommandMux = 0x7f
|
||||
)
|
||||
|
||||
var CRLF = []byte{'\r', '\n'}
|
||||
|
||||
type ClientConn struct {
|
||||
N.ExtendedConn
|
||||
key [KeyLength]byte
|
||||
destination M.Socksaddr
|
||||
headerWritten bool
|
||||
}
|
||||
|
||||
func NewClientConn(conn net.Conn, key [KeyLength]byte, destination M.Socksaddr) *ClientConn {
|
||||
return &ClientConn{
|
||||
ExtendedConn: bufio.NewExtendedConn(conn),
|
||||
key: key,
|
||||
destination: destination,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientConn) Write(p []byte) (n int, err error) {
|
||||
if c.headerWritten {
|
||||
return c.ExtendedConn.Write(p)
|
||||
}
|
||||
err = ClientHandshake(c.ExtendedConn, c.key, c.destination, p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = len(p)
|
||||
c.headerWritten = true
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
if c.headerWritten {
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
err := ClientHandshakeBuffer(c.ExtendedConn, c.key, c.destination, buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.headerWritten = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if !c.headerWritten {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return bufio.Copy(c.ExtendedConn, r)
|
||||
}
|
||||
|
||||
func (c *ClientConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return bufio.Copy(w, c.ExtendedConn)
|
||||
}
|
||||
|
||||
func (c *ClientConn) FrontHeadroom() int {
|
||||
if !c.headerWritten {
|
||||
return KeyLength + 5 + M.MaxSocksaddrLength
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *ClientConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
type ClientPacketConn struct {
|
||||
net.Conn
|
||||
key [KeyLength]byte
|
||||
headerWritten bool
|
||||
}
|
||||
|
||||
func NewClientPacketConn(conn net.Conn, key [KeyLength]byte) *ClientPacketConn {
|
||||
return &ClientPacketConn{
|
||||
Conn: conn,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
return ReadPacket(c.Conn, buffer)
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
if !c.headerWritten {
|
||||
err := ClientHandshakePacket(c.Conn, c.key, destination, buffer)
|
||||
c.headerWritten = true
|
||||
return err
|
||||
}
|
||||
return WritePacket(c.Conn, buffer, destination)
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
buffer := buf.With(p)
|
||||
destination, err := c.ReadPacket(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = buffer.Len()
|
||||
addr = destination.UDPAddr()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
return bufio.WritePacket(c, p, addr)
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Read(p []byte) (n int, err error) {
|
||||
n, _, err = c.ReadFrom(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Write(p []byte) (n int, err error) {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) FrontHeadroom() int {
|
||||
if !c.headerWritten {
|
||||
return KeyLength + 2*M.MaxSocksaddrLength + 9
|
||||
}
|
||||
return M.MaxSocksaddrLength + 4
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func Key(password string) [KeyLength]byte {
|
||||
var key [KeyLength]byte
|
||||
hash := sha256.New224()
|
||||
common.Must1(hash.Write([]byte(password)))
|
||||
hex.Encode(key[:], hash.Sum(nil))
|
||||
return key
|
||||
}
|
||||
|
||||
func ClientHandshakeRaw(conn net.Conn, key [KeyLength]byte, command byte, destination M.Socksaddr, payload []byte) error {
|
||||
_, err := conn.Write(key[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = conn.Write(CRLF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = conn.Write([]byte{command})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = conn.Write(CRLF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(payload) > 0 {
|
||||
_, err = conn.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClientHandshake(conn net.Conn, key [KeyLength]byte, destination M.Socksaddr, payload []byte) error {
|
||||
headerLen := KeyLength + M.SocksaddrSerializer.AddrPortLen(destination) + 5
|
||||
var header *buf.Buffer
|
||||
defer header.Release()
|
||||
var writeHeader bool
|
||||
if len(payload) > 0 && headerLen+len(payload) < 65535 {
|
||||
buffer := buf.StackNewSize(headerLen + len(payload))
|
||||
defer common.KeepAlive(buffer)
|
||||
header = common.Dup(buffer)
|
||||
} else {
|
||||
buffer := buf.StackNewSize(headerLen)
|
||||
defer common.KeepAlive(buffer)
|
||||
header = common.Dup(buffer)
|
||||
writeHeader = true
|
||||
}
|
||||
common.Must1(header.Write(key[:]))
|
||||
common.Must1(header.Write(CRLF))
|
||||
common.Must(header.WriteByte(CommandTCP))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
common.Must1(header.Write(CRLF))
|
||||
if !writeHeader {
|
||||
common.Must1(header.Write(payload))
|
||||
}
|
||||
|
||||
_, err := conn.Write(header.Bytes())
|
||||
if err != nil {
|
||||
return E.Cause(err, "write request")
|
||||
}
|
||||
|
||||
if writeHeader {
|
||||
_, err = conn.Write(payload)
|
||||
if err != nil {
|
||||
return E.Cause(err, "write payload")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClientHandshakeBuffer(conn net.Conn, key [KeyLength]byte, destination M.Socksaddr, payload *buf.Buffer) error {
|
||||
header := buf.With(payload.ExtendHeader(KeyLength + M.SocksaddrSerializer.AddrPortLen(destination) + 5))
|
||||
common.Must1(header.Write(key[:]))
|
||||
common.Must1(header.Write(CRLF))
|
||||
common.Must(header.WriteByte(CommandTCP))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
common.Must1(header.Write(CRLF))
|
||||
|
||||
_, err := conn.Write(payload.Bytes())
|
||||
if err != nil {
|
||||
return E.Cause(err, "write request")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClientHandshakePacket(conn net.Conn, key [KeyLength]byte, destination M.Socksaddr, payload *buf.Buffer) error {
|
||||
headerLen := KeyLength + 2*M.SocksaddrSerializer.AddrPortLen(destination) + 9
|
||||
payloadLen := payload.Len()
|
||||
var header *buf.Buffer
|
||||
defer header.Release()
|
||||
var writeHeader bool
|
||||
if payload.Start() >= headerLen {
|
||||
header = buf.With(payload.ExtendHeader(headerLen))
|
||||
} else {
|
||||
buffer := buf.StackNewSize(headerLen)
|
||||
defer common.KeepAlive(buffer)
|
||||
header = common.Dup(buffer)
|
||||
writeHeader = true
|
||||
}
|
||||
common.Must1(header.Write(key[:]))
|
||||
common.Must1(header.Write(CRLF))
|
||||
common.Must(header.WriteByte(CommandUDP))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
common.Must1(header.Write(CRLF))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
common.Must(binary.Write(header, binary.BigEndian, uint16(payloadLen)))
|
||||
common.Must1(header.Write(CRLF))
|
||||
|
||||
if writeHeader {
|
||||
_, err := conn.Write(header.Bytes())
|
||||
if err != nil {
|
||||
return E.Cause(err, "write request")
|
||||
}
|
||||
}
|
||||
|
||||
_, err := conn.Write(payload.Bytes())
|
||||
if err != nil {
|
||||
return E.Cause(err, "write payload")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadPacket(conn net.Conn, buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
destination, err := M.SocksaddrSerializer.ReadAddrPort(conn)
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, E.Cause(err, "read destination")
|
||||
}
|
||||
|
||||
var length uint16
|
||||
err = binary.Read(conn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, E.Cause(err, "read chunk length")
|
||||
}
|
||||
|
||||
err = rw.SkipN(conn, 2)
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, E.Cause(err, "skip crlf")
|
||||
}
|
||||
|
||||
_, err = buffer.ReadFullFrom(conn, int(length))
|
||||
return destination, err
|
||||
}
|
||||
|
||||
func WritePacket(conn net.Conn, buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
bufferLen := buffer.Len()
|
||||
header := buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination) + 4))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
common.Must(binary.Write(header, binary.BigEndian, uint16(bufferLen)))
|
||||
common.Must1(header.Write(CRLF))
|
||||
_, err := conn.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return E.Cause(err, "write packet")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
138
transport/trojan/service.go
Normal file
138
transport/trojan/service.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package trojan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
type Handler interface {
|
||||
N.TCPConnectionHandler
|
||||
N.UDPConnectionHandler
|
||||
E.Handler
|
||||
}
|
||||
|
||||
type Service[K comparable] struct {
|
||||
users map[K][56]byte
|
||||
keys map[[56]byte]K
|
||||
handler Handler
|
||||
fallbackHandler N.TCPConnectionHandler
|
||||
}
|
||||
|
||||
func NewService[K comparable](handler Handler, fallbackHandler N.TCPConnectionHandler) *Service[K] {
|
||||
return &Service[K]{
|
||||
users: make(map[K][56]byte),
|
||||
keys: make(map[[56]byte]K),
|
||||
handler: handler,
|
||||
fallbackHandler: fallbackHandler,
|
||||
}
|
||||
}
|
||||
|
||||
var ErrUserExists = E.New("user already exists")
|
||||
|
||||
func (s *Service[K]) UpdateUsers(userList []K, passwordList []string) error {
|
||||
users := make(map[K][56]byte)
|
||||
keys := make(map[[56]byte]K)
|
||||
for i, user := range userList {
|
||||
if _, loaded := users[user]; loaded {
|
||||
return ErrUserExists
|
||||
}
|
||||
key := Key(passwordList[i])
|
||||
if oldUser, loaded := keys[key]; loaded {
|
||||
return E.Extend(ErrUserExists, "password used by ", oldUser)
|
||||
}
|
||||
users[user] = key
|
||||
keys[key] = user
|
||||
}
|
||||
s.users = users
|
||||
s.keys = keys
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
var key [KeyLength]byte
|
||||
n, err := conn.Read(common.Dup(key[:]))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if n != KeyLength {
|
||||
return s.fallback(ctx, conn, metadata, key[:n], E.New("bad request size"))
|
||||
}
|
||||
|
||||
if user, loaded := s.keys[key]; loaded {
|
||||
ctx = auth.ContextWithUser(ctx, user)
|
||||
} else {
|
||||
return s.fallback(ctx, conn, metadata, key[:], E.New("bad request"))
|
||||
}
|
||||
|
||||
err = rw.SkipN(conn, 2)
|
||||
if err != nil {
|
||||
return E.Cause(err, "skip crlf")
|
||||
}
|
||||
|
||||
command, err := rw.ReadByte(conn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read command")
|
||||
}
|
||||
|
||||
switch command {
|
||||
case CommandTCP, CommandUDP, CommandMux:
|
||||
default:
|
||||
return E.New("unknown command ", command)
|
||||
}
|
||||
|
||||
// var destination M.Socksaddr
|
||||
destination, err := M.SocksaddrSerializer.ReadAddrPort(conn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read destination")
|
||||
}
|
||||
|
||||
err = rw.SkipN(conn, 2)
|
||||
if err != nil {
|
||||
return E.Cause(err, "skip crlf")
|
||||
}
|
||||
|
||||
metadata.Protocol = "trojan"
|
||||
metadata.Destination = destination
|
||||
|
||||
switch command {
|
||||
case CommandTCP:
|
||||
return s.handler.NewConnection(ctx, conn, metadata)
|
||||
case CommandUDP:
|
||||
return s.handler.NewPacketConnection(ctx, &PacketConn{conn}, metadata)
|
||||
// case CommandMux:
|
||||
default:
|
||||
return HandleMuxConnection(ctx, conn, metadata, s.handler)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service[K]) fallback(ctx context.Context, conn net.Conn, metadata M.Metadata, header []byte, err error) error {
|
||||
if s.fallbackHandler == nil {
|
||||
return E.Extend(err, "fallback disabled")
|
||||
}
|
||||
conn = bufio.NewCachedConn(conn, buf.As(header).ToOwned())
|
||||
return s.fallbackHandler.NewConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
type PacketConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
return ReadPacket(c.Conn, buffer)
|
||||
}
|
||||
|
||||
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
return WritePacket(c.Conn, buffer, destination)
|
||||
}
|
||||
|
||||
func (c *PacketConn) FrontHeadroom() int {
|
||||
return M.MaxSocksaddrLength + 4
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
if options.ForceLite {
|
||||
return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ func RegisterQUICConstructor(server ServerConstructor[option.V2RayQUICOptions],
|
||||
quicClientConstructor = client
|
||||
}
|
||||
|
||||
func NewQUICServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
func NewQUICServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
if quicServerConstructor == nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
ServerConstructor[O any] func(ctx context.Context, options O, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error)
|
||||
ServerConstructor[O any] func(ctx context.Context, options O, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error)
|
||||
ClientConstructor[O any] func(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options O, tlsConfig tls.Config) (adapter.V2RayClientTransport, error)
|
||||
)
|
||||
|
||||
func NewServerTransport(ctx context.Context, options option.V2RayTransportOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
func NewServerTransport(ctx context.Context, options option.V2RayTransportOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
if options.Type == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
@@ -35,11 +35,8 @@ type Client struct {
|
||||
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
|
||||
var dialOptions []grpc.DialOption
|
||||
if tlsConfig != nil {
|
||||
stdConfig, err := tlsConfig.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(stdConfig)))
|
||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||
dialOptions = append(dialOptions, grpc.WithTransportCredentials(NewTLSTransportCredentials(tlsConfig)))
|
||||
} else {
|
||||
dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
}
|
||||
|
||||
49
transport/v2raygrpc/credentials/credentials.go
Normal file
49
transport/v2raygrpc/credentials/credentials.go
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2021 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// requestInfoKey is a struct to be used as the key to store RequestInfo in a
|
||||
// context.
|
||||
type requestInfoKey struct{}
|
||||
|
||||
// NewRequestInfoContext creates a context with ri.
|
||||
func NewRequestInfoContext(ctx context.Context, ri interface{}) context.Context {
|
||||
return context.WithValue(ctx, requestInfoKey{}, ri)
|
||||
}
|
||||
|
||||
// RequestInfoFromContext extracts the RequestInfo from ctx.
|
||||
func RequestInfoFromContext(ctx context.Context) interface{} {
|
||||
return ctx.Value(requestInfoKey{})
|
||||
}
|
||||
|
||||
// clientHandshakeInfoKey is a struct used as the key to store
|
||||
// ClientHandshakeInfo in a context.
|
||||
type clientHandshakeInfoKey struct{}
|
||||
|
||||
// ClientHandshakeInfoFromContext extracts the ClientHandshakeInfo from ctx.
|
||||
func ClientHandshakeInfoFromContext(ctx context.Context) interface{} {
|
||||
return ctx.Value(clientHandshakeInfoKey{})
|
||||
}
|
||||
|
||||
// NewClientHandshakeInfoContext creates a context with chi.
|
||||
func NewClientHandshakeInfoContext(ctx context.Context, chi interface{}) context.Context {
|
||||
return context.WithValue(ctx, clientHandshakeInfoKey{}, chi)
|
||||
}
|
||||
75
transport/v2raygrpc/credentials/spiffe.go
Normal file
75
transport/v2raygrpc/credentials/spiffe.go
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2020 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package credentials defines APIs for parsing SPIFFE ID.
|
||||
//
|
||||
// All APIs in this package are experimental.
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net/url"
|
||||
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
var logger = grpclog.Component("credentials")
|
||||
|
||||
// SPIFFEIDFromState parses the SPIFFE ID from State. If the SPIFFE ID format
|
||||
// is invalid, return nil with warning.
|
||||
func SPIFFEIDFromState(state tls.ConnectionState) *url.URL {
|
||||
if len(state.PeerCertificates) == 0 || len(state.PeerCertificates[0].URIs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return SPIFFEIDFromCert(state.PeerCertificates[0])
|
||||
}
|
||||
|
||||
// SPIFFEIDFromCert parses the SPIFFE ID from x509.Certificate. If the SPIFFE
|
||||
// ID format is invalid, return nil with warning.
|
||||
func SPIFFEIDFromCert(cert *x509.Certificate) *url.URL {
|
||||
if cert == nil || cert.URIs == nil {
|
||||
return nil
|
||||
}
|
||||
var spiffeID *url.URL
|
||||
for _, uri := range cert.URIs {
|
||||
if uri == nil || uri.Scheme != "spiffe" || uri.Opaque != "" || (uri.User != nil && uri.User.Username() != "") {
|
||||
continue
|
||||
}
|
||||
// From this point, we assume the uri is intended for a SPIFFE ID.
|
||||
if len(uri.String()) > 2048 {
|
||||
logger.Warning("invalid SPIFFE ID: total ID length larger than 2048 bytes")
|
||||
return nil
|
||||
}
|
||||
if len(uri.Host) == 0 || len(uri.Path) == 0 {
|
||||
logger.Warning("invalid SPIFFE ID: domain or workload ID is empty")
|
||||
return nil
|
||||
}
|
||||
if len(uri.Host) > 255 {
|
||||
logger.Warning("invalid SPIFFE ID: domain length larger than 255 characters")
|
||||
return nil
|
||||
}
|
||||
// A valid SPIFFE certificate can only have exactly one URI SAN field.
|
||||
if len(cert.URIs) > 1 {
|
||||
logger.Warning("invalid SPIFFE ID: multiple URI SANs")
|
||||
return nil
|
||||
}
|
||||
spiffeID = uri
|
||||
}
|
||||
return spiffeID
|
||||
}
|
||||
58
transport/v2raygrpc/credentials/syscallconn.go
Normal file
58
transport/v2raygrpc/credentials/syscallconn.go
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2018 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type sysConn = syscall.Conn
|
||||
|
||||
// syscallConn keeps reference of rawConn to support syscall.Conn for channelz.
|
||||
// SyscallConn() (the method in interface syscall.Conn) is explicitly
|
||||
// implemented on this type,
|
||||
//
|
||||
// Interface syscall.Conn is implemented by most net.Conn implementations (e.g.
|
||||
// TCPConn, UnixConn), but is not part of net.Conn interface. So wrapper conns
|
||||
// that embed net.Conn don't implement syscall.Conn. (Side note: tls.Conn
|
||||
// doesn't embed net.Conn, so even if syscall.Conn is part of net.Conn, it won't
|
||||
// help here).
|
||||
type syscallConn struct {
|
||||
net.Conn
|
||||
// sysConn is a type alias of syscall.Conn. It's necessary because the name
|
||||
// `Conn` collides with `net.Conn`.
|
||||
sysConn
|
||||
}
|
||||
|
||||
// WrapSyscallConn tries to wrap rawConn and newConn into a net.Conn that
|
||||
// implements syscall.Conn. rawConn will be used to support syscall, and newConn
|
||||
// will be used for read/write.
|
||||
//
|
||||
// This function returns newConn if rawConn doesn't implement syscall.Conn.
|
||||
func WrapSyscallConn(rawConn, newConn net.Conn) net.Conn {
|
||||
sysConn, ok := rawConn.(syscall.Conn)
|
||||
if !ok {
|
||||
return newConn
|
||||
}
|
||||
return &syscallConn{
|
||||
Conn: newConn,
|
||||
sysConn: sysConn,
|
||||
}
|
||||
}
|
||||
52
transport/v2raygrpc/credentials/util.go
Normal file
52
transport/v2raygrpc/credentials/util.go
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2020 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
const alpnProtoStrH2 = "h2"
|
||||
|
||||
// AppendH2ToNextProtos appends h2 to next protos.
|
||||
func AppendH2ToNextProtos(ps []string) []string {
|
||||
for _, p := range ps {
|
||||
if p == alpnProtoStrH2 {
|
||||
return ps
|
||||
}
|
||||
}
|
||||
ret := make([]string, 0, len(ps)+1)
|
||||
ret = append(ret, ps...)
|
||||
return append(ret, alpnProtoStrH2)
|
||||
}
|
||||
|
||||
// CloneTLSConfig returns a shallow clone of the exported
|
||||
// fields of cfg, ignoring the unexported sync.Once, which
|
||||
// contains a mutex and must not be copied.
|
||||
//
|
||||
// If cfg is nil, a new zero tls.Config is returned.
|
||||
//
|
||||
// TODO: inline this function if possible.
|
||||
func CloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
|
||||
return cfg.Clone()
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
gM "google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
)
|
||||
@@ -26,15 +25,11 @@ type Server struct {
|
||||
server *grpc.Server
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler) (*Server, error) {
|
||||
func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler) (*Server, error) {
|
||||
var serverOptions []grpc.ServerOption
|
||||
if tlsConfig != nil {
|
||||
stdConfig, err := tlsConfig.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stdConfig.NextProtos = []string{"h2"}
|
||||
serverOptions = append(serverOptions, grpc.Creds(credentials.NewTLS(stdConfig)))
|
||||
tlsConfig.SetNextProtos([]string{"h2"})
|
||||
serverOptions = append(serverOptions, grpc.Creds(NewTLSTransportCredentials(tlsConfig)))
|
||||
}
|
||||
server := &Server{ctx, handler, grpc.NewServer(serverOptions...)}
|
||||
RegisterGunServiceCustomNameServer(server.server, server, options.ServiceName)
|
||||
|
||||
86
transport/v2raygrpc/tls_credentials.go
Normal file
86
transport/v2raygrpc/tls_credentials.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package v2raygrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
internal_credentials "github.com/sagernet/sing-box/transport/v2raygrpc/credentials"
|
||||
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
type TLSTransportCredentials struct {
|
||||
config tls.Config
|
||||
}
|
||||
|
||||
func NewTLSTransportCredentials(config tls.Config) credentials.TransportCredentials {
|
||||
return &TLSTransportCredentials{config}
|
||||
}
|
||||
|
||||
func (c *TLSTransportCredentials) Info() credentials.ProtocolInfo {
|
||||
return credentials.ProtocolInfo{
|
||||
SecurityProtocol: "tls",
|
||||
SecurityVersion: "1.2",
|
||||
ServerName: c.config.ServerName(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TLSTransportCredentials) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
||||
cfg := c.config.Clone()
|
||||
if cfg.ServerName() == "" {
|
||||
serverName, _, err := net.SplitHostPort(authority)
|
||||
if err != nil {
|
||||
serverName = authority
|
||||
}
|
||||
cfg.SetServerName(serverName)
|
||||
}
|
||||
conn, err := tls.ClientHandshake(ctx, rawConn, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
tlsInfo := credentials.TLSInfo{
|
||||
State: conn.ConnectionState(),
|
||||
CommonAuthInfo: credentials.CommonAuthInfo{
|
||||
SecurityLevel: credentials.PrivacyAndIntegrity,
|
||||
},
|
||||
}
|
||||
id := internal_credentials.SPIFFEIDFromState(conn.ConnectionState())
|
||||
if id != nil {
|
||||
tlsInfo.SPIFFEID = id
|
||||
}
|
||||
return internal_credentials.WrapSyscallConn(rawConn, conn), tlsInfo, nil
|
||||
}
|
||||
|
||||
func (c *TLSTransportCredentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
||||
serverConfig, isServer := c.config.(tls.ServerConfig)
|
||||
if !isServer {
|
||||
return nil, nil, os.ErrInvalid
|
||||
}
|
||||
conn, err := tls.ServerHandshake(context.Background(), rawConn, serverConfig)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
tlsInfo := credentials.TLSInfo{
|
||||
State: conn.ConnectionState(),
|
||||
CommonAuthInfo: credentials.CommonAuthInfo{
|
||||
SecurityLevel: credentials.PrivacyAndIntegrity,
|
||||
},
|
||||
}
|
||||
id := internal_credentials.SPIFFEIDFromState(conn.ConnectionState())
|
||||
if id != nil {
|
||||
tlsInfo.SPIFFEID = id
|
||||
}
|
||||
return internal_credentials.WrapSyscallConn(rawConn, conn), tlsInfo, nil
|
||||
}
|
||||
|
||||
func (c *TLSTransportCredentials) Clone() credentials.TransportCredentials {
|
||||
return NewTLSTransportCredentials(c.config)
|
||||
}
|
||||
|
||||
func (c *TLSTransportCredentials) OverrideServerName(serverNameOverride string) error {
|
||||
c.config.SetServerName(serverNameOverride)
|
||||
return nil
|
||||
}
|
||||
@@ -106,31 +106,22 @@ func (c *GunConn) Write(b []byte) (n int, err error) {
|
||||
_, err = bufio.Copy(c.writer, io.MultiReader(bytes.NewReader(grpcHeader), bytes.NewReader(protobufHeader[:varuintLen+1]), bytes.NewReader(b)))
|
||||
c.writeAccess.Unlock()
|
||||
buf.Put(grpcHeader)
|
||||
if c.flusher != nil {
|
||||
if err == nil && c.flusher != nil {
|
||||
c.flusher.Flush()
|
||||
}
|
||||
return len(b), baderror.WrapH2(err)
|
||||
}
|
||||
|
||||
func uLen(x uint64) int {
|
||||
i := 0
|
||||
for x >= 0x80 {
|
||||
x >>= 7
|
||||
i++
|
||||
}
|
||||
return i + 1
|
||||
}
|
||||
|
||||
func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
defer buffer.Release()
|
||||
dataLen := buffer.Len()
|
||||
varLen := uLen(uint64(dataLen))
|
||||
varLen := rw.UVariantLen(uint64(dataLen))
|
||||
header := buffer.ExtendHeader(6 + varLen)
|
||||
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))
|
||||
header[5] = 0x0A
|
||||
binary.PutUvarint(header[6:], uint64(dataLen))
|
||||
err := rw.WriteBytes(c.writer, buffer.Bytes())
|
||||
if c.flusher != nil {
|
||||
if err == nil && c.flusher != nil {
|
||||
c.flusher.Flush()
|
||||
}
|
||||
return baderror.WrapH2(err)
|
||||
@@ -153,13 +144,28 @@ func (c *GunConn) RemoteAddr() net.Addr {
|
||||
}
|
||||
|
||||
func (c *GunConn) SetDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetWriteDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GunConn) SetReadDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetReadDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetReadDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GunConn) SetWriteDeadline(t time.Time) error {
|
||||
if responseWriter, loaded := c.writer.(interface {
|
||||
SetWriteDeadline(time.Time) error
|
||||
}); loaded {
|
||||
return responseWriter.SetWriteDeadline(t)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user