mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 02:27:19 +10:00
Compare commits
42 Commits
v1.1-beta4
...
v1.1-beta8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ce244dd04 | ||
|
|
0f57b93925 | ||
|
|
c90a77a185 | ||
|
|
c6586f19fa | ||
|
|
cbab86ae38 | ||
|
|
17b5f031f1 | ||
|
|
b00b6b9e25 | ||
|
|
fb6b3b0401 | ||
|
|
22ea878fe9 | ||
|
|
abe3dc6039 | ||
|
|
852829b9dc | ||
|
|
407509c985 | ||
|
|
9856b73cb5 | ||
|
|
f42356fbcb | ||
|
|
d0b467671a | ||
|
|
c18c545798 | ||
|
|
693ef293ac | ||
|
|
a006627795 | ||
|
|
0738b184e4 | ||
|
|
42524ba04e | ||
|
|
63fc95b96d | ||
|
|
ab436fc137 | ||
|
|
1546770bfd | ||
|
|
f4b2099488 | ||
|
|
a2c4d68031 | ||
|
|
cfe14f2817 | ||
|
|
a5402ffb69 | ||
|
|
4d24cf5ec4 | ||
|
|
668d354771 | ||
|
|
ad14719b14 | ||
|
|
d9aa0a67d6 | ||
|
|
92bf784f4f | ||
|
|
395b13103a | ||
|
|
628cf56d3c | ||
|
|
ac5582537f | ||
|
|
9aa7a20d96 | ||
|
|
189f02c802 | ||
|
|
2373281c41 | ||
|
|
e8f4c2d36f | ||
|
|
07b6db23c1 | ||
|
|
9a3360e5d0 | ||
|
|
007a278ac8 |
34
.github/workflows/test.yml
vendored
Normal file
34
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Test build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- dev-next
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Debug build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: make test
|
||||
@@ -9,8 +9,9 @@ builds:
|
||||
gcflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Commit={{ .ShortCommit }} -s -w -buildid=
|
||||
- -s -w -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_wireguard
|
||||
- with_clash_api
|
||||
|
||||
@@ -8,9 +8,9 @@ ENV CGO_ENABLED=0
|
||||
RUN set -ex \
|
||||
&& apk add git build-base \
|
||||
&& export COMMIT=$(git rev-parse --short HEAD) \
|
||||
&& go build -v -trimpath -tags 'no_gvisor,with_quic,with_wireguard,with_acme' \
|
||||
&& go build -v -trimpath -tags with_quic,with_wireguard,with_acme \
|
||||
-o /go/bin/sing-box \
|
||||
-ldflags "-X github.com/sagernet/sing-box/constant.Commit=${COMMIT} -w -s -buildid=" \
|
||||
-ldflags "-s -w -buildid=" \
|
||||
./cmd/sing-box
|
||||
FROM alpine AS dist
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
|
||||
22
Makefile
22
Makefile
@@ -1,9 +1,8 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_quic,with_wireguard,with_clash_api
|
||||
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \
|
||||
'-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \
|
||||
-w -s -buildid='
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_clash_api
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
|
||||
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
|
||||
MAIN = ./cmd/sing-box
|
||||
|
||||
.PHONY: test release
|
||||
@@ -61,14 +60,19 @@ release_install:
|
||||
go install -v github.com/tcnksm/ghr@latest
|
||||
|
||||
test:
|
||||
@go test -v . && \
|
||||
pushd test && \
|
||||
@go test -v ./... && \
|
||||
cd test && \
|
||||
go mod tidy && \
|
||||
go test -v -tags with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr . && \
|
||||
popd
|
||||
go test -v -tags "$(TAGS_TEST)" .
|
||||
|
||||
test_stdio:
|
||||
@go test -v ./... && \
|
||||
cd test && \
|
||||
go mod tidy && \
|
||||
go test -v -tags "$(TAGS_TEST),force_stdio" .
|
||||
|
||||
clean:
|
||||
rm -rf bin dist
|
||||
rm -rf bin dist sing-box
|
||||
rm -f $(shell go env GOPATH)/sing-box
|
||||
|
||||
update:
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
@@ -12,6 +13,7 @@ type ClashServer interface {
|
||||
Mode() string
|
||||
StoreSelected() bool
|
||||
CacheFile() ClashCacheFile
|
||||
HistoryStorage() *urltest.HistoryStorage
|
||||
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
||||
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/sagernet/sing/common/control"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mdns "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type Router interface {
|
||||
@@ -27,11 +27,11 @@ type Router interface {
|
||||
GeoIPReader() *geoip.Reader
|
||||
LoadGeosite(code string) (Rule, error)
|
||||
|
||||
Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error)
|
||||
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
|
||||
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||
|
||||
InterfaceBindManager() control.BindManager
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
DefaultInterface() string
|
||||
AutoDetectInterface() bool
|
||||
DefaultMark() int
|
||||
|
||||
@@ -38,13 +38,25 @@ type myUpstreamHandlerWrapper struct {
|
||||
}
|
||||
|
||||
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
w.metadata.Destination = metadata.Destination
|
||||
return w.connectionHandler(ctx, conn, w.metadata)
|
||||
myMetadata := w.metadata
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
}
|
||||
return w.connectionHandler(ctx, conn, myMetadata)
|
||||
}
|
||||
|
||||
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
w.metadata.Destination = metadata.Destination
|
||||
return w.packetHandler(ctx, conn, w.metadata)
|
||||
myMetadata := w.metadata
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
}
|
||||
return w.packetHandler(ctx, conn, myMetadata)
|
||||
}
|
||||
|
||||
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||
@@ -78,13 +90,23 @@ func NewUpstreamContextHandler(
|
||||
|
||||
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
myMetadata := ContextFrom(ctx)
|
||||
myMetadata.Destination = metadata.Destination
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
}
|
||||
return w.connectionHandler(ctx, conn, *myMetadata)
|
||||
}
|
||||
|
||||
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
myMetadata := ContextFrom(ctx)
|
||||
myMetadata.Destination = metadata.Destination
|
||||
if metadata.Source.IsValid() {
|
||||
myMetadata.Source = metadata.Source
|
||||
}
|
||||
if metadata.Destination.IsValid() {
|
||||
myMetadata.Destination = metadata.Destination
|
||||
}
|
||||
return w.packetHandler(ctx, conn, *myMetadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -25,30 +25,40 @@ func init() {
|
||||
}
|
||||
|
||||
func printVersion(cmd *cobra.Command, args []string) {
|
||||
var version string
|
||||
if !nameOnly {
|
||||
version = "sing-box "
|
||||
if nameOnly {
|
||||
os.Stdout.WriteString(C.Version + "\n")
|
||||
return
|
||||
}
|
||||
version += F.ToString(C.Version)
|
||||
if C.Commit != "" {
|
||||
version += "." + C.Commit
|
||||
}
|
||||
if !nameOnly {
|
||||
version += " ("
|
||||
version += runtime.Version()
|
||||
version += ", "
|
||||
version += runtime.GOOS
|
||||
version += "/"
|
||||
version += runtime.GOARCH
|
||||
version += ", "
|
||||
version += "CGO "
|
||||
if C.CGO_ENABLED {
|
||||
version += "enabled"
|
||||
} else {
|
||||
version += "disabled"
|
||||
version := "sing-box version " + C.Version + "\n\n"
|
||||
version += "Environment: " + runtime.Version() + " " + runtime.GOOS + "/" + runtime.GOARCH + "\n"
|
||||
|
||||
var tags string
|
||||
var revision string
|
||||
|
||||
debugInfo, loaded := debug.ReadBuildInfo()
|
||||
if loaded {
|
||||
for _, setting := range debugInfo.Settings {
|
||||
switch setting.Key {
|
||||
case "-tags":
|
||||
tags = setting.Value
|
||||
case "vcs.revision":
|
||||
revision = setting.Value
|
||||
}
|
||||
}
|
||||
version += ")"
|
||||
}
|
||||
version += "\n"
|
||||
|
||||
if tags != "" {
|
||||
version += "Tags: " + tags + "\n"
|
||||
}
|
||||
if revision != "" {
|
||||
version += "Revision: " + revision + "\n"
|
||||
}
|
||||
|
||||
if C.CGO_ENABLED {
|
||||
version += "CGO: enabled\n"
|
||||
} else {
|
||||
version += "CGO: disabled\n"
|
||||
}
|
||||
|
||||
os.Stdout.WriteString(version)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
_ "github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -26,7 +26,7 @@ func WrapH2(err error) error {
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
return io.EOF
|
||||
}
|
||||
if Contains(err, "client disconnected", "body closed by handler") {
|
||||
if Contains(err, "client disconnected", "body closed by handler", "response body closed", "; CANCEL") {
|
||||
return net.ErrClosed
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -65,25 +65,23 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
var listener net.ListenConfig
|
||||
if options.BindInterface != "" {
|
||||
warnBindInterfaceOnUnsupportedPlatform.Check()
|
||||
bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)
|
||||
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if router.AutoDetectInterface() {
|
||||
if C.IsWindows {
|
||||
bindFunc := control.BindToInterfaceIndexFunc(func(network, address string) int {
|
||||
return router.InterfaceMonitor().DefaultInterfaceIndex(M.ParseSocksaddr(address).Addr)
|
||||
})
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else {
|
||||
bindFunc := control.BindToInterfaceFunc(router.InterfaceBindManager(), func(network, address string) string {
|
||||
return router.InterfaceMonitor().DefaultInterfaceName(M.ParseSocksaddr(address).Addr)
|
||||
})
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
const useInterfaceName = C.IsLinux
|
||||
bindFunc := control.BindToInterfaceFunc(router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||
if C.IsLinux {
|
||||
return router.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
||||
} else {
|
||||
return "", router.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
||||
}
|
||||
})
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if router.DefaultInterface() != "" {
|
||||
bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())
|
||||
bindFunc := control.BindToInterface(router.InterfaceFinder(), router.DefaultInterface(), -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
@@ -112,7 +110,13 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
if options.TCPFastOpen {
|
||||
warnTFOOnUnsupportedPlatform.Check()
|
||||
}
|
||||
if !options.UDPFragment {
|
||||
var udpFragment bool
|
||||
if options.UDPFragment != nil {
|
||||
udpFragment = *options.UDPFragment
|
||||
} else {
|
||||
udpFragment = options.UDPFragmentDefault
|
||||
}
|
||||
if !udpFragment {
|
||||
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
|
||||
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
|
||||
}
|
||||
@@ -142,7 +146,7 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
case N.NetworkUDP:
|
||||
return d.udpDialer.DialContext(ctx, network, address.String())
|
||||
}
|
||||
return d.dialer.DialContext(ctx, network, address.Unwrap().String())
|
||||
return d.dialer.DialContext(ctx, network, address.String())
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
|
||||
@@ -466,10 +466,7 @@ func (c *ClientPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buffer.FreeLen() < int(length) {
|
||||
return destination, io.ErrShortBuffer
|
||||
}
|
||||
_, err = io.ReadFull(c.ExtendedConn, buffer.Extend(int(length)))
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package mux
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
)
|
||||
|
||||
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
@@ -26,14 +26,21 @@ func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var stream net.Conn
|
||||
for {
|
||||
stream, err = session.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
var stream net.Conn
|
||||
for {
|
||||
stream, err = session.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
|
||||
}
|
||||
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
|
||||
}
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
session.Close()
|
||||
})
|
||||
return group.Run(ctx)
|
||||
}
|
||||
|
||||
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
|
||||
@@ -158,9 +165,6 @@ func (c *ServerPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksad
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buffer.FreeLen() < int(length) {
|
||||
return destination, io.ErrShortBuffer
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
if err != nil {
|
||||
return
|
||||
@@ -223,9 +227,6 @@ func (c *ServerPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Soc
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buffer.FreeLen() < int(length) {
|
||||
return destination, io.ErrShortBuffer
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -28,11 +28,5 @@ type Info struct {
|
||||
}
|
||||
|
||||
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||
info, err := findProcessInfo(searcher, ctx, network, source, destination)
|
||||
if err != nil {
|
||||
if source.Addr().Is4In6() {
|
||||
info, err = findProcessInfo(searcher, ctx, network, netip.AddrPortFrom(netip.AddrFrom4(source.Addr().As4()), source.Port()), destination)
|
||||
}
|
||||
}
|
||||
return info, err
|
||||
return findProcessInfo(searcher, ctx, network, source, destination)
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ func (l *Listener) Accept() (net.Conn, error) {
|
||||
}
|
||||
if header != nil {
|
||||
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
|
||||
Source: M.SocksaddrFromNet(header.SourceAddr),
|
||||
Destination: M.SocksaddrFromNet(header.DestinationAddr),
|
||||
Source: M.SocksaddrFromNet(header.SourceAddr).Unwrap(),
|
||||
Destination: M.SocksaddrFromNet(header.DestinationAddr).Unwrap(),
|
||||
}}, nil
|
||||
}
|
||||
return conn, nil
|
||||
|
||||
@@ -2,14 +2,11 @@ package redir
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -32,6 +29,18 @@ func TProxy(fd uintptr, isIPv6 bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func TProxyWriteBack() control.Func {
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
return control.Raw(conn, func(fd uintptr) error {
|
||||
if M.ParseSocksaddr(address).Addr.Is6() {
|
||||
return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
|
||||
} else {
|
||||
return syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
||||
controlMessages, err := unix.ParseSocketControlMessage(oob)
|
||||
if err != nil {
|
||||
@@ -46,79 +55,3 @@ func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
||||
}
|
||||
return netip.AddrPort{}, E.New("not found")
|
||||
}
|
||||
|
||||
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
rSockAddr, err := udpAddrToSockAddr(rAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lSockAddr, err := udpAddrToSockAddr(lAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fd, err := syscall.Socket(udpAddrFamily(lAddr, rAddr), syscall.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = syscall.Bind(fd, lSockAddr); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = syscall.Connect(fd, rSockAddr); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr))
|
||||
defer fdFile.Close()
|
||||
|
||||
c, err := net.FileConn(fdFile)
|
||||
if err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.(*net.UDPConn), nil
|
||||
}
|
||||
|
||||
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
|
||||
switch {
|
||||
case addr.IP.To4() != nil:
|
||||
ip := [4]byte{}
|
||||
copy(ip[:], addr.IP.To4())
|
||||
|
||||
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
|
||||
|
||||
default:
|
||||
ip := [16]byte{}
|
||||
copy(ip[:], addr.IP.To16())
|
||||
|
||||
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
|
||||
if err != nil {
|
||||
zoneID = 0
|
||||
}
|
||||
|
||||
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func udpAddrFamily(lAddr, rAddr *net.UDPAddr) int {
|
||||
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
|
||||
return syscall.AF_INET
|
||||
}
|
||||
return syscall.AF_INET6
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
package redir
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
func TProxy(fd uintptr, isIPv6 bool) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func TProxyWriteBack() control.Func {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
||||
return netip.AddrPort{}, os.ErrInvalid
|
||||
}
|
||||
|
||||
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.InboundContext, error) {
|
||||
@@ -44,18 +45,13 @@ func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.
|
||||
}
|
||||
|
||||
func DomainNameQuery(ctx context.Context, packet []byte) (*adapter.InboundContext, error) {
|
||||
var parser dnsmessage.Parser
|
||||
_, err := parser.Start(packet)
|
||||
var msg mDNS.Msg
|
||||
err := msg.Unpack(packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
question, err := parser.Question()
|
||||
if err != nil {
|
||||
if len(msg.Question) == 0 || msg.Question[0].Qclass != mDNS.ClassINET || !M.IsDomainName(msg.Question[0].Name) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
domain := question.Name.String()
|
||||
if question.Class == dnsmessage.ClassINET && IsDomainName(domain) {
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolDNS /*, Domain: domain*/}, nil
|
||||
}
|
||||
return nil, os.ErrInvalid
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolDNS}, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package sniff
|
||||
|
||||
import _ "unsafe" // for linkname
|
||||
|
||||
//go:linkname IsDomainName net.isDomainName
|
||||
func IsDomainName(domain string) bool
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"crypto/tls"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/certmagic"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/mholt/acmez/acme"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
type echClientConfig struct {
|
||||
@@ -166,19 +165,17 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
|
||||
return &echClientConfig{&tlsConfig}, nil
|
||||
}
|
||||
|
||||
const typeHTTPS = 65
|
||||
|
||||
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||
message := &dnsmessage.Message{
|
||||
Header: dnsmessage.Header{
|
||||
message := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
RecursionDesired: true,
|
||||
},
|
||||
Questions: []dnsmessage.Question{
|
||||
Question: []mDNS.Question{
|
||||
{
|
||||
Name: dnsmessage.MustNewName(serverName + "."),
|
||||
Type: typeHTTPS,
|
||||
Class: dnsmessage.ClassINET,
|
||||
Name: serverName + ".",
|
||||
Qtype: mDNS.TypeHTTPS,
|
||||
Qclass: mDNS.ClassINET,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -186,19 +183,10 @@ func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serve
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.RCode != dnsmessage.RCodeSuccess {
|
||||
return nil, dns.RCodeError(response.RCode)
|
||||
if response.Rcode != mDNS.RcodeSuccess {
|
||||
return nil, dns.RCodeError(response.Rcode)
|
||||
}
|
||||
content, err := response.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mMsg mDNS.Msg
|
||||
err = mMsg.Unpack(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rr := range mMsg.Answer {
|
||||
for _, rr := range response.Answer {
|
||||
switch resource := rr.(type) {
|
||||
case *mDNS.HTTPS:
|
||||
for _, value := range resource.Value {
|
||||
|
||||
50
common/tls/mkcert.go
Normal file
50
common/tls/mkcert.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateKeyPair(serverName string) (*tls.Certificate, error) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: time.Now().Add(time.Hour * -1),
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
Subject: pkix.Name{
|
||||
CommonName: serverName,
|
||||
},
|
||||
DNSNames: []string{serverName},
|
||||
}
|
||||
publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privateDer, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publicPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer})
|
||||
privPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDer})
|
||||
keyPair, err := tls.X509KeyPair(publicPem, privPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &keyPair, err
|
||||
}
|
||||
@@ -34,6 +34,8 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
||||
c.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
var errInsecureUnused = E.New("tls: insecure unused")
|
||||
|
||||
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
@@ -46,6 +48,9 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.Insecure {
|
||||
return nil, errInsecureUnused
|
||||
}
|
||||
} else {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
@@ -102,17 +107,23 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
}
|
||||
key = content
|
||||
}
|
||||
if certificate == nil {
|
||||
return nil, E.New("missing certificate")
|
||||
if certificate == nil && key == nil && options.Insecure {
|
||||
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return GenerateKeyPair(info.ServerName)
|
||||
}
|
||||
} else {
|
||||
if certificate == nil {
|
||||
return nil, E.New("missing certificate")
|
||||
} else if key == nil {
|
||||
return nil, E.New("missing key")
|
||||
}
|
||||
|
||||
keyPair, err := tls.X509KeyPair(certificate, key)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse x509 key pair")
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||
}
|
||||
if key == nil {
|
||||
return nil, E.New("missing key")
|
||||
}
|
||||
keyPair, err := tls.X509KeyPair(certificate, key)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse x509 key pair")
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||
}
|
||||
return &STDServerConfig{
|
||||
config: tlsConfig,
|
||||
|
||||
@@ -25,4 +25,5 @@ const (
|
||||
|
||||
const (
|
||||
TypeSelector = "selector"
|
||||
TypeURLTest = "urltest"
|
||||
)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//go:build with_quic
|
||||
|
||||
package constant
|
||||
|
||||
const QUIC_AVAILABLE = true
|
||||
@@ -1,9 +0,0 @@
|
||||
//go:build !with_quic
|
||||
|
||||
package constant
|
||||
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
const QUIC_AVAILABLE = false
|
||||
|
||||
var ErrQUICNotIncluded = E.New(`QUIC is not included in this build, rebuild with -tags with_quic`)
|
||||
@@ -3,10 +3,11 @@ package constant
|
||||
import "time"
|
||||
|
||||
const (
|
||||
TCPTimeout = 5 * time.Second
|
||||
ReadPayloadTimeout = 300 * time.Millisecond
|
||||
DNSTimeout = 10 * time.Second
|
||||
QUICTimeout = 30 * time.Second
|
||||
STUNTimeout = 15 * time.Second
|
||||
UDPTimeout = 5 * time.Minute
|
||||
TCPTimeout = 5 * time.Second
|
||||
ReadPayloadTimeout = 300 * time.Millisecond
|
||||
DNSTimeout = 10 * time.Second
|
||||
QUICTimeout = 30 * time.Second
|
||||
STUNTimeout = 15 * time.Second
|
||||
UDPTimeout = 5 * time.Minute
|
||||
DefaultURLTestInterval = 1 * time.Minute
|
||||
)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
package constant
|
||||
|
||||
var (
|
||||
Version = "1.1-beta4"
|
||||
Commit = ""
|
||||
)
|
||||
var Version = "1.1-beta8"
|
||||
|
||||
@@ -1,3 +1,58 @@
|
||||
#### 1.1-beta8
|
||||
|
||||
* Fix leaks on close
|
||||
* Improve websocket writer
|
||||
* Refine tproxy write back
|
||||
* Refine 4in6 processing
|
||||
* Fix shadowsocks plugins
|
||||
* Fix missing source address from transport connection
|
||||
* Fix fqdn socks5 outbound connection
|
||||
* Fix read source address from grpc-go
|
||||
|
||||
#### 1.0.5
|
||||
|
||||
* Fix missing source address from transport connection
|
||||
* Fix fqdn socks5 outbound connection
|
||||
* Fix read source address from grpc-go
|
||||
|
||||
#### 1.1-beta7
|
||||
|
||||
* Add v2ray mux and XUDP support for VMess inbound
|
||||
* Add XUDP support for VMess outbound
|
||||
* Disable DF on direct outbound by default
|
||||
* Fix bugs in 1.1-beta6
|
||||
|
||||
#### 1.1-beta6
|
||||
|
||||
* Add [URLTest outbound](/configuration/outbound/urltest)
|
||||
* Fix bugs in 1.1-beta5
|
||||
|
||||
#### 1.1-beta5
|
||||
|
||||
* Print tags in version command
|
||||
* Redirect clash hello to external ui
|
||||
* Move shadowsocksr implementation to clash
|
||||
* Make gVisor optional **1**
|
||||
* Refactor to miekg/dns
|
||||
* Refactor bind control
|
||||
* Fix build on go1.18
|
||||
* Fix clash store-selected
|
||||
* Fix close grpc conn
|
||||
* Fix port rule match logic
|
||||
* Fix clash api proxy type
|
||||
|
||||
*1*:
|
||||
|
||||
The build tag `no_gvisor` is replaced by `with_gvisor`.
|
||||
|
||||
The default tun stack is changed to system.
|
||||
|
||||
#### 1.0.4
|
||||
|
||||
* Fix close grpc conn
|
||||
* Fix port rule match logic
|
||||
* Fix clash api proxy type
|
||||
|
||||
#### 1.1-beta4
|
||||
|
||||
* Add internal simple-obfs and v2ray-plugin [Shadowsocks plugins](/configuration/outbound/shadowsocks#plugin)
|
||||
|
||||
@@ -104,8 +104,10 @@
|
||||
|
||||
The default rule uses the following matching logic:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -103,8 +103,10 @@
|
||||
|
||||
默认规则使用以下匹配逻辑:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"auto_route": true,
|
||||
"strict_route": true,
|
||||
"endpoint_independent_nat": false,
|
||||
"stack": "gvisor",
|
||||
"stack": "system",
|
||||
"include_uid": [
|
||||
0
|
||||
],
|
||||
@@ -112,15 +112,15 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
|
||||
|
||||
TCP/IP stack.
|
||||
|
||||
| Stack | Description | Status |
|
||||
|------------------|--------------------------------------------------------------------------------|-------------------|
|
||||
| gVisor (default) | Based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| system | Less compatibility and sometimes better performance. | recommended |
|
||||
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
| Stack | Description | Status |
|
||||
|------------------|----------------------------------------------------------------------------------|-------------------|
|
||||
| system (default) | Sometimes better performance | recommended |
|
||||
| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
The LWIP stack is not included by default, see [Installation](/#installation).
|
||||
gVisor and LWIP stacks is not included by default, see [Installation](/#installation).
|
||||
|
||||
#### include_uid
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"auto_route": true,
|
||||
"strict_route": true,
|
||||
"endpoint_independent_nat": false,
|
||||
"stack": "gvisor",
|
||||
"stack": "system",
|
||||
"include_uid": [
|
||||
0
|
||||
],
|
||||
@@ -107,15 +107,15 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
|
||||
|
||||
TCP/IP 栈。
|
||||
|
||||
| 栈 | 描述 | 状态 |
|
||||
|------------------|--------------------------------------------------------------------------|-------|
|
||||
| gVisor (default) | 基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
||||
| system | 兼容性较差,有时性能更好。 | 推荐 |
|
||||
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
||||
| 栈 | 描述 | 状态 |
|
||||
|-------------|--------------------------------------------------------------------------|-------|
|
||||
| system (默认) | 有时性能更好 | 推荐 |
|
||||
| gVisor | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
||||
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
|
||||
默认安装不包含 gVisor 和 LWIP 栈,请参阅 [安装](/zh/#_2)。
|
||||
|
||||
#### include_uid
|
||||
|
||||
@@ -145,10 +145,10 @@ TCP/IP 栈。
|
||||
|
||||
限制被路由的 Android 用户。
|
||||
|
||||
| 常用用户 | ID |
|
||||
| 常用用户 | ID |
|
||||
|--|-----|
|
||||
| 您 | 0 |
|
||||
| 工作资料 | 10 |
|
||||
| 您 | 0 |
|
||||
| 工作资料 | 10 |
|
||||
|
||||
#### include_package
|
||||
|
||||
|
||||
@@ -15,21 +15,24 @@
|
||||
|
||||
### Fields
|
||||
|
||||
| Type | Format |
|
||||
|---------------|------------------------------|
|
||||
| `direct` | [Direct](./direct) |
|
||||
| `block` | [Block](./block) |
|
||||
| `socks` | [SOCKS](./socks) |
|
||||
| `http` | [HTTP](./http) |
|
||||
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
||||
| `vmess` | [VMess](./vmess) |
|
||||
| `trojan` | [Trojan](./trojan) |
|
||||
| `wireguard` | [Wireguard](./wireguard) |
|
||||
| `hysteria` | [Hysteria](./hysteria) |
|
||||
| `tor` | [Tor](./tor) |
|
||||
| `ssh` | [SSH](./ssh) |
|
||||
| `dns` | [DNS](./dns) |
|
||||
| `selector` | [Selector](./selector) |
|
||||
| Type | Format |
|
||||
|----------------|--------------------------------|
|
||||
| `direct` | [Direct](./direct) |
|
||||
| `block` | [Block](./block) |
|
||||
| `socks` | [SOCKS](./socks) |
|
||||
| `http` | [HTTP](./http) |
|
||||
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
||||
| `vmess` | [VMess](./vmess) |
|
||||
| `trojan` | [Trojan](./trojan) |
|
||||
| `wireguard` | [Wireguard](./wireguard) |
|
||||
| `hysteria` | [Hysteria](./hysteria) |
|
||||
| `shadowsocksr` | [ShadowsocksR](./shadowsocksr) |
|
||||
| `vless` | [VLESS](./vless) |
|
||||
| `tor` | [Tor](./tor) |
|
||||
| `ssh` | [SSH](./ssh) |
|
||||
| `dns` | [DNS](./dns) |
|
||||
| `selector` | [Selector](./selector) |
|
||||
| `urltest` | [URLTest](./urltest) |
|
||||
|
||||
#### tag
|
||||
|
||||
|
||||
@@ -15,21 +15,24 @@
|
||||
|
||||
### 字段
|
||||
|
||||
| 类型 | 格式 |
|
||||
|---------------|------------------------------|
|
||||
| `direct` | [Direct](./direct) |
|
||||
| `block` | [Block](./block) |
|
||||
| `socks` | [SOCKS](./socks) |
|
||||
| `http` | [HTTP](./http) |
|
||||
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
||||
| `vmess` | [VMess](./vmess) |
|
||||
| `trojan` | [Trojan](./trojan) |
|
||||
| `wireguard` | [Wireguard](./wireguard) |
|
||||
| `hysteria` | [Hysteria](./hysteria) |
|
||||
| `tor` | [Tor](./tor) |
|
||||
| `ssh` | [SSH](./ssh) |
|
||||
| `dns` | [DNS](./dns) |
|
||||
| `selector` | [Selector](./selector) |
|
||||
| 类型 | 格式 |
|
||||
|----------------|--------------------------------|
|
||||
| `direct` | [Direct](./direct) |
|
||||
| `block` | [Block](./block) |
|
||||
| `socks` | [SOCKS](./socks) |
|
||||
| `http` | [HTTP](./http) |
|
||||
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
|
||||
| `vmess` | [VMess](./vmess) |
|
||||
| `trojan` | [Trojan](./trojan) |
|
||||
| `wireguard` | [Wireguard](./wireguard) |
|
||||
| `hysteria` | [Hysteria](./hysteria) |
|
||||
| `shadowsocksr` | [ShadowsocksR](./shadowsocksr) |
|
||||
| `vless` | [VLESS](./vless) |
|
||||
| `tor` | [Tor](./tor) |
|
||||
| `ssh` | [SSH](./ssh) |
|
||||
| `dns` | [DNS](./dns) |
|
||||
| `selector` | [Selector](./selector) |
|
||||
| `urltest` | [URLTest](./urltest) |
|
||||
|
||||
#### tag
|
||||
|
||||
|
||||
37
docs/configuration/outbound/urltest.md
Normal file
37
docs/configuration/outbound/urltest.md
Normal file
@@ -0,0 +1,37 @@
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "urltest",
|
||||
"tag": "auto",
|
||||
|
||||
"outbounds": [
|
||||
"proxy-a",
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"url": "http://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"tolerance": 50
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
#### outbounds
|
||||
|
||||
==Required==
|
||||
|
||||
List of outbound tags to test.
|
||||
|
||||
#### url
|
||||
|
||||
The URL to test. `http://www.gstatic.com/generate_204` will be used if empty.
|
||||
|
||||
#### interval
|
||||
|
||||
The test interval. `1m` will be used if empty.
|
||||
|
||||
#### tolerance
|
||||
|
||||
The test tolerance in milliseconds. `50` will be used if empty.
|
||||
37
docs/configuration/outbound/urltest.zh.md
Normal file
37
docs/configuration/outbound/urltest.zh.md
Normal file
@@ -0,0 +1,37 @@
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "urltest",
|
||||
"tag": "auto",
|
||||
|
||||
"outbounds": [
|
||||
"proxy-a",
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"url": "http://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"tolerance": 50
|
||||
}
|
||||
```
|
||||
|
||||
### 字段
|
||||
|
||||
#### outbounds
|
||||
|
||||
==必填==
|
||||
|
||||
用于测试的出站标签列表。
|
||||
|
||||
#### url
|
||||
|
||||
用于测试的链接。默认使用 `http://www.gstatic.com/generate_204`。
|
||||
|
||||
#### interval
|
||||
|
||||
测试间隔。 默认使用 `1m`。
|
||||
|
||||
#### tolerance
|
||||
|
||||
以毫秒为单位的测试容差。 默认使用 `50`。
|
||||
@@ -14,7 +14,7 @@
|
||||
"authenticated_length": true,
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
"packet_addr": false,
|
||||
"packet_encoding": "",
|
||||
"multiplex": {},
|
||||
"transport": {},
|
||||
|
||||
@@ -84,9 +84,13 @@ Both is enabled by default.
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
#### packet_addr
|
||||
#### packet_encoding
|
||||
|
||||
Enable packetaddr support.
|
||||
| Encoding | Description |
|
||||
|------------|-----------------------|
|
||||
| (none) | Disabled |
|
||||
| packetaddr | Supported by v2ray 5+ |
|
||||
| xudp | Supported by xray |
|
||||
|
||||
#### multiplex
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"authenticated_length": true,
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
"packet_addr": false,
|
||||
"packet_encoding": "",
|
||||
"multiplex": {},
|
||||
"transport": {},
|
||||
|
||||
@@ -84,9 +84,13 @@ VMess 用户 ID。
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
#### packet_addr
|
||||
#### packet_encoding
|
||||
|
||||
启用 packetaddr 支持。
|
||||
| 编码 | 描述 |
|
||||
|------------|---------------|
|
||||
| (空) | 禁用 |
|
||||
| packetaddr | 由 v2ray 5+ 支持 |
|
||||
| xudp | 由 xray 支持 |
|
||||
|
||||
#### multiplex
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
|
||||
WireGuard is not included by default, see [Installation](/#installation).
|
||||
|
||||
!!! warning ""
|
||||
|
||||
gVisor, which is required by the unprivileged WireGuard is not included by default, see [Installation](/#installation).
|
||||
|
||||
### Fields
|
||||
|
||||
#### server
|
||||
@@ -44,7 +48,9 @@ The server port.
|
||||
|
||||
Use system tun support.
|
||||
|
||||
Requires privileges and cannot conflict with system interfaces.
|
||||
Requires privilege and cannot conflict with system interfaces.
|
||||
|
||||
Forced if gVisor not included in the build.
|
||||
|
||||
#### interface_name
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
|
||||
默认安装不包含 WireGuard, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含被非特权 WireGuard 需要的 gVisor, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
### 字段
|
||||
|
||||
#### server
|
||||
@@ -46,6 +50,8 @@
|
||||
|
||||
需要特权且不能与系统接口冲突。
|
||||
|
||||
如果 gVisor 未包含在构建中,则强制执行。
|
||||
|
||||
#### interface_name
|
||||
|
||||
启用 `system_interface` 时的自定义设备名称。
|
||||
|
||||
@@ -107,8 +107,10 @@
|
||||
|
||||
The default rule uses the following matching logic:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -105,8 +105,10 @@
|
||||
|
||||
默认规则使用以下匹配逻辑:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -53,17 +53,6 @@ we need to do this.
|
||||
|
||||
The library needs to be updated with the upstream.
|
||||
|
||||
#### certmagic
|
||||
|
||||
Link: [GitHub repository](https://github.com/SagerNet/certmagic)
|
||||
|
||||
Fork of `caddyserver/certmagic`
|
||||
|
||||
Since upstream uses `miekg/dns` and we use `x/net/dnsmessage`, we need to replace its DNS part with our own
|
||||
implementation.
|
||||
|
||||
The library needs to be updated with the upstream.
|
||||
|
||||
#### smux
|
||||
|
||||
Link: [GitHub repository](https://github.com/SagerNet/smux)
|
||||
|
||||
@@ -95,7 +95,9 @@
|
||||
| cn | 17.8M | 140.3M |
|
||||
| cn (Loyalsoldier) | 74.3M | 246.7M |
|
||||
|
||||
#### Shadowsocks benchmark
|
||||
#### Benchmark
|
||||
|
||||
##### Shadowsocks
|
||||
|
||||
| / | none | aes-128-gcm | 2022-blake3-aes-128-gcm |
|
||||
|------------------------------------|:---------:|:-----------:|:-----------------------:|
|
||||
@@ -103,6 +105,13 @@
|
||||
| shadowsocks-rust (v1.15.0-alpha.5) | 10.7 Gbps | / | 9.36 Gbps |
|
||||
| sing-box | 29.0 Gbps | / | 11.8 Gbps |
|
||||
|
||||
##### VMess
|
||||
|
||||
| / | TCP | HTTP | H2 TLS | WebSocket TLS | gRPC TLS |
|
||||
|--------------------|:---------:|:---------:|:---------:|:-------------:|:---------:|
|
||||
| v2ray-core (5.1.0) | 7.86 GBps | 2.86 Gbps | 1.83 Gbps | 2.36 Gbps | 2.43 Gbps |
|
||||
| sing-box | 7.96 Gbps | 8.09 Gbps | 6.11 Gbps | 8.02 Gbps | 6.35 Gbps |
|
||||
|
||||
#### License
|
||||
|
||||
| / | License |
|
||||
|
||||
@@ -32,7 +32,7 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
| `with_utls` | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](./configuration/shared/tls#utls). |
|
||||
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
|
||||
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
|
||||
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
| `with_gvisor` | Build with gVisor support, see [Tun inbound](./configuration/inbound/tun#stack) and [WireGuard outbound](./configuration/outbound/wireguard#system_interface). |
|
||||
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
|
||||
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
|
||||
@@ -45,10 +45,6 @@ sing-box version
|
||||
It is also recommended to use systemd to manage sing-box service,
|
||||
see [Linux server installation example](./examples/linux-server-installation).
|
||||
|
||||
## Contributors
|
||||
|
||||
[](https://github.com/sagernet/sing-box/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
|
||||
@@ -29,10 +29,10 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
|
||||
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
||||
| `with_utls` | 启用 [uTLS](https://github.com/refraction-networking/utls) 支持, 参阅 [TLS](./configuration/shared/tls#utls)。 |
|
||||
| `with_utls` | 启用 uTLS 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
| `with_clash_api` | 启用 Clash API 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `with_gvisor` | 启用 gVisor 支持,参阅 [Tun 入站](./configuration/inbound/tun#stack) 和 [WireGuard 出站](./configuration/outbound/wireguard#system_interface)。 |
|
||||
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
||||
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
|
||||
@@ -42,13 +42,9 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
sing-box version
|
||||
```
|
||||
|
||||
同时推荐使用 Systemd 来管理 sing-box 服务器实例。
|
||||
同时推荐使用 systemd 来管理 sing-box 服务器实例。
|
||||
参阅 [Linux 服务器安装示例](./examples/linux-server-installation)。
|
||||
|
||||
## 贡献者
|
||||
|
||||
[](https://github.com/sagernet/sing-box/graphs/contributors)
|
||||
|
||||
## 授权
|
||||
|
||||
```
|
||||
|
||||
@@ -61,7 +61,6 @@ func findProxyByName(router adapter.Router) func(next http.Handler) http.Handler
|
||||
func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||
var info badjson.JSONObject
|
||||
var clashType string
|
||||
var isGroup bool
|
||||
switch detour.Type() {
|
||||
case C.TypeDirect:
|
||||
clashType = "Direct"
|
||||
@@ -70,18 +69,31 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||
case C.TypeSocks:
|
||||
clashType = "Socks"
|
||||
case C.TypeHTTP:
|
||||
clashType = "Http"
|
||||
clashType = "HTTP"
|
||||
case C.TypeShadowsocks:
|
||||
clashType = "Shadowsocks"
|
||||
case C.TypeVMess:
|
||||
clashType = "Vmess"
|
||||
clashType = "VMess"
|
||||
case C.TypeTrojan:
|
||||
clashType = "Trojan"
|
||||
case C.TypeHysteria:
|
||||
clashType = "Hysteria"
|
||||
case C.TypeWireGuard:
|
||||
clashType = "WireGuard"
|
||||
case C.TypeShadowsocksR:
|
||||
clashType = "ShadowsocksR"
|
||||
case C.TypeVLESS:
|
||||
clashType = "VLESS"
|
||||
case C.TypeTor:
|
||||
clashType = "Tor"
|
||||
case C.TypeSSH:
|
||||
clashType = "SSH"
|
||||
case C.TypeSelector:
|
||||
clashType = "Selector"
|
||||
isGroup = true
|
||||
case C.TypeURLTest:
|
||||
clashType = "URLTest"
|
||||
default:
|
||||
clashType = "Socks"
|
||||
clashType = "Direct"
|
||||
}
|
||||
info.Put("type", clashType)
|
||||
info.Put("name", detour.Tag())
|
||||
@@ -92,10 +104,9 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||
} else {
|
||||
info.Put("history", []*urltest.History{})
|
||||
}
|
||||
if isGroup {
|
||||
selector := detour.(adapter.OutboundGroup)
|
||||
info.Put("now", selector.Now())
|
||||
info.Put("all", selector.All())
|
||||
if group, isGroup := detour.(adapter.OutboundGroup); isGroup {
|
||||
info.Put("now", group.Now())
|
||||
info.Put("all", group.All())
|
||||
}
|
||||
return &info
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
server.mode = "rule"
|
||||
}
|
||||
if options.StoreSelected {
|
||||
server.storeSelected = true
|
||||
cachePath := os.ExpandEnv(options.CacheFile)
|
||||
if cachePath == "" {
|
||||
cachePath = "cache.db"
|
||||
@@ -80,7 +81,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
chiRouter.Use(cors.Handler)
|
||||
chiRouter.Group(func(r chi.Router) {
|
||||
r.Use(authentication(options.Secret))
|
||||
r.Get("/", hello)
|
||||
r.Get("/", hello(options.ExternalUI != ""))
|
||||
r.Get("/logs", getLogs(logFactory))
|
||||
r.Get("/traffic", traffic(trafficManager))
|
||||
r.Get("/version", version)
|
||||
@@ -143,6 +144,10 @@ func (s *Server) CacheFile() adapter.ClashCacheFile {
|
||||
return s.cacheFile
|
||||
}
|
||||
|
||||
func (s *Server) HistoryStorage() *urltest.HistoryStorage {
|
||||
return s.urlTestHistory
|
||||
}
|
||||
|
||||
func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) {
|
||||
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule)
|
||||
return tracker, tracker
|
||||
@@ -232,8 +237,14 @@ func authentication(serverSecret string) func(next http.Handler) http.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
func hello(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, render.M{"hello": "clash"})
|
||||
func hello(redirect bool) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if redirect {
|
||||
http.Redirect(w, r, "/ui/", http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
render.JSON(w, r, render.M{"hello": "clash"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
|
||||
22
go.mod
22
go.mod
@@ -4,6 +4,8 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.11.8
|
||||
github.com/caddyserver/certmagic v0.17.1
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/database64128/tfo-go v1.1.2
|
||||
@@ -20,25 +22,23 @@ require (
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/refraction-networking/utls v1.1.2
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
|
||||
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220
|
||||
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12
|
||||
github.com/sagernet/sing-tun v0.0.0-20220925112147-6bad0c2380ca
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.uber.org/atomic v1.10.0
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0
|
||||
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab
|
||||
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7
|
||||
golang.org/x/net v0.0.0-20220923203811-8be639271d50
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
|
||||
google.golang.org/grpc v1.49.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||
|
||||
51
go.sum
51
go.sum
@@ -3,6 +3,8 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8=
|
||||
github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
@@ -10,6 +12,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/caddyserver/certmagic v0.17.1 h1:VrWANhQAj3brK7jAUKyN6XBHg56WsyorI/84Ilq1tCQ=
|
||||
github.com/caddyserver/certmagic v0.17.1/go.mod h1:pSS2aZcdKlrTZrb2DKuRafckx20o5Fz1EdDKEB8KOQM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
|
||||
@@ -91,8 +95,8 @@ github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
@@ -133,28 +137,24 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kxbgGa2Xp0H2CCsx1o2pVTt0f+hmfuHH4=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 h1:lQ4RFWY/wBYmXl/zJJCwQbhiEIbgEqC7j+nhZYkgwmU=
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0/go.mod h1:xSHLCsdgy5QIozXFEv6uDgMWzyrRdFFjr3TgL+juu6g=
|
||||
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-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
||||
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220 h1:fQk/BHOeHw5murjeNTdmkXmDy9cMlbubRINRH7GDuu4=
|
||||
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220/go.mod h1:5/u6RMDMoGIkSNtrZb41kJvyIFg3Ysn69P3WiAu8m0c=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b h1:cXCMNJ9heZ+c6l+qUcku60x9KyXo4SOAaJfg/6spOmU=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 h1:LsmPeFvj4GhiV5Y7Rm8I845XysdxVN4MQmfZ36P5bmw=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220925112147-6bad0c2380ca h1:Owgx9izFNYyMyUZ61td+mL3vumBhJz4zNismYlCyQbw=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220925112147-6bad0c2380ca/go.mod h1:ftP5VXlp3RJO5+WS3mPFk7jB9B/K/QrPEZX3BUORGQY=
|
||||
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/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=
|
||||
@@ -188,17 +188,16 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
|
||||
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
||||
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q=
|
||||
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 h1:WJywXQVIb56P2kAvXeMGTIgQ1ZHQxR60+F9dLsodECc=
|
||||
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||
@@ -230,8 +229,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI=
|
||||
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -239,8 +238,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -248,7 +247,6 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -266,9 +264,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -300,8 +297,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.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-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
|
||||
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=
|
||||
|
||||
@@ -141,13 +141,13 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
if !metadata.Source.IsValid() {
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||
}
|
||||
if !metadata.Destination.IsValid() {
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
}
|
||||
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr())
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr()).Unwrap()
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (a *myInboundAdapter) loopUDPIn() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||
if err != nil {
|
||||
@@ -86,7 +86,7 @@ func (a *myInboundAdapter) loopUDPOOBIn() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||
if err != nil {
|
||||
@@ -112,7 +112,7 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
|
||||
if err != nil {
|
||||
@@ -140,7 +140,7 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
|
||||
metadata.SniffEnabled = a.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr)
|
||||
metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap()
|
||||
metadata.OriginDestination = a.udpAddr
|
||||
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
|
||||
if err != nil {
|
||||
|
||||
@@ -261,9 +261,9 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
|
||||
metadata.SniffEnabled = h.listenOptions.SniffEnabled
|
||||
metadata.SniffOverrideDestination = h.listenOptions.SniffOverrideDestination
|
||||
metadata.DomainStrategy = dns.DomainStrategy(h.listenOptions.DomainStrategy)
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr())
|
||||
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port).Unwrap()
|
||||
|
||||
if !request.UDP {
|
||||
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
I "github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
return nil, I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -105,7 +106,7 @@ func (n *Naive) Start() error {
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
err := n.configureHTTP3Listener()
|
||||
if !C.QUIC_AVAILABLE && len(n.network) > 1 {
|
||||
if !include.WithQUIC && len(n.network) > 1 {
|
||||
log.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else if err != nil {
|
||||
return err
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
I "github.com/sagernet/sing-box/include"
|
||||
)
|
||||
|
||||
func (n *Naive) configureHTTP3Listener() error {
|
||||
return C.ErrQUICNotIncluded
|
||||
return I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func (t *TProxy) Start() error {
|
||||
}
|
||||
|
||||
func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
return t.newConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
@@ -84,14 +84,15 @@ func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.B
|
||||
if err != nil {
|
||||
return E.Cause(err, "get tproxy destination")
|
||||
}
|
||||
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
||||
metadata.Destination = M.SocksaddrFromNetIP(destination).Unwrap()
|
||||
t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
|
||||
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{source: natConn}
|
||||
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn, destination: metadata.Destination}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type tproxyPacketWriter struct {
|
||||
ctx context.Context
|
||||
source N.PacketConn
|
||||
destination M.Socksaddr
|
||||
conn *net.UDPConn
|
||||
@@ -99,25 +100,27 @@ type tproxyPacketWriter struct {
|
||||
|
||||
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
var udpConn *net.UDPConn
|
||||
if w.destination == destination && w.conn != nil {
|
||||
_, err := w.conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
|
||||
if err == nil {
|
||||
w.conn = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
var listener net.ListenConfig
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
listener.Control = control.Append(listener.Control, redir.TProxyWriteBack())
|
||||
packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
udpConn := packetConn.(*net.UDPConn)
|
||||
if w.destination == destination {
|
||||
if w.conn != nil {
|
||||
udpConn = w.conn
|
||||
}
|
||||
w.conn = udpConn
|
||||
} else {
|
||||
defer udpConn.Close()
|
||||
}
|
||||
if udpConn == nil {
|
||||
var err error
|
||||
udpConn, err = redir.DialUDP(destination.UDPAddr(), M.SocksaddrFromNet(w.source.LocalAddr()).UDPAddr())
|
||||
if err != nil {
|
||||
return E.Cause(err, "tproxy udp write back")
|
||||
}
|
||||
if w.destination == destination {
|
||||
w.conn = udpConn
|
||||
} else {
|
||||
defer udpConn.Close()
|
||||
}
|
||||
}
|
||||
return common.Error(udpConn.Write(buffer.Bytes()))
|
||||
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
|
||||
}
|
||||
|
||||
func (w *tproxyPacketWriter) Close() error {
|
||||
|
||||
@@ -190,7 +190,7 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata
|
||||
if err != nil {
|
||||
t.NewError(ctx, err)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error {
|
||||
@@ -212,7 +212,7 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre
|
||||
if err != nil {
|
||||
t.NewError(ctx, err)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tun) NewError(ctx context.Context, err error) {
|
||||
|
||||
7
include/quic.go
Normal file
7
include/quic.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build with_quic
|
||||
|
||||
package include
|
||||
|
||||
import _ "github.com/sagernet/sing-dns/quic"
|
||||
|
||||
const WithQUIC = true
|
||||
21
include/quic_stub.go
Normal file
21
include/quic_stub.go
Normal file
@@ -0,0 +1,21 @@
|
||||
//go:build !with_quic
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
const WithQUIC = false
|
||||
|
||||
var ErrQUICNotIncluded = E.New(`QUIC is not included in this build, rebuild with -tags with_quic`)
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"quic", "h3"}, func(ctx context.Context, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return nil, ErrQUICNotIncluded
|
||||
})
|
||||
}
|
||||
@@ -90,6 +90,7 @@ nav:
|
||||
- SSH: configuration/outbound/ssh.md
|
||||
- DNS: configuration/outbound/dns.md
|
||||
- Selector: configuration/outbound/selector.md
|
||||
- URLTest: configuration/outbound/urltest.md
|
||||
- FAQ:
|
||||
- faq/index.md
|
||||
- Known Issues: faq/known-issues.md
|
||||
|
||||
@@ -14,3 +14,10 @@ type SelectorOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
Default string `json:"default,omitempty"`
|
||||
}
|
||||
|
||||
type URLTestOutboundOptions struct {
|
||||
Outbounds []string `json:"outbounds"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Interval Duration `json:"interval,omitempty"`
|
||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ type _Outbound struct {
|
||||
ShadowsocksROptions ShadowsocksROutboundOptions `json:"-"`
|
||||
VLESSOptions VLESSOutboundOptions `json:"-"`
|
||||
SelectorOptions SelectorOutboundOptions `json:"-"`
|
||||
URLTestOptions URLTestOutboundOptions `json:"-"`
|
||||
}
|
||||
|
||||
type Outbound _Outbound
|
||||
@@ -61,6 +62,8 @@ func (h Outbound) MarshalJSON() ([]byte, error) {
|
||||
v = h.VLESSOptions
|
||||
case C.TypeSelector:
|
||||
v = h.SelectorOptions
|
||||
case C.TypeURLTest:
|
||||
v = h.URLTestOptions
|
||||
default:
|
||||
return nil, E.New("unknown outbound type: ", h.Type)
|
||||
}
|
||||
@@ -104,6 +107,8 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error {
|
||||
v = &h.VLESSOptions
|
||||
case C.TypeSelector:
|
||||
v = &h.SelectorOptions
|
||||
case C.TypeURLTest:
|
||||
v = &h.URLTestOptions
|
||||
default:
|
||||
return E.New("unknown outbound type: ", h.Type)
|
||||
}
|
||||
@@ -115,17 +120,18 @@ 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"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark int `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPFragment bool `json:"udp_fragment,omitempty"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
FallbackDelay Duration `json:"fallback_delay,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
BindAddress *ListenAddress `json:"bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark int `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
FallbackDelay Duration `json:"fallback_delay,omitempty"`
|
||||
}
|
||||
|
||||
type ServerOptions struct {
|
||||
|
||||
@@ -3,6 +3,7 @@ package option
|
||||
type InboundTLSOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
ALPN Listable[string] `json:"alpn,omitempty"`
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
|
||||
@@ -23,7 +23,7 @@ type VMessOutboundOptions struct {
|
||||
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
PacketAddr bool `json:"packet_addr,omitempty"`
|
||||
PacketEncoding string `json:"packet_encoding,omitempty"`
|
||||
Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
|
||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
|
||||
return NewVLESS(ctx, router, logger, options.Tag, options.VLESSOptions)
|
||||
case C.TypeSelector:
|
||||
return NewSelector(router, logger, options.Tag, options.SelectorOptions)
|
||||
case C.TypeURLTest:
|
||||
return NewURLTest(router, logger, options.Tag, options.URLTestOptions)
|
||||
default:
|
||||
return nil, E.New("unknown outbound type: ", options.Type)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type Direct struct {
|
||||
}
|
||||
|
||||
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
|
||||
options.UDPFragmentDefault = true
|
||||
outbound := &Direct{
|
||||
myOutboundAdapter: myOutboundAdapter{
|
||||
protocol: C.TypeDirect,
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*DNS)(nil)
|
||||
@@ -72,7 +72,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var message dnsmessage.Message
|
||||
var message mDNS.Msg
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -88,7 +88,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
responseBuffer := common.Dup(_responseBuffer)
|
||||
defer responseBuffer.Release()
|
||||
responseBuffer.Resize(2, 0)
|
||||
n, err := response.AppendPack(responseBuffer.Index(0))
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var message dnsmessage.Message
|
||||
var message mDNS.Msg
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -131,7 +131,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer := buf.NewPacket()
|
||||
n, err := response.AppendPack(responseBuffer.Index(0))
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
responseBuffer.Release()
|
||||
return err
|
||||
|
||||
@@ -38,6 +38,7 @@ type Hysteria struct {
|
||||
recvBPS uint64
|
||||
connAccess sync.Mutex
|
||||
conn quic.Connection
|
||||
rawConn net.Conn
|
||||
udpAccess sync.RWMutex
|
||||
udpSessions map[uint32]chan *hysteria.UDPMessage
|
||||
udpDefragger hysteria.Defragger
|
||||
@@ -149,7 +150,6 @@ func (h *Hysteria) offer(ctx context.Context) (quic.Connection, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.conn = conn
|
||||
if common.Contains(h.network, N.NetworkUDP) {
|
||||
for _, session := range h.udpSessions {
|
||||
close(session)
|
||||
@@ -201,6 +201,8 @@ func (h *Hysteria) offerNew(ctx context.Context) (quic.Connection, error) {
|
||||
return nil, E.New("remote error: ", serverHello.Message)
|
||||
}
|
||||
quicConn.SetCongestionControl(hysteria.NewBrutalSender(congestion.ByteCount(serverHello.RecvBPS)))
|
||||
h.conn = quicConn
|
||||
h.rawConn = udpConn
|
||||
return quicConn, nil
|
||||
}
|
||||
|
||||
@@ -240,6 +242,7 @@ func (h *Hysteria) Close() error {
|
||||
defer h.udpAccess.Unlock()
|
||||
if h.conn != nil {
|
||||
h.conn.CloseWithError(0, "")
|
||||
h.rawConn.Close()
|
||||
}
|
||||
for _, session := range h.udpSessions {
|
||||
close(session)
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
I "github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (adapter.Outbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
return nil, I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
@@ -4,41 +4,36 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/shadowsocksr"
|
||||
"github.com/sagernet/shadowsocksr/obfs"
|
||||
"github.com/sagernet/shadowsocksr/protocol"
|
||||
"github.com/sagernet/shadowsocksr/ssr"
|
||||
"github.com/sagernet/shadowsocksr/streamCipher"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowimpl"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing-box/transport/clashssr/obfs"
|
||||
"github.com/sagernet/sing-box/transport/clashssr/protocol"
|
||||
"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/Dreamacro/clash/transport/shadowsocks/core"
|
||||
"github.com/Dreamacro/clash/transport/shadowsocks/shadowstream"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*ShadowsocksR)(nil)
|
||||
|
||||
type ShadowsocksR struct {
|
||||
myOutboundAdapter
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
method shadowsocks.Method
|
||||
cipher string
|
||||
password string
|
||||
obfs string
|
||||
obfsParams *ssr.ServerInfo
|
||||
protocol string
|
||||
protocolParams *ssr.ServerInfo
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
cipher core.Cipher
|
||||
obfs obfs.Obfs
|
||||
protocol protocol.Protocol
|
||||
}
|
||||
|
||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (*ShadowsocksR, error) {
|
||||
@@ -52,40 +47,54 @@ func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.Cont
|
||||
},
|
||||
dialer: dialer.New(router, options.DialerOptions),
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
cipher: options.Method,
|
||||
password: options.Password,
|
||||
obfs: options.Obfs,
|
||||
protocol: options.Protocol,
|
||||
}
|
||||
var cipher string
|
||||
var err error
|
||||
outbound.method, err = shadowimpl.FetchMethod(options.Method, options.Password)
|
||||
switch options.Method {
|
||||
case "none":
|
||||
cipher = "dummy"
|
||||
default:
|
||||
cipher = options.Method
|
||||
}
|
||||
outbound.cipher, err = core.PickCipher(cipher, nil, options.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = streamCipher.NewStreamCipher(options.Method, options.Password); err != nil {
|
||||
return nil, E.New(strings.ToLower(err.Error()))
|
||||
var (
|
||||
ivSize int
|
||||
key []byte
|
||||
)
|
||||
if cipher == "dummy" {
|
||||
ivSize = 0
|
||||
key = core.Kdf(options.Password, 16)
|
||||
} else {
|
||||
streamCipher, ok := outbound.cipher.(*core.StreamCipher)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher)
|
||||
}
|
||||
ivSize = streamCipher.IVSize()
|
||||
key = streamCipher.Key
|
||||
}
|
||||
if obfs.NewObfs(options.Obfs) == nil {
|
||||
return nil, E.New("unknown obfs: " + options.Obfs)
|
||||
}
|
||||
outbound.obfsParams = &ssr.ServerInfo{
|
||||
Host: outbound.serverAddr.AddrString(),
|
||||
Port: outbound.serverAddr.Port,
|
||||
TcpMss: 1460,
|
||||
obfs, obfsOverhead, err := obfs.PickObfs(options.Obfs, &obfs.Base{
|
||||
Host: options.Server,
|
||||
Port: int(options.ServerPort),
|
||||
Key: key,
|
||||
IVSize: ivSize,
|
||||
Param: options.ObfsParam,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize obfs")
|
||||
}
|
||||
if protocol.NewProtocol(options.Protocol) == nil {
|
||||
return nil, E.New("unknown protocol: " + options.Protocol)
|
||||
}
|
||||
outbound.protocolParams = &ssr.ServerInfo{
|
||||
Host: outbound.serverAddr.AddrString(),
|
||||
Port: outbound.serverAddr.Port,
|
||||
TcpMss: 1460,
|
||||
Param: options.Protocol,
|
||||
}
|
||||
if outbound.method == nil {
|
||||
outbound.network = common.Filter(outbound.network, func(it string) bool { return it == N.NetworkTCP })
|
||||
protocol, err := protocol.PickProtocol(options.Protocol, &protocol.Base{
|
||||
Key: key,
|
||||
Overhead: obfsOverhead,
|
||||
Param: options.ProtocolParam,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize protocol")
|
||||
}
|
||||
outbound.obfs = obfs
|
||||
outbound.protocol = protocol
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
@@ -97,20 +106,17 @@ func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destinat
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipher, err := streamCipher.NewStreamCipher(h.cipher, h.password)
|
||||
conn = h.cipher.StreamConn(h.obfs.StreamConn(conn))
|
||||
writeIv, err := conn.(*shadowstream.Conn).ObtainWriteIV()
|
||||
if err != nil {
|
||||
return nil, E.New(strings.ToLower(err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
ssConn := shadowsocksr.NewSSTCPConn(conn, cipher)
|
||||
ssConn.IObfs = obfs.NewObfs(h.obfs)
|
||||
ssConn.IObfs.SetServerInfo(h.obfsParams)
|
||||
ssConn.IProtocol = protocol.NewProtocol(h.protocol)
|
||||
ssConn.IProtocol.SetServerInfo(h.protocolParams)
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(ssConn, destination)
|
||||
conn = h.protocol.StreamConn(conn, writeIv)
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "write request")
|
||||
}
|
||||
return ssConn, nil
|
||||
return conn, nil
|
||||
case N.NetworkUDP:
|
||||
conn, err := h.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
@@ -128,7 +134,10 @@ func (h *ShadowsocksR) ListenPacket(ctx context.Context, destination M.Socksaddr
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.method.DialPacketConn(outConn), nil
|
||||
packetConn := h.cipher.PacketConn(bufio.NewUnbindPacketConn(outConn))
|
||||
packetConn = h.protocol.PacketConn(packetConn)
|
||||
packetConn = &ssPacketConn{packetConn, outConn.RemoteAddr()}
|
||||
return packetConn, nil
|
||||
}
|
||||
|
||||
func (h *ShadowsocksR) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
@@ -138,3 +147,36 @@ func (h *ShadowsocksR) NewConnection(ctx context.Context, conn net.Conn, metadat
|
||||
func (h *ShadowsocksR) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
type ssPacketConn struct {
|
||||
net.PacketConn
|
||||
rAddr net.Addr
|
||||
}
|
||||
|
||||
func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return spc.PacketConn.WriteTo(packet[3:], spc.rAddr)
|
||||
}
|
||||
|
||||
func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
n, _, e := spc.PacketConn.ReadFrom(b)
|
||||
if e != nil {
|
||||
return 0, nil, e
|
||||
}
|
||||
|
||||
addr := socks5.SplitAddr(b[:n])
|
||||
if addr == nil {
|
||||
return 0, nil, errors.New("parse addr error")
|
||||
}
|
||||
|
||||
udpAddr := addr.UDPAddr()
|
||||
if udpAddr == nil {
|
||||
return 0, nil, errors.New("parse addr error")
|
||||
}
|
||||
|
||||
copy(b, b[len(addr):])
|
||||
return n - len(addr), udpAddr, e
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
if destination.IsFqdn() {
|
||||
if h.resolve && destination.IsFqdn() {
|
||||
addrs, err := h.router.LookupDefault(ctx, destination.Fqdn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -103,6 +103,10 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Trojan) Close() error {
|
||||
return common.Close(h.multiplexDialer, h.transport)
|
||||
}
|
||||
|
||||
type trojanDialer Trojan
|
||||
|
||||
func (h *trojanDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
|
||||
287
outbound/urltest.go
Normal file
287
outbound/urltest.go
Normal file
@@ -0,0 +1,287 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Outbound = (*URLTest)(nil)
|
||||
_ adapter.OutboundGroup = (*URLTest)(nil)
|
||||
)
|
||||
|
||||
type URLTest struct {
|
||||
myOutboundAdapter
|
||||
tags []string
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
group *URLTestGroup
|
||||
}
|
||||
|
||||
func NewURLTest(router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
||||
outbound := &URLTest{
|
||||
myOutboundAdapter: myOutboundAdapter{
|
||||
protocol: C.TypeURLTest,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
},
|
||||
tags: options.Outbounds,
|
||||
link: options.URL,
|
||||
interval: time.Duration(options.Interval),
|
||||
tolerance: options.Tolerance,
|
||||
}
|
||||
if len(outbound.tags) == 0 {
|
||||
return nil, E.New("missing tags")
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (s *URLTest) Network() []string {
|
||||
if s.group == nil {
|
||||
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||
}
|
||||
return s.group.Select(N.NetworkTCP).Network()
|
||||
}
|
||||
|
||||
func (s *URLTest) Start() error {
|
||||
outbounds := make([]adapter.Outbound, 0, len(s.tags))
|
||||
for i, tag := range s.tags {
|
||||
detour, loaded := s.router.Outbound(tag)
|
||||
if !loaded {
|
||||
return E.New("outbound ", i, " not found: ", tag)
|
||||
}
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
s.group = NewURLTestGroup(s.router, s.logger, outbounds, s.link, s.interval, s.tolerance)
|
||||
return s.group.Start()
|
||||
}
|
||||
|
||||
func (s URLTest) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.group),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *URLTest) Now() string {
|
||||
return s.group.Select(N.NetworkTCP).Tag()
|
||||
}
|
||||
|
||||
func (s *URLTest) All() []string {
|
||||
return s.tags
|
||||
}
|
||||
|
||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
outbound := s.group.Select(network)
|
||||
conn, err := outbound.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
s.logger.ErrorContext(ctx, err)
|
||||
go s.group.checkOutbounds()
|
||||
outbounds := s.group.Fallback(outbound)
|
||||
for _, fallback := range outbounds {
|
||||
conn, err = fallback.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
outbound := s.group.Select(N.NetworkUDP)
|
||||
conn, err := outbound.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
s.logger.ErrorContext(ctx, err)
|
||||
go s.group.checkOutbounds()
|
||||
outbounds := s.group.Fallback(outbound)
|
||||
for _, fallback := range outbounds {
|
||||
conn, err = fallback.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
type URLTestGroup struct {
|
||||
router adapter.Router
|
||||
logger log.Logger
|
||||
outbounds []adapter.Outbound
|
||||
link string
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
history *urltest.HistoryStorage
|
||||
|
||||
ticker *time.Ticker
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
func NewURLTestGroup(router adapter.Router, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16) *URLTestGroup {
|
||||
if link == "" {
|
||||
//goland:noinspection HttpUrlsUsage
|
||||
link = "http://www.gstatic.com/generate_204"
|
||||
}
|
||||
if interval == 0 {
|
||||
interval = C.DefaultURLTestInterval
|
||||
}
|
||||
if tolerance == 0 {
|
||||
tolerance = 50
|
||||
}
|
||||
var history *urltest.HistoryStorage
|
||||
if clashServer := router.ClashServer(); clashServer != nil {
|
||||
history = clashServer.HistoryStorage()
|
||||
} else {
|
||||
history = urltest.NewHistoryStorage()
|
||||
}
|
||||
return &URLTestGroup{
|
||||
router: router,
|
||||
logger: logger,
|
||||
outbounds: outbounds,
|
||||
link: link,
|
||||
interval: interval,
|
||||
tolerance: tolerance,
|
||||
history: history,
|
||||
close: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Start() error {
|
||||
g.ticker = time.NewTicker(g.interval)
|
||||
go g.loopCheck()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Close() error {
|
||||
g.ticker.Stop()
|
||||
close(g.close)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Select(network string) adapter.Outbound {
|
||||
var minDelay uint16
|
||||
var minTime time.Time
|
||||
var minOutbound adapter.Outbound
|
||||
for _, detour := range g.outbounds {
|
||||
if !common.Contains(detour.Network(), network) {
|
||||
continue
|
||||
}
|
||||
history := g.history.LoadURLTestHistory(RealTag(detour))
|
||||
if history == nil {
|
||||
continue
|
||||
}
|
||||
if minDelay == 0 || minDelay > history.Delay+g.tolerance || minDelay > history.Delay-g.tolerance && minTime.Before(history.Time) {
|
||||
minDelay = history.Delay
|
||||
minTime = history.Time
|
||||
minOutbound = detour
|
||||
}
|
||||
}
|
||||
if minOutbound == nil {
|
||||
for _, detour := range g.outbounds {
|
||||
if !common.Contains(detour.Network(), network) {
|
||||
continue
|
||||
}
|
||||
minOutbound = detour
|
||||
break
|
||||
}
|
||||
}
|
||||
return minOutbound
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
|
||||
outbounds := make([]adapter.Outbound, 0, len(g.outbounds)-1)
|
||||
for _, detour := range g.outbounds {
|
||||
if detour != used {
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
}
|
||||
sort.Slice(outbounds, func(i, j int) bool {
|
||||
oi := outbounds[i]
|
||||
oj := outbounds[j]
|
||||
hi := g.history.LoadURLTestHistory(RealTag(oi))
|
||||
if hi == nil {
|
||||
return false
|
||||
}
|
||||
hj := g.history.LoadURLTestHistory(RealTag(oj))
|
||||
if hj == nil {
|
||||
return false
|
||||
}
|
||||
return hi.Delay < hj.Delay
|
||||
})
|
||||
return outbounds
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) loopCheck() {
|
||||
go g.checkOutbounds()
|
||||
for {
|
||||
select {
|
||||
case <-g.close:
|
||||
return
|
||||
case <-g.ticker.C:
|
||||
g.checkOutbounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) checkOutbounds() {
|
||||
b, _ := batch.New(context.Background(), batch.WithConcurrencyNum[any](10))
|
||||
checked := make(map[string]bool)
|
||||
for _, detour := range g.outbounds {
|
||||
tag := detour.Tag()
|
||||
realTag := RealTag(detour)
|
||||
if checked[realTag] {
|
||||
continue
|
||||
}
|
||||
history := g.history.LoadURLTestHistory(realTag)
|
||||
if history != nil && time.Now().Sub(history.Time) < g.interval {
|
||||
continue
|
||||
}
|
||||
checked[realTag] = true
|
||||
p, loaded := g.router.Outbound(realTag)
|
||||
if !loaded {
|
||||
continue
|
||||
}
|
||||
b.Go(realTag, func() (any, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
|
||||
defer cancel()
|
||||
t, err := urltest.URLTest(ctx, g.link, p)
|
||||
if err != nil {
|
||||
g.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||
g.history.DeleteURLTestHistory(realTag)
|
||||
} else {
|
||||
g.logger.Debug("outbound ", tag, " available: ", t, "ms")
|
||||
g.history.StoreURLTestHistory(realTag, &urltest.History{
|
||||
Time: time.Now(),
|
||||
Delay: t,
|
||||
})
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
b.Wait()
|
||||
}
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-box/transport/vless"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing-vmess/vless"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -73,10 +74,6 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
return common.Close(h.transport)
|
||||
}
|
||||
|
||||
func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
@@ -104,7 +101,7 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S
|
||||
return h.client.DialEarlyConn(conn, destination), nil
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
return h.client.DialPacketConn(conn, destination), nil
|
||||
return h.client.DialEarlyPacketConn(conn, destination), nil
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
@@ -129,11 +126,11 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
|
||||
return nil, err
|
||||
}
|
||||
if h.xudp {
|
||||
return h.client.DialXUDPPacketConn(conn, destination), nil
|
||||
return h.client.DialEarlyXUDPPacketConn(conn, destination), nil
|
||||
} else if h.packetAddr {
|
||||
return packetaddr.NewConn(h.client.DialPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
|
||||
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil
|
||||
} else {
|
||||
return h.client.DialPacketConn(conn, destination), nil
|
||||
return h.client.DialEarlyPacketConn(conn, destination), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,3 +141,7 @@ func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
|
||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
return common.Close(h.transport)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -31,6 +32,7 @@ type VMess struct {
|
||||
tlsConfig tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
}
|
||||
|
||||
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
|
||||
@@ -62,8 +64,14 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if outbound.multiplexDialer == nil && options.PacketAddr {
|
||||
switch options.PacketEncoding {
|
||||
case "":
|
||||
case "packetaddr":
|
||||
outbound.packetAddr = true
|
||||
case "xudp":
|
||||
outbound.xudp = true
|
||||
default:
|
||||
return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
|
||||
}
|
||||
var clientOptions []vmess.ClientOption
|
||||
if options.GlobalPadding {
|
||||
@@ -88,7 +96,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
|
||||
func (h *VMess) Close() error {
|
||||
return common.Close(h.transport)
|
||||
return common.Close(h.multiplexDialer, h.transport)
|
||||
}
|
||||
|
||||
func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
@@ -176,7 +184,9 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
return nil, err
|
||||
}
|
||||
if h.packetAddr {
|
||||
return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil
|
||||
return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil
|
||||
} else if h.xudp {
|
||||
return h.client.DialEarlyXUDPPacketConn(conn, destination), nil
|
||||
} else {
|
||||
return h.client.DialEarlyPacketConn(conn, destination), nil
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/wireguard"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -98,7 +99,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
}
|
||||
var wireTunDevice wireguard.Device
|
||||
var err error
|
||||
if !options.SystemInterface {
|
||||
if !options.SystemInterface && tun.WithGVisor {
|
||||
wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu)
|
||||
} else {
|
||||
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, localPrefixes, mtu)
|
||||
@@ -161,9 +162,8 @@ func (w *WireGuard) Start() error {
|
||||
}
|
||||
|
||||
func (w *WireGuard) Close() error {
|
||||
return common.Close(
|
||||
w.tunDevice,
|
||||
common.PtrOrNil(w.device),
|
||||
common.PtrOrNil(w.bind),
|
||||
)
|
||||
if w.device != nil {
|
||||
w.device.Close()
|
||||
}
|
||||
return common.Close(w.tunDevice)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pushd $PROJECT
|
||||
git fetch
|
||||
git reset FETCH_HEAD --hard
|
||||
git clean -fdx
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_acme,debug ./cmd/sing-box
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_acme,debug ./cmd/sing-box
|
||||
popd
|
||||
|
||||
sudo systemctl stop sing-box
|
||||
|
||||
@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
|
||||
PROJECT=$DIR/../..
|
||||
|
||||
pushd $PROJECT
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
popd
|
||||
|
||||
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.19.linux-amd64.tar.gz
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.19.1.linux-amd64.tar.gz
|
||||
sudo rm -rf /usr/local/go
|
||||
sudo tar -C /usr/local -xzf go.tar.gz
|
||||
rm go.tar.gz
|
||||
@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
|
||||
PROJECT=$DIR/../..
|
||||
|
||||
pushd $PROJECT
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
popd
|
||||
|
||||
sudo systemctl stop sing-box
|
||||
|
||||
50
route/interface_finder.go
Normal file
50
route/interface_finder.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
var _ control.InterfaceFinder = (*myInterfaceFinder)(nil)
|
||||
|
||||
type myInterfaceFinder struct {
|
||||
ifs []net.Interface
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) update() error {
|
||||
ifs, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ifs = ifs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
if netInterface.Name == name {
|
||||
return netInterface.Index, nil
|
||||
}
|
||||
}
|
||||
netInterface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.update()
|
||||
return netInterface.Index, nil
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceNameByIndex(index int) (interfaceName string, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
if netInterface.Index == index {
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
}
|
||||
netInterface, err := net.InterfaceByIndex(index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.update()
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
@@ -86,7 +86,7 @@ type Router struct {
|
||||
transports []dns.Transport
|
||||
transportMap map[string]dns.Transport
|
||||
transportDomainStrategy map[dns.Transport]dns.DomainStrategy
|
||||
interfaceBindManager control.BindManager
|
||||
interfaceFinder myInterfaceFinder
|
||||
autoDetectInterface bool
|
||||
defaultInterface string
|
||||
defaultMark int
|
||||
@@ -123,7 +123,6 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
defaultDetour: options.Final,
|
||||
dnsClient: dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire),
|
||||
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
|
||||
interfaceBindManager: control.NewBindManager(),
|
||||
autoDetectInterface: options.AutoDetectInterface,
|
||||
defaultInterface: options.DefaultInterface,
|
||||
defaultMark: options.DefaultMark,
|
||||
@@ -196,7 +195,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.NewTransport(ctx, detour, server.Address)
|
||||
transport, err := dns.CreateTransport(ctx, detour, server.Address)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||
}
|
||||
@@ -233,7 +232,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
}
|
||||
if defaultTransport == nil {
|
||||
if len(transports) == 0 {
|
||||
transports = append(transports, dns.NewLocalTransport())
|
||||
transports = append(transports, &dns.LocalTransport{})
|
||||
}
|
||||
defaultTransport = transports[0]
|
||||
}
|
||||
@@ -247,13 +246,11 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || C.IsAndroid && inbound.TunOptions.AutoRoute
|
||||
})
|
||||
|
||||
if router.interfaceBindManager != nil || needInterfaceMonitor {
|
||||
if needInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err == nil {
|
||||
router.networkMonitor = networkMonitor
|
||||
if router.interfaceBindManager != nil {
|
||||
networkMonitor.RegisterCallback(router.interfaceBindManager.Update)
|
||||
}
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,7 +554,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
if err == nil {
|
||||
metadata.Protocol = sniffMetadata.Protocol
|
||||
metadata.Domain = sniffMetadata.Domain
|
||||
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
|
||||
if metadata.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
|
||||
metadata.Destination = M.Socksaddr{
|
||||
Fqdn: metadata.Domain,
|
||||
Port: metadata.Destination.Port,
|
||||
@@ -634,7 +631,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
if err == nil {
|
||||
metadata.Protocol = sniffMetadata.Protocol
|
||||
metadata.Domain = sniffMetadata.Domain
|
||||
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
|
||||
if metadata.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
|
||||
metadata.Destination = M.Socksaddr{
|
||||
Fqdn: metadata.Domain,
|
||||
Port: metadata.Destination.Port,
|
||||
@@ -714,8 +711,8 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
|
||||
return nil, defaultOutbound
|
||||
}
|
||||
|
||||
func (r *Router) InterfaceBindManager() control.BindManager {
|
||||
return r.interfaceBindManager
|
||||
func (r *Router) InterfaceFinder() control.InterfaceFinder {
|
||||
return &r.interfaceFinder
|
||||
}
|
||||
|
||||
func (r *Router) AutoDetectInterface() bool {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport, dns.DomainStrategy) {
|
||||
@@ -40,29 +40,29 @@ func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport,
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy
|
||||
}
|
||||
|
||||
func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) {
|
||||
if len(message.Questions) > 0 {
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatDNSQuestion(message.Questions[0]))
|
||||
func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if len(message.Question) > 0 {
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
||||
}
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
if len(message.Questions) > 0 {
|
||||
switch message.Questions[0].Type {
|
||||
case dnsmessage.TypeA:
|
||||
if len(message.Question) > 0 {
|
||||
switch message.Question[0].Qtype {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case dnsmessage.TypeAAAA:
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
metadata.Domain = string(message.Questions[0].Name.Data[:message.Questions[0].Name.Length-1])
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
ctx, transport, strategy := r.matchDNS(ctx)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
response, err := r.dnsClient.Exchange(ctx, transport, message, strategy)
|
||||
if err != nil && len(message.Questions) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", message.Questions[0].Name.String()))
|
||||
if err != nil && len(message.Question) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||
}
|
||||
if len(message.Questions) > 0 && response != nil {
|
||||
LogDNSAnswers(r.dnsLogger, ctx, message.Questions[0].Name.String(), response.Answers)
|
||||
if len(message.Question) > 0 && response != nil {
|
||||
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
@@ -93,61 +93,26 @@ func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr
|
||||
return r.Lookup(ctx, domain, dns.DomainStrategyAsIS)
|
||||
}
|
||||
|
||||
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []dnsmessage.Resource) {
|
||||
for _, rawAnswer := range answers {
|
||||
var content string
|
||||
switch answer := rawAnswer.Body.(type) {
|
||||
case *dnsmessage.AResource:
|
||||
content = netip.AddrFrom4(answer.A).String()
|
||||
case *dnsmessage.NSResource:
|
||||
content = answer.NS.String()
|
||||
case *dnsmessage.CNAMEResource:
|
||||
content = answer.CNAME.String()
|
||||
case *dnsmessage.SOAResource:
|
||||
content = answer.MBox.String()
|
||||
case *dnsmessage.PTRResource:
|
||||
content = answer.PTR.String()
|
||||
case *dnsmessage.MXResource:
|
||||
content = answer.MX.String()
|
||||
case *dnsmessage.TXTResource:
|
||||
content = strings.Join(answer.TXT, " ")
|
||||
case *dnsmessage.AAAAResource:
|
||||
content = netip.AddrFrom16(answer.AAAA).String()
|
||||
case *dnsmessage.SRVResource:
|
||||
content = answer.Target.String()
|
||||
case *dnsmessage.UnknownResource:
|
||||
content = answer.Type.String()
|
||||
default:
|
||||
continue
|
||||
}
|
||||
rType := formatDNSType(rawAnswer.Header.Type)
|
||||
if rType == "" {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", rType)
|
||||
} else {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", rType, " ", content)
|
||||
}
|
||||
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []mDNS.RR) {
|
||||
for _, answer := range answers {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", mDNS.Type(answer.Header().Rrtype).String(), " ", formatQuestion(answer.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func formatDNSQuestion(question dnsmessage.Question) string {
|
||||
var qType string
|
||||
qType = question.Type.String()
|
||||
if len(qType) > 4 {
|
||||
qType = qType[4:]
|
||||
func fqdnToDomain(fqdn string) string {
|
||||
if mDNS.IsFqdn(fqdn) {
|
||||
return fqdn[:len(fqdn)-1]
|
||||
}
|
||||
var qClass string
|
||||
qClass = question.Class.String()
|
||||
if len(qClass) > 5 {
|
||||
qClass = qClass[5:]
|
||||
}
|
||||
return string(question.Name.Data[:question.Name.Length-1]) + " " + qType + " " + qClass
|
||||
return fqdn
|
||||
}
|
||||
|
||||
func formatDNSType(qType dnsmessage.Type) string {
|
||||
qTypeName := qType.String()
|
||||
if len(qTypeName) > 4 {
|
||||
return qTypeName[4:]
|
||||
} else {
|
||||
return F.ToString("unknown (type ", qTypeName, ")")
|
||||
func formatQuestion(string string) string {
|
||||
if strings.HasPrefix(string, ";") {
|
||||
string = string[1:]
|
||||
}
|
||||
string = strings.ReplaceAll(string, "\t", " ")
|
||||
for strings.Contains(string, " ") {
|
||||
string = strings.ReplaceAll(string, " ", " ")
|
||||
}
|
||||
return string
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ var _ adapter.Rule = (*DefaultRule)(nil)
|
||||
type DefaultRule struct {
|
||||
items []RuleItem
|
||||
sourceAddressItems []RuleItem
|
||||
sourcePortItems []RuleItem
|
||||
destinationAddressItems []RuleItem
|
||||
destinationPortItems []RuleItem
|
||||
allItems []RuleItem
|
||||
invert bool
|
||||
outbound string
|
||||
@@ -143,7 +145,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
item := NewPortItem(true, options.SourcePort)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePortRange) > 0 {
|
||||
@@ -151,12 +153,12 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.Port) > 0 {
|
||||
item := NewPortItem(false, options.Port)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.PortRange) > 0 {
|
||||
@@ -164,7 +166,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.ProcessName) > 0 {
|
||||
@@ -256,6 +258,19 @@ func (r *DefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.sourcePortItems) > 0 {
|
||||
var sourcePortMatch bool
|
||||
for _, item := range r.sourcePortItems {
|
||||
if item.Match(metadata) {
|
||||
sourcePortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !sourcePortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 {
|
||||
var destinationAddressMatch bool
|
||||
for _, item := range r.destinationAddressItems {
|
||||
@@ -269,6 +284,19 @@ func (r *DefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationPortItems) > 0 {
|
||||
var destinationPortMatch bool
|
||||
for _, item := range r.destinationPortItems {
|
||||
if item.Match(metadata) {
|
||||
destinationPortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destinationPortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
return !r.invert
|
||||
}
|
||||
|
||||
|
||||
@@ -59,13 +59,13 @@ func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
|
||||
|
||||
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
|
||||
if r.isSource {
|
||||
return r.match(metadata.Source.Addr)
|
||||
return r.ipSet.Contains(metadata.Source.Addr)
|
||||
} else {
|
||||
if metadata.Destination.IsIP() {
|
||||
return r.match(metadata.Destination.Addr)
|
||||
return r.ipSet.Contains(metadata.Destination.Addr)
|
||||
} else {
|
||||
for _, address := range metadata.DestinationAddresses {
|
||||
if r.match(address) {
|
||||
if r.ipSet.Contains(address) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -74,14 +74,6 @@ func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *IPCIDRItem) match(address netip.Addr) bool {
|
||||
if address.Is4In6() {
|
||||
return r.ipSet.Contains(netip.AddrFrom4(address.As4()))
|
||||
} else {
|
||||
return r.ipSet.Contains(address)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *IPCIDRItem) String() string {
|
||||
return r.description
|
||||
}
|
||||
|
||||
@@ -39,12 +39,15 @@ func NewDNSRule(router adapter.Router, logger log.ContextLogger, options option.
|
||||
var _ adapter.DNSRule = (*DefaultDNSRule)(nil)
|
||||
|
||||
type DefaultDNSRule struct {
|
||||
items []RuleItem
|
||||
addressItems []RuleItem
|
||||
allItems []RuleItem
|
||||
invert bool
|
||||
outbound string
|
||||
disableCache bool
|
||||
items []RuleItem
|
||||
sourceAddressItems []RuleItem
|
||||
sourcePortItems []RuleItem
|
||||
destinationAddressItems []RuleItem
|
||||
destinationPortItems []RuleItem
|
||||
allItems []RuleItem
|
||||
invert bool
|
||||
outbound string
|
||||
disableCache bool
|
||||
}
|
||||
|
||||
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
||||
@@ -90,12 +93,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
}
|
||||
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
||||
item := NewDomainItem(options.Domain, options.DomainSuffix)
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DomainKeyword) > 0 {
|
||||
item := NewDomainKeywordItem(options.DomainKeyword)
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DomainRegex) > 0 {
|
||||
@@ -103,17 +106,17 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "domain_regex")
|
||||
}
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.Geosite) > 0 {
|
||||
item := NewGeositeItem(router, logger, options.Geosite)
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceGeoIP) > 0 {
|
||||
item := NewGeoIPItem(router, logger, true, options.SourceGeoIP)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
@@ -121,12 +124,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_ipcidr")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
item := NewPortItem(true, options.SourcePort)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePortRange) > 0 {
|
||||
@@ -134,12 +137,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.Port) > 0 {
|
||||
item := NewPortItem(false, options.Port)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.PortRange) > 0 {
|
||||
@@ -147,7 +150,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.ProcessName) > 0 {
|
||||
@@ -230,18 +233,59 @@ func (r *DefaultDNSRule) Match(metadata *adapter.InboundContext) bool {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
if len(r.addressItems) > 0 {
|
||||
var addressMatch bool
|
||||
for _, item := range r.addressItems {
|
||||
|
||||
if len(r.sourceAddressItems) > 0 {
|
||||
var sourceAddressMatch bool
|
||||
for _, item := range r.sourceAddressItems {
|
||||
if item.Match(metadata) {
|
||||
addressMatch = true
|
||||
sourceAddressMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !addressMatch {
|
||||
if !sourceAddressMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.sourcePortItems) > 0 {
|
||||
var sourcePortMatch bool
|
||||
for _, item := range r.sourcePortItems {
|
||||
if item.Match(metadata) {
|
||||
sourcePortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !sourcePortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 {
|
||||
var destinationAddressMatch bool
|
||||
for _, item := range r.destinationAddressItems {
|
||||
if item.Match(metadata) {
|
||||
destinationAddressMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destinationAddressMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationPortItems) > 0 {
|
||||
var destinationPortMatch bool
|
||||
for _, item := range r.destinationPortItems {
|
||||
if item.Match(metadata) {
|
||||
destinationPortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destinationPortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
return !r.invert
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,14 @@ import (
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func startInstance(t *testing.T, options option.Options) {
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
|
||||
func startInstance(t *testing.T, options option.Options) *box.Box {
|
||||
if debug.Enabled {
|
||||
options.Log = &option.LogOptions{
|
||||
Level: "trace",
|
||||
@@ -27,10 +32,12 @@ func startInstance(t *testing.T, options option.Options) {
|
||||
Level: "warning",
|
||||
}
|
||||
}
|
||||
// ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var instance *box.Box
|
||||
var err error
|
||||
for retry := 0; retry < 3; retry++ {
|
||||
instance, err = box.New(context.Background(), options)
|
||||
instance, err = box.New(ctx, options)
|
||||
require.NoError(t, err)
|
||||
err = instance.Start()
|
||||
if err != nil {
|
||||
@@ -42,7 +49,9 @@ func startInstance(t *testing.T, options option.Options) {
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
instance.Close()
|
||||
cancel()
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
func testSuit(t *testing.T, clientPort uint16, testPort uint16) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"net/netip"
|
||||
"sync"
|
||||
@@ -39,6 +38,7 @@ const (
|
||||
ImageShadowTLS = "ghcr.io/ihciah/shadow-tls:latest"
|
||||
ImageShadowsocksR = "teddysun/shadowsocks-r:latest"
|
||||
ImageXRayCore = "teddysun/xray:latest"
|
||||
ImageShadowsocksLegacy = "mritd/shadowsocks:latest"
|
||||
)
|
||||
|
||||
var allImages = []string{
|
||||
@@ -53,6 +53,7 @@ var allImages = []string{
|
||||
ImageShadowTLS,
|
||||
ImageShadowsocksR,
|
||||
ImageXRayCore,
|
||||
ImageShadowsocksLegacy,
|
||||
}
|
||||
|
||||
var localIP = netip.MustParseAddr("127.0.0.1")
|
||||
@@ -101,12 +102,6 @@ func init() {
|
||||
|
||||
io.Copy(io.Discard, imageStream)
|
||||
}
|
||||
go func() {
|
||||
err = http.ListenAndServe("0.0.0.0:8965", nil)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func newPingPongPair() (chan []byte, chan []byte, func(t *testing.T) error) {
|
||||
|
||||
41
test/config/vmess-mux-client.json
Normal file
41
test/config/vmess-mux-client.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"listen": "127.0.0.1",
|
||||
"port": "1080",
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "noauth",
|
||||
"udp": true,
|
||||
"ip": "127.0.0.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"port": 1234,
|
||||
"users": [
|
||||
{
|
||||
"id": "",
|
||||
"alterId": 0,
|
||||
"security": "none",
|
||||
"experiments": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"mux": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
21
test/go.mod
21
test/go.mod
@@ -10,10 +10,11 @@ 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/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
|
||||
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
go.uber.org/goleak v1.2.0
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
|
||||
)
|
||||
|
||||
@@ -21,9 +22,11 @@ require (
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.11.8 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/caddyserver/certmagic v0.17.1 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc // indirect
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/database64128/tfo-go v1.1.2 // indirect
|
||||
@@ -61,32 +64,30 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/refraction-networking/utls v1.1.2 // indirect
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a // 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-20220818150011-de611ab3e2bb // indirect
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 // indirect
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 // indirect
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // 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
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.22.0 // indirect
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d // indirect
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 // 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.49.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
|
||||
51
test/go.sum
51
test/go.sum
@@ -5,6 +5,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
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/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=
|
||||
@@ -14,6 +16,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/caddyserver/certmagic v0.17.1 h1:VrWANhQAj3brK7jAUKyN6XBHg56WsyorI/84Ilq1tCQ=
|
||||
github.com/caddyserver/certmagic v0.17.1/go.mod h1:pSS2aZcdKlrTZrb2DKuRafckx20o5Fz1EdDKEB8KOQM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
|
||||
@@ -103,8 +107,8 @@ github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
@@ -153,35 +157,31 @@ github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tS
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kxbgGa2Xp0H2CCsx1o2pVTt0f+hmfuHH4=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 h1:lQ4RFWY/wBYmXl/zJJCwQbhiEIbgEqC7j+nhZYkgwmU=
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0/go.mod h1:xSHLCsdgy5QIozXFEv6uDgMWzyrRdFFjr3TgL+juu6g=
|
||||
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-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
||||
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f h1:GX416thAwyc0vHBOal/qplvdhFgYO2dHD5GqADCJ0Ig=
|
||||
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b h1:cXCMNJ9heZ+c6l+qUcku60x9KyXo4SOAaJfg/6spOmU=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 h1:LsmPeFvj4GhiV5Y7Rm8I845XysdxVN4MQmfZ36P5bmw=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 h1:DOQQXQbB2gq4n2FuMHrL07HRs2naCCsuiu/9l1JFb9A=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 h1:w7JlEa4xHXuuPTL3Opb+Z5f/l66SYi54rSfobzuxSF8=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9/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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/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=
|
||||
@@ -205,8 +205,9 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
@@ -218,11 +219,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||
@@ -230,6 +230,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -266,8 +267,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -295,10 +296,10 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc h1:dpclq5m2YrqPGStKmtw7IcNbKLfbIqKXvNxDJKdIKYc=
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -334,8 +335,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.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-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
|
||||
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=
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestHysteriaInbound(t *testing.T) {
|
||||
caPem: "/etc/hysteria/ca.pem",
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testSuitSimple1(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestHysteriaOutbound(t *testing.T) {
|
||||
@@ -162,5 +162,5 @@ func TestHysteriaOutbound(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testSuitSimple1(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
170
test/mux_cool_test.go
Normal file
170
test/mux_cool_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMuxCoolServer(t *testing.T) {
|
||||
userId := newUUID()
|
||||
content, err := os.ReadFile("config/vmess-mux-client.json")
|
||||
require.NoError(t, err)
|
||||
config, err := ajson.Unmarshal(content)
|
||||
require.NoError(t, err)
|
||||
|
||||
config.MustKey("inbounds").MustIndex(0).MustKey("port").SetNumeric(float64(clientPort))
|
||||
outbound := config.MustKey("outbounds").MustIndex(0).MustKey("settings").MustKey("vnext").MustIndex(0)
|
||||
outbound.MustKey("port").SetNumeric(float64(serverPort))
|
||||
user := outbound.MustKey("users").MustIndex(0)
|
||||
user.MustKey("id").SetString(userId.String())
|
||||
|
||||
content, err = ajson.Marshal(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
startDockerContainer(t, DockerOptions{
|
||||
Image: ImageV2RayCore,
|
||||
Ports: []uint16{serverPort, testPort},
|
||||
EntryPoint: "v2ray",
|
||||
Stdin: content,
|
||||
})
|
||||
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeVMess,
|
||||
VMessOptions: option.VMessInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.VMessUser{
|
||||
{
|
||||
Name: "sekai",
|
||||
UUID: userId.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestMuxCoolClient(t *testing.T) {
|
||||
user := newUUID()
|
||||
content, err := os.ReadFile("config/vmess-server.json")
|
||||
require.NoError(t, err)
|
||||
config, err := ajson.Unmarshal(content)
|
||||
require.NoError(t, err)
|
||||
|
||||
inbound := config.MustKey("inbounds").MustIndex(0)
|
||||
inbound.MustKey("port").SetNumeric(float64(serverPort))
|
||||
inbound.MustKey("settings").MustKey("clients").MustIndex(0).MustKey("id").SetString(user.String())
|
||||
|
||||
content, err = ajson.Marshal(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
startDockerContainer(t, DockerOptions{
|
||||
Image: ImageXRayCore,
|
||||
Ports: []uint16{serverPort, testPort},
|
||||
EntryPoint: "xray",
|
||||
Stdin: content,
|
||||
})
|
||||
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeVMess,
|
||||
VMessOptions: option.VMessOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
UUID: user.String(),
|
||||
PacketEncoding: "xudp",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestMuxCoolSelf(t *testing.T) {
|
||||
user := newUUID()
|
||||
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.TypeVMess,
|
||||
VMessOptions: option.VMessInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.VMessUser{
|
||||
{
|
||||
Name: "sekai",
|
||||
UUID: user.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeVMess,
|
||||
Tag: "vmess-out",
|
||||
VMessOptions: option.VMessOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
UUID: user.String(),
|
||||
PacketEncoding: "xudp",
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "vmess-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
@@ -17,6 +17,10 @@ var muxProtocols = []mux.Protocol{
|
||||
mux.ProtocolSMux,
|
||||
}
|
||||
|
||||
func TestVMessSMux(t *testing.T) {
|
||||
testVMessMux(t, mux.ProtocolSMux.String())
|
||||
}
|
||||
|
||||
func TestShadowsocksMux(t *testing.T) {
|
||||
for _, protocol := range muxProtocols {
|
||||
t.Run(protocol.String(), func(t *testing.T) {
|
||||
@@ -25,14 +29,6 @@ func TestShadowsocksMux(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMessMux(t *testing.T) {
|
||||
for _, protocol := range muxProtocols {
|
||||
t.Run(protocol.String(), func(t *testing.T) {
|
||||
testVMessMux(t, protocol.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testShadowsocksMux(t *testing.T, protocol string) {
|
||||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
|
||||
63
test/ss_plugin_test.go
Normal file
63
test/ss_plugin_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func TestShadowsocksObfs(t *testing.T) {
|
||||
for _, mode := range []string{
|
||||
"http", "tls",
|
||||
} {
|
||||
t.Run("obfs-local "+mode, func(t *testing.T) {
|
||||
testShadowsocksPlugin(t, "obfs-local", "obfs="+mode, "--plugin obfs-server --plugin-opts obfs="+mode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShadowsocksV2RayPlugin(t *testing.T) {
|
||||
testShadowsocksPlugin(t, "v2ray-plugin", "", "--plugin v2ray-plugin --plugin-opts=server")
|
||||
}
|
||||
|
||||
func testShadowsocksPlugin(t *testing.T, name string, opts string, args string) {
|
||||
startDockerContainer(t, DockerOptions{
|
||||
Image: ImageShadowsocksLegacy,
|
||||
Ports: []uint16{serverPort, testPort},
|
||||
Env: []string{
|
||||
"SS_MODULE=ss-server",
|
||||
"SS_CONFIG=-s 0.0.0.0 -u -p 10000 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL " + args,
|
||||
},
|
||||
})
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeShadowsocks,
|
||||
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
Method: "chacha20-ietf-poly1305",
|
||||
Password: "FzcLbKs2dY9mhL",
|
||||
Plugin: name,
|
||||
PluginOptions: opts,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuitSimple(t, clientPort, testPort)
|
||||
}
|
||||
@@ -266,7 +266,7 @@ func TestVMessQUICSelf(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testSuitSimple1(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||
|
||||
@@ -18,7 +18,7 @@ func newUUID() uuid.UUID {
|
||||
return user
|
||||
}
|
||||
|
||||
func _TestVMessAuto(t *testing.T) {
|
||||
func TestVMessAuto(t *testing.T) {
|
||||
security := "auto"
|
||||
t.Run("self", func(t *testing.T) {
|
||||
testVMessSelf(t, security, 0, false, false, false)
|
||||
@@ -306,7 +306,7 @@ func testVMessSelf(t *testing.T, security string, alterId int, globalPadding boo
|
||||
AlterId: alterId,
|
||||
GlobalPadding: globalPadding,
|
||||
AuthenticatedLength: authenticatedLength,
|
||||
PacketAddr: packetAddr,
|
||||
PacketEncoding: "packetaddr",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
9
transport/clashssr/obfs/base.go
Normal file
9
transport/clashssr/obfs/base.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package obfs
|
||||
|
||||
type Base struct {
|
||||
Host string
|
||||
Port int
|
||||
Key []byte
|
||||
IVSize int
|
||||
Param string
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user