Compare commits

..

4 Commits

Author SHA1 Message Date
世界
a37cab48d2 Bump version 2022-09-10 23:13:58 +08:00
世界
c586c8f361 Fix socks4 request 2022-09-10 22:53:06 +08:00
世界
e68fa3e12d Fix processing empty dns result 2022-09-10 22:52:54 +08:00
世界
7f5b9e0e3b Run build on main branch 2022-09-10 22:52:54 +08:00
248 changed files with 1669 additions and 23134 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,25 +38,13 @@ type myUpstreamHandlerWrapper struct {
}
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
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)
w.metadata.Destination = metadata.Destination
return w.connectionHandler(ctx, conn, w.metadata)
}
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
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)
w.metadata.Destination = metadata.Destination
return w.packetHandler(ctx, conn, w.metadata)
}
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
@@ -90,23 +78,13 @@ func NewUpstreamContextHandler(
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
myMetadata := ContextFrom(ctx)
if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
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)
if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
myMetadata.Destination = metadata.Destination
return w.packetHandler(ctx, conn, *myMetadata)
}

2
box.go
View File

@@ -154,7 +154,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
if err != nil {
return nil, E.Cause(err, "create clash api server")
}
router.SetClashServer(clashServer)
router.SetTrafficController(clashServer)
}
return &Box{
router: router,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +0,0 @@
package tls
import (
"context"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
)
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
return newSTDServer(ctx, logger, options)
}

View File

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

View File

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

View File

@@ -1,29 +1,26 @@
package constant
const (
TypeTun = "tun"
TypeRedirect = "redirect"
TypeTProxy = "tproxy"
TypeDirect = "direct"
TypeBlock = "block"
TypeDNS = "dns"
TypeSocks = "socks"
TypeHTTP = "http"
TypeMixed = "mixed"
TypeShadowsocks = "shadowsocks"
TypeVMess = "vmess"
TypeTrojan = "trojan"
TypeNaive = "naive"
TypeWireGuard = "wireguard"
TypeHysteria = "hysteria"
TypeTor = "tor"
TypeSSH = "ssh"
TypeShadowTLS = "shadowtls"
TypeShadowsocksR = "shadowsocksr"
TypeVLESS = "vless"
TypeTun = "tun"
TypeRedirect = "redirect"
TypeTProxy = "tproxy"
TypeDirect = "direct"
TypeBlock = "block"
TypeDNS = "dns"
TypeSocks = "socks"
TypeHTTP = "http"
TypeMixed = "mixed"
TypeShadowsocks = "shadowsocks"
TypeVMess = "vmess"
TypeTrojan = "trojan"
TypeNaive = "naive"
TypeWireGuard = "wireguard"
TypeHysteria = "hysteria"
TypeTor = "tor"
TypeSSH = "ssh"
TypeShadowTLS = "shadowtls"
)
const (
TypeSelector = "selector"
TypeURLTest = "urltest"
)

5
constant/quic.go Normal file
View File

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

9
constant/quic_stub.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,10 +8,7 @@
"clash_api": {
"external_controller": "127.0.0.1:9090",
"external_ui": "folder",
"secret": "",
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
"secret": ""
}
}
}
@@ -29,7 +26,7 @@
#### external_controller
RESTful web API listening address. Clash API will be disabled if empty.
RESTful web API listening address. Disabled if empty.
#### external_ui
@@ -41,22 +38,4 @@ serve it at `http://{{external-controller}}/ui`.
Secret for the RESTful API (optional)
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
ALWAYS set a secret if RESTful API is listening on 0.0.0.0
#### default_mode
Default mode in clash, `rule` will be used if empty.
This setting has no direct effect, but can be used in routing and DNS rules via the `clash_mode` rule item.
#### store_selected
!!! note ""
The tag must be set for target outbounds.
Store selected outbound for the `Selector` outbound in cache file.
#### cache_file
Cache file path, `cache.db` will be used if empty.
ALWAYS set a secret if RESTful API is listening on 0.0.0.0

View File

@@ -8,10 +8,7 @@
"clash_api": {
"external_controller": "127.0.0.1:9090",
"external_ui": "folder",
"secret": "",
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
"secret": ""
}
}
}
@@ -29,7 +26,7 @@
#### external_controller
RESTful web API 监听地址。如果为空,则禁用 Clash API。
RESTful web API 监听地址。
#### external_ui
@@ -39,22 +36,4 @@ RESTful web API 监听地址。如果为空,则禁用 Clash API。
RESTful API 的密钥(可选)
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
#### default_mode
Clash 中的默认模式,默认使用 `rule`
此设置没有直接影响,但可以通过 `clash_mode` 规则项在路由和 DNS 规则中使用。
#### store_selected
!!! note ""
必须为目标出站设置标签。
`Selector` 中出站的选定的目标出站存储在缓存文件中。
#### cache_file
缓存文件路径,默认使用`cache.db`
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。

View File

@@ -11,12 +11,12 @@
"interface_name": "tun0",
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"mtu": 9000,
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
"stack": "system",
"stack": "gvisor",
"include_uid": [
0
],
@@ -80,10 +80,6 @@ Set the default route to the Tun.
To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface`
!!! note "Use with Android VPN"
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
#### strict_route
Enforce strict routing rules in Linux when `auto_route` is enabled:
@@ -96,10 +92,6 @@ not be accessible by others.
#### endpoint_independent_nat
!!! info ""
This item is only available on the gvisor stack, other stacks are endpoint-independent NAT by default.
Enable endpoint-independent NAT.
Performance may degrade slightly, so it is not recommended to enable on when it is not needed.
@@ -112,15 +104,14 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
TCP/IP stack.
| Stack | Description | Status |
|------------------|----------------------------------------------------------------------------------|-------------------|
| system (default) | Sometimes better performance | recommended |
| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended |
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
| Stack | Upstream | Status |
|------------------|-----------------------------------------------------------------------|-------------------|
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | recommended |
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
!!! warning ""
gVisor and LWIP stacks is not included by default, see [Installation](/#installation).
The LWIP stack is not included by default, see [Installation](/#installation).
#### include_uid

View File

@@ -11,12 +11,12 @@
"interface_name": "tun0",
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"mtu": 9000,
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
"stack": "system",
"stack": "gvisor",
"include_uid": [
0
],
@@ -80,10 +80,6 @@ tun 接口的 IPv6 前缀。
为避免流量环回,请设置 `route.auto_detect_interface``route.default_interface``outbound.bind_interface`
!!! note "与 Android VPN 一起使用"
VPN 默认优先于 tun。要使 tun 经过 VPN启用 `route.override_android_vpn`
#### strict_route
在 Linux 中启用 `auto_route` 时执行严格的路由规则。
@@ -107,15 +103,14 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
TCP/IP 栈。
| 栈 | 描述 | 状态 |
|-------------|--------------------------------------------------------------------------|-------|
| system (默认) | 有时性能更好 | 推荐 |
| gVisor | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
| 栈 | 上游 | 状态 |
|------------------|-----------------------------------------------------------------------|-------|
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | 推荐 |
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
!!! warning ""
默认安装不包含 gVisor 和 LWIP 栈,请参阅 [安装](/zh/#_2)。
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
#### include_uid
@@ -145,10 +140,10 @@ TCP/IP 栈。
限制被路由的 Android 用户。
| 常用用户 | ID |
| 常用用户 | ID |
|--|-----|
| 您 | 0 |
| 工作资料 | 10 |
| 您 | 0 |
| 工作资料 | 10 |
#### include_package

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,5 +10,5 @@ import (
)
func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
return clashapi.NewServer(router, logFactory, options)
return clashapi.NewServer(router, logFactory, options), nil
}

View File

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

View File

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

View File

@@ -8,10 +8,10 @@ import (
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/websocket"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
"github.com/gorilla/websocket"
)
func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler {

View File

@@ -61,6 +61,7 @@ func findProxyByName(router adapter.Router) func(next http.Handler) http.Handler
func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
var info badjson.JSONObject
var clashType string
var isGroup bool
switch detour.Type() {
case C.TypeDirect:
clashType = "Direct"
@@ -69,31 +70,18 @@ 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"
case C.TypeURLTest:
clashType = "URLTest"
isGroup = true
default:
clashType = "Direct"
clashType = "Socks"
}
info.Put("type", clashType)
info.Put("name", detour.Tag())
@@ -104,9 +92,10 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
} else {
info.Put("history", []*urltest.History{})
}
if group, isGroup := detour.(adapter.OutboundGroup); isGroup {
info.Put("now", group.Now())
info.Put("all", group.All())
if isGroup {
selector := detour.(adapter.OutboundGroup)
info.Put("now", selector.Now())
info.Put("all", selector.All())
}
return &info
}

View File

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

33
go.mod
View File

@@ -4,9 +4,6 @@ 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
github.com/dustin/go-humanize v1.0.0
@@ -14,48 +11,44 @@ require (
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.3.0+incompatible
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/yamux v0.1.1
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.0.4
github.com/miekg/dns v1.1.50
github.com/oschwald/maxminddb-golang v1.10.0
github.com/pires/go-proxyproto v0.6.2
github.com/refraction-networking/utls v1.1.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220
github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20220925112147-6bad0c2380ca
github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.10.0
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7
golang.org/x/net v0.0.0-20220923203811-8be639271d50
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
)
//replace github.com/sagernet/sing => ../sing
require (
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
@@ -66,7 +59,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/multierr v1.6.0 // indirect

75
go.sum
View File

@@ -3,21 +3,13 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8=
github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/caddyserver/certmagic v0.17.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=
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -51,8 +43,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
@@ -81,22 +73,22 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
@@ -109,8 +101,6 @@ github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -131,34 +121,32 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/refraction-networking/utls v1.1.2 h1:a7GQauRt72VG+wtNm0lnrAaCGlyX47gEi1++dSsDBpw=
github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tSdX1+SqUB7rL0QnGqyg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kxbgGa2Xp0H2CCsx1o2pVTt0f+hmfuHH4=
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-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 v0.0.0-20220910144724-62c4ebdbcb3f h1:w1TJq7Lw3It35tDyMsZLtYz4T2msf1UK9JxC85L5+sk=
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-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/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -175,8 +163,6 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
@@ -188,16 +174,15 @@ 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-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q=
go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 h1:WJywXQVIb56P2kAvXeMGTIgQ1ZHQxR60+F9dLsodECc=
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -226,11 +211,9 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI=
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -239,7 +222,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -251,7 +233,6 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -259,13 +240,12 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -288,7 +268,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -347,8 +326,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=

View File

@@ -3,6 +3,7 @@ package inbound
import (
"context"
"net"
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/settings"
@@ -41,6 +42,7 @@ type myInboundAdapter struct {
tcpListener net.Listener
udpConn *net.UDPConn
udpAddr M.Socksaddr
packetAccess sync.RWMutex
packetOutboundClosed chan struct{}
packetOutbound chan *myInboundPacket
}
@@ -141,13 +143,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()).Unwrap()
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
}
if !metadata.Destination.IsValid() {
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
}
if tcpConn, isTCP := common.Cast[*net.TCPConn](conn); isTCP {
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr()).Unwrap()
metadata.OriginDestination = M.SocksaddrFromNet(tcpConn.LocalAddr())
}
return metadata
}

View File

@@ -29,7 +29,7 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
}
if a.listenOptions.ProxyProtocol {
a.logger.Debug("proxy protocol enabled")
tcpListener = &proxyproto.Listener{Listener: tcpListener, AcceptNoHeader: a.listenOptions.ProxyProtocolAcceptNoHeader}
tcpListener = &proxyproto.Listener{Listener: tcpListener}
}
a.tcpListener = tcpListener
return tcpListener, err

View File

@@ -10,7 +10,6 @@ import (
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -18,15 +17,11 @@ import (
func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) {
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
var lc net.ListenConfig
if !a.listenOptions.UDPFragment {
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
}
udpConn, err := lc.ListenPacket(a.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
if err != nil {
return nil, err
}
a.udpConn = udpConn.(*net.UDPConn)
a.udpConn = udpConn
a.udpAddr = bindAddr
a.logger.Info("udp server started at ", udpConn.LocalAddr())
return udpConn, err
@@ -54,7 +49,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).Unwrap()
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
if err != nil {
@@ -86,7 +81,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).Unwrap()
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
if err != nil {
@@ -112,7 +107,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).Unwrap()
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata)
if err != nil {
@@ -140,7 +135,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).Unwrap()
metadata.Source = M.SocksaddrFromNetIP(addr)
metadata.OriginDestination = a.udpAddr
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
if err != nil {
@@ -208,12 +203,17 @@ func (s *myInboundPacketAdapter) Upstream() any {
}
func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
s.packetAccess.RLock()
defer s.packetAccess.RUnlock()
select {
case s.packetOutbound <- &myInboundPacket{buffer, destination}:
return nil
case <-s.packetOutboundClosed:
return os.ErrClosed
default:
}
s.packetOutbound <- &myInboundPacket{buffer, destination}
return nil
}
func (s *myInboundPacketAdapter) Close() error {

View File

@@ -3,11 +3,11 @@ package inbound
import (
std_bufio "bufio"
"context"
"crypto/tls"
"net"
"os"
"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/log"
"github.com/sagernet/sing-box/option"
@@ -26,7 +26,7 @@ var (
type HTTP struct {
myInboundAdapter
authenticator auth.Authenticator
tlsConfig tls.ServerConfig
tlsConfig *TLSConfig
}
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (*HTTP, error) {
@@ -44,7 +44,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
authenticator: auth.NewAuthenticator(options.Users),
}
if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
@@ -67,13 +67,13 @@ func (h *HTTP) Start() error {
func (h *HTTP) Close() error {
return common.Close(
&h.myInboundAdapter,
h.tlsConfig,
common.PtrOrNil(h.tlsConfig),
)
}
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.tlsConfig != nil {
conn = h.tlsConfig.Server(conn)
conn = tls.Server(conn, h.tlsConfig.Config())
}
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/sagernet/quic-go"
"github.com/sagernet/quic-go/congestion"
"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/log"
"github.com/sagernet/sing-box/option"
@@ -27,7 +26,7 @@ var _ adapter.Inbound = (*Hysteria)(nil)
type Hysteria struct {
myInboundAdapter
quicConfig *quic.Config
tlsConfig tls.ServerConfig
tlsConfig *TLSConfig
authKey []byte
xplusKey []byte
sendBPS uint64
@@ -117,7 +116,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
if len(options.TLS.ALPN) == 0 {
options.TLS.ALPN = []string{hysteria.DefaultALPN}
}
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
@@ -138,11 +137,7 @@ func (h *Hysteria) Start() error {
if err != nil {
return err
}
rawConfig, err := h.tlsConfig.Config()
if err != nil {
return err
}
listener, err := quic.Listen(packetConn, rawConfig, h.quicConfig)
listener, err := quic.Listen(packetConn, h.tlsConfig.Config(), h.quicConfig)
if err != nil {
return err
}
@@ -255,25 +250,24 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
if err != nil {
return err
}
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
})
if err != nil {
return err
}
var metadata adapter.InboundContext
metadata.Inbound = h.tag
metadata.InboundType = C.TypeHysteria
metadata.SniffEnabled = h.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = h.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(h.listenOptions.DomainStrategy)
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port).Unwrap()
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr())
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
if !request.UDP {
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
})
if err != nil {
return err
}
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination, false), metadata)
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata)
} else {
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
var id uint32
@@ -283,13 +277,6 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
h.udpSessions[id] = nCh
h.udpSessionId += 1
h.udpAccess.Unlock()
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
UDPSessionID: id,
})
if err != nil {
return err
}
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
h.udpAccess.Lock()
if ch, ok := h.udpSessions[id]; ok {
@@ -314,6 +301,6 @@ func (h *Hysteria) Close() error {
return common.Close(
&h.myInboundAdapter,
h.listener,
h.tlsConfig,
common.PtrOrNil(h.tlsConfig),
)
}

View File

@@ -6,11 +6,11 @@ import (
"context"
"github.com/sagernet/sing-box/adapter"
I "github.com/sagernet/sing-box/include"
C "github.com/sagernet/sing-box/constant"
"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, I.ErrQUICNotIncluded
return nil, C.ErrQUICNotIncluded
}

View File

@@ -2,6 +2,7 @@ package inbound
import (
"context"
"crypto/tls"
"encoding/base64"
"encoding/binary"
"io"
@@ -13,9 +14,7 @@ import (
"time"
"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"
@@ -33,7 +32,7 @@ var _ adapter.Inbound = (*Naive)(nil)
type Naive struct {
myInboundAdapter
authenticator auth.Authenticator
tlsConfig tls.ServerConfig
tlsConfig *TLSConfig
httpServer *http.Server
h3Server any
}
@@ -60,7 +59,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
return nil, E.New("missing users")
}
if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
@@ -70,16 +69,13 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
}
func (n *Naive) Start() error {
var tlsConfig *tls.STDConfig
var tlsConfig *tls.Config
if n.tlsConfig != nil {
err := n.tlsConfig.Start()
if err != nil {
return E.Cause(err, "create TLS config")
}
tlsConfig, err = n.tlsConfig.Config()
if err != nil {
return err
}
tlsConfig = n.tlsConfig.Config()
}
if common.Contains(n.network, N.NetworkTCP) {
@@ -106,7 +102,7 @@ func (n *Naive) Start() error {
if common.Contains(n.network, N.NetworkUDP) {
err := n.configureHTTP3Listener()
if !include.WithQUIC && len(n.network) > 1 {
if !C.QUIC_AVAILABLE && len(n.network) > 1 {
log.Warn(E.Cause(err, "naive http3 disabled"))
} else if err != nil {
return err
@@ -121,7 +117,7 @@ func (n *Naive) Close() error {
&n.myInboundAdapter,
common.PtrOrNil(n.httpServer),
n.h3Server,
n.tlsConfig,
common.PtrOrNil(n.tlsConfig),
)
}

View File

@@ -8,13 +8,9 @@ import (
)
func (n *Naive) configureHTTP3Listener() error {
tlsConfig, err := n.tlsConfig.Config()
if err != nil {
return err
}
h3Server := &http3.Server{
Port: int(n.listenOptions.ListenPort),
TLSConfig: tlsConfig,
TLSConfig: n.tlsConfig.Config(),
Handler: n,
}

View File

@@ -3,9 +3,9 @@
package inbound
import (
I "github.com/sagernet/sing-box/include"
C "github.com/sagernet/sing-box/constant"
)
func (n *Naive) configureHTTP3Listener() error {
return I.ErrQUICNotIncluded
return C.ErrQUICNotIncluded
}

View File

@@ -1,9 +1,8 @@
package tls
package inbound
import (
"context"
"crypto/tls"
"net"
"os"
"github.com/sagernet/sing-box/adapter"
@@ -15,7 +14,9 @@ import (
"github.com/fsnotify/fsnotify"
)
type STDServerConfig struct {
var _ adapter.Service = (*TLSConfig)(nil)
type TLSConfig struct {
config *tls.Config
logger log.Logger
acmeService adapter.Service
@@ -26,17 +27,105 @@ type STDServerConfig struct {
watcher *fsnotify.Watcher
}
func (c *STDServerConfig) NextProtos() []string {
return c.config.NextProtos
func (c *TLSConfig) Config() *tls.Config {
return c.config
}
func (c *STDServerConfig) SetNextProtos(nextProto []string) {
c.config.NextProtos = nextProto
func (c *TLSConfig) Start() error {
if c.acmeService != nil {
return c.acmeService.Start()
} else {
if c.certificatePath == "" && c.keyPath == "" {
return nil
}
err := c.startWatcher()
if err != nil {
c.logger.Warn("create fsnotify watcher: ", err)
}
return nil
}
}
var errInsecureUnused = E.New("tls: insecure unused")
func (c *TLSConfig) startWatcher() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
if c.certificatePath != "" {
err = watcher.Add(c.certificatePath)
if err != nil {
return err
}
}
if c.keyPath != "" {
err = watcher.Add(c.keyPath)
if err != nil {
return err
}
}
c.watcher = watcher
go c.loopUpdate()
return nil
}
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
func (c *TLSConfig) loopUpdate() {
for {
select {
case event, ok := <-c.watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write != fsnotify.Write {
continue
}
err := c.reloadKeyPair()
if err != nil {
c.logger.Error(E.Cause(err, "reload TLS key pair"))
}
case err, ok := <-c.watcher.Errors:
if !ok {
return
}
c.logger.Error(E.Cause(err, "fsnotify error"))
}
}
}
func (c *TLSConfig) reloadKeyPair() error {
if c.certificatePath != "" {
certificate, err := os.ReadFile(c.certificatePath)
if err != nil {
return E.Cause(err, "reload certificate from ", c.certificatePath)
}
c.certificate = certificate
}
if c.keyPath != "" {
key, err := os.ReadFile(c.keyPath)
if err != nil {
return E.Cause(err, "reload key from ", c.keyPath)
}
c.key = key
}
keyPair, err := tls.X509KeyPair(c.certificate, c.key)
if err != nil {
return E.Cause(err, "reload key pair")
}
c.config.Certificates = []tls.Certificate{keyPair}
c.logger.Info("reloaded TLS certificate")
return nil
}
func (c *TLSConfig) Close() error {
if c.acmeService != nil {
return c.acmeService.Close()
}
if c.watcher != nil {
return c.watcher.Close()
}
return nil
}
func NewTLSConfig(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (*TLSConfig, error) {
if !options.Enabled {
return nil, nil
}
@@ -48,9 +137,6 @@ 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{}
}
@@ -61,14 +147,14 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...)
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
minVersion, err := option.ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
@@ -107,25 +193,19 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
}
key = content
}
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 certificate == nil {
return nil, E.New("missing certificate")
}
if key == nil {
return nil, E.New("missing key")
}
keyPair, err := tls.X509KeyPair(certificate, key)
if err != nil {
return nil, E.Cause(err, "parse x509 key pair")
}
tlsConfig.Certificates = []tls.Certificate{keyPair}
}
return &STDServerConfig{
return &TLSConfig{
config: tlsConfig,
logger: logger,
acmeService: acmeService,
@@ -135,109 +215,3 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
keyPath: options.KeyPath,
}, nil
}
func (c *STDServerConfig) Config() (*STDConfig, error) {
return c.config, nil
}
func (c *STDServerConfig) Client(conn net.Conn) Conn {
return tls.Client(conn, c.config)
}
func (c *STDServerConfig) Server(conn net.Conn) Conn {
return tls.Server(conn, c.config)
}
func (c *STDServerConfig) Start() error {
if c.acmeService != nil {
return c.acmeService.Start()
} else {
if c.certificatePath == "" && c.keyPath == "" {
return nil
}
err := c.startWatcher()
if err != nil {
c.logger.Warn("create fsnotify watcher: ", err)
}
return nil
}
}
func (c *STDServerConfig) startWatcher() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
if c.certificatePath != "" {
err = watcher.Add(c.certificatePath)
if err != nil {
return err
}
}
if c.keyPath != "" {
err = watcher.Add(c.keyPath)
if err != nil {
return err
}
}
c.watcher = watcher
go c.loopUpdate()
return nil
}
func (c *STDServerConfig) loopUpdate() {
for {
select {
case event, ok := <-c.watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write != fsnotify.Write {
continue
}
err := c.reloadKeyPair()
if err != nil {
c.logger.Error(E.Cause(err, "reload TLS key pair"))
}
case err, ok := <-c.watcher.Errors:
if !ok {
return
}
c.logger.Error(E.Cause(err, "fsnotify error"))
}
}
}
func (c *STDServerConfig) reloadKeyPair() error {
if c.certificatePath != "" {
certificate, err := os.ReadFile(c.certificatePath)
if err != nil {
return E.Cause(err, "reload certificate from ", c.certificatePath)
}
c.certificate = certificate
}
if c.keyPath != "" {
key, err := os.ReadFile(c.keyPath)
if err != nil {
return E.Cause(err, "reload key from ", c.keyPath)
}
c.key = key
}
keyPair, err := tls.X509KeyPair(c.certificate, c.key)
if err != nil {
return E.Cause(err, "reload key pair")
}
c.config.Certificates = []tls.Certificate{keyPair}
c.logger.Info("reloaded TLS certificate")
return nil
}
func (c *STDServerConfig) Close() error {
if c.acmeService != nil {
return c.acmeService.Close()
}
if c.watcher != nil {
return c.watcher.Close()
}
return nil
}

View File

@@ -1,17 +1,17 @@
//go:build with_acme
package tls
package inbound
import (
"context"
"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"
)

View File

@@ -1,6 +1,6 @@
//go:build !with_acme
package tls
package inbound
import (
"context"

View File

@@ -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()).Unwrap()
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
return t.newConnection(ctx, conn, metadata)
}
@@ -84,15 +84,14 @@ 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).Unwrap()
metadata.Destination = M.SocksaddrFromNetIP(destination)
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{ctx: ctx, source: natConn, destination: metadata.Destination}
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{source: natConn}
})
return nil
}
type tproxyPacketWriter struct {
ctx context.Context
source N.PacketConn
destination M.Socksaddr
conn *net.UDPConn
@@ -100,27 +99,25 @@ type tproxyPacketWriter struct {
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
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)
var udpConn *net.UDPConn
if w.destination == destination {
w.conn = udpConn
} else {
defer udpConn.Close()
if w.conn != nil {
udpConn = w.conn
}
}
return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
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()))
}
func (w *tproxyPacketWriter) Close() error {

View File

@@ -2,11 +2,11 @@ package inbound
import (
"context"
"crypto/tls"
"net"
"os"
"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/log"
"github.com/sagernet/sing-box/option"
@@ -29,7 +29,7 @@ type Trojan struct {
myInboundAdapter
service *trojan.Service[int]
users []option.TrojanUser
tlsConfig tls.ServerConfig
tlsConfig *TLSConfig
fallbackAddr M.Socksaddr
fallbackAddrTLSNextProto map[string]M.Socksaddr
transport adapter.V2RayServerTransport
@@ -49,7 +49,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
users: options.Users,
}
if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
@@ -89,7 +89,11 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
return nil, err
}
if options.Transport != nil {
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
var tlsConfig *tls.Config
if inbound.tlsConfig != nil {
tlsConfig = inbound.tlsConfig.Config()
}
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
if err != nil {
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
}
@@ -139,7 +143,7 @@ func (h *Trojan) Start() error {
func (h *Trojan) Close() error {
return common.Close(
&h.myInboundAdapter,
h.tlsConfig,
common.PtrOrNil(h.tlsConfig),
h.transport,
)
}
@@ -151,7 +155,7 @@ func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, meta
func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.tlsConfig != nil && h.transport == nil {
conn = h.tlsConfig.Server(conn)
conn = tls.Server(conn, h.tlsConfig.Config())
}
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
}
@@ -178,7 +182,7 @@ func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adap
func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
var fallbackAddr M.Socksaddr
if len(h.fallbackAddrTLSNextProto) > 0 {
if tlsConn, loaded := common.Cast[*tls.STDConn](conn); loaded {
if tlsConn, loaded := common.Cast[*tls.Conn](conn); loaded {
connectionState := tlsConn.ConnectionState()
if connectionState.NegotiatedProtocol != "" {
if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {

View File

@@ -40,11 +40,11 @@ type Tun struct {
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
tunName := options.InterfaceName
if tunName == "" {
tunName = tun.CalculateInterfaceName("")
tunName = tun.DefaultInterfaceName()
}
tunMTU := options.MTU
if tunMTU == 0 {
tunMTU = 9000
tunMTU = 1500
}
var udpTimeout int64
if options.UDPTimeout != 0 {
@@ -77,8 +77,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
tunOptions: tun.Options{
Name: tunName,
MTU: tunMTU,
Inet4Address: common.Map(options.Inet4Address, option.ListenPrefix.Build),
Inet6Address: common.Map(options.Inet6Address, option.ListenPrefix.Build),
Inet4Address: options.Inet4Address.Build(),
Inet6Address: options.Inet6Address.Build(),
AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute,
IncludeUID: includeUID,
@@ -86,8 +86,6 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
InterfaceMonitor: router.InterfaceMonitor(),
TableIndex: 2022,
},
endpointIndependentNat: options.EndpointIndependentNat,
udpTimeout: udpTimeout,
@@ -144,18 +142,7 @@ func (t *Tun) Start() error {
return E.Cause(err, "configure tun interface")
}
t.tunIf = tunIf
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
Context: t.ctx,
Tun: tunIf,
MTU: t.tunOptions.MTU,
Name: t.tunOptions.Name,
Inet4Address: t.tunOptions.Inet4Address,
Inet6Address: t.tunOptions.Inet6Address,
EndpointIndependentNat: t.endpointIndependentNat,
UDPTimeout: t.udpTimeout,
Handler: t,
Logger: t.logger,
})
t.tunStack, err = tun.NewStack(t.ctx, t.stack, tunIf, t.tunOptions.MTU, t.endpointIndependentNat, t.udpTimeout, t)
if err != nil {
return err
}
@@ -190,7 +177,7 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata
if err != nil {
t.NewError(ctx, err)
}
return nil
return err
}
func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error {
@@ -212,7 +199,7 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre
if err != nil {
t.NewError(ctx, err)
}
return nil
return err
}
func (t *Tun) NewError(ctx context.Context, err error) {

View File

@@ -2,11 +2,11 @@ package inbound
import (
"context"
"crypto/tls"
"net"
"os"
"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/log"
"github.com/sagernet/sing-box/option"
@@ -31,7 +31,7 @@ type VMess struct {
ctx context.Context
service *vmess.Service[int]
users []option.VMessUser
tlsConfig tls.ServerConfig
tlsConfig *TLSConfig
transport adapter.V2RayServerTransport
}
@@ -62,13 +62,17 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
return nil, err
}
if options.TLS != nil {
inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
inbound.tlsConfig, err = NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
}
if options.Transport != nil {
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
var tlsConfig *tls.Config
if inbound.tlsConfig != nil {
tlsConfig = inbound.tlsConfig.Config()
}
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
if err != nil {
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
}
@@ -80,7 +84,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
func (h *VMess) Start() error {
err := common.Start(
h.service,
h.tlsConfig,
common.PtrOrNil(h.tlsConfig),
)
if err != nil {
return err
@@ -119,7 +123,7 @@ func (h *VMess) Close() error {
return common.Close(
h.service,
&h.myInboundAdapter,
h.tlsConfig,
common.PtrOrNil(h.tlsConfig),
h.transport,
)
}
@@ -131,7 +135,7 @@ func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metad
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.tlsConfig != nil && h.transport == nil {
conn = h.tlsConfig.Server(conn)
conn = tls.Server(conn, h.tlsConfig.Config())
}
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
}

View File

@@ -1,7 +0,0 @@
//go:build with_quic
package include
import _ "github.com/sagernet/sing-dns/quic"
const WithQUIC = true

View File

@@ -1,21 +0,0 @@
//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
})
}

View File

@@ -84,13 +84,10 @@ nav:
- WireGuard: configuration/outbound/wireguard.md
- Hysteria: configuration/outbound/hysteria.md
- ShadowTLS: configuration/outbound/shadowtls.md
- ShadowsocksR: configuration/outbound/shadowsocksr.md
- VLESS: configuration/outbound/vless.md
- Tor: configuration/outbound/tor.md
- 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

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