Compare commits

..

39 Commits

Author SHA1 Message Date
世界
18162df6de Update documentation 2022-11-21 14:28:28 +08:00
世界
0c48aaef08 Fix default dns transport strategy 2022-11-21 14:26:56 +08:00
世界
871f4aebbd Remove unused 2022-11-21 14:26:53 +08:00
世界
12ad50d427 Fix connect packet connection for mux client 2022-11-21 14:26:53 +08:00
世界
14329ad62d Remove follow in update script 2022-11-21 14:26:45 +08:00
世界
eb15e885b9 Support x/h2 v0.2.0 deadline 2022-11-21 14:26:43 +08:00
世界
a55726694a Fix tor geoip 2022-11-21 14:26:40 +08:00
世界
b0235c6257 Fix h2c transport 2022-11-21 14:26:30 +08:00
世界
9a01d52a2c Fix vmess request buffer 2022-11-21 14:21:11 +08:00
世界
2c0524578f Fix smux keep alive 2022-11-13 12:18:08 +08:00
Dreamacro
b10a8d55f3 Fix macOS Ventura process name match 2022-11-13 12:16:25 +08:00
世界
9a8de0f378 Fix decrypt xplus packet 2022-11-13 12:16:00 +08:00
世界
e5018383fe Fix copy pipe 2022-11-13 12:15:45 +08:00
世界
1e4f68b024 Update issue template 2022-11-13 12:14:20 +08:00
Fei1Yang
8c00885599 Update container action
* Fix non-native architectures build for Docker

* Tag latest image with a pre-defined tag

* Replace set-output commands
2022-11-13 12:14:19 +08:00
永雏塔菲
7cf5169c98 Add s390x architecture support
* Update debug.yml

Signed-off-by: 永雏塔菲 <108621198+taffychan@users.noreply.github.com>
2022-11-13 12:14:11 +08:00
世界
ce396466bd Add go1.18 debug build 2022-11-13 12:14:08 +08:00
世界
1635c98783 Bump version 2022-10-19 10:33:46 +08:00
Skyxim
cbcaa0f590 Check destination before udp connect 2022-10-19 10:33:28 +08:00
世界
763b93c021 Fix naive overflow 2022-10-19 10:33:28 +08:00
世界
00da3e0765 Fix sniff fragmented quic client hello 2022-10-19 10:33:28 +08:00
世界
46d7a78158 Fix ssh outbound 2022-10-19 10:31:43 +08:00
世界
de3f70195e Add binary to .gitignore 2022-10-19 10:30:05 +08:00
世界
3105b8c920 Bump version 2022-09-25 22:27:23 +08:00
世界
4c67ab1a54 Fix read source address from grpc-go 2022-09-25 22:27:23 +08:00
世界
84783c5359 Fix fqdn socks5 outbound connection 2022-09-25 14:44:39 +08:00
世界
22b16f82bd Fix missing source address from transport connection 2022-09-25 14:44:33 +08:00
世界
d2add33723 Bump version 2022-09-15 13:12:18 +08:00
世界
ab0daf31c1 Fix clash api proxy type 2022-09-15 13:11:52 +08:00
世界
3d94b948dd Fix port rule match logic 2022-09-15 13:11:20 +08:00
世界
1659ae5d79 Fix close grpc conn 2022-09-15 13:10:18 +08:00
世界
7279855b08 Bump version 2022-09-13 11:25:38 +08:00
世界
925fbca363 Fix concurrent write 2022-09-13 10:36:37 +08:00
世界
a5163e3e3c Fix hysteria inbound 2022-09-13 10:32:14 +08:00
世界
62859e0c6b Fix socks4 client 2022-09-13 10:32:12 +08:00
世界
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
171 changed files with 1576 additions and 17894 deletions

View File

@@ -12,7 +12,7 @@ body:
required: true required: true
- label: Yes, I've searched similar issues on GitHub and didn't find any. - label: Yes, I've searched similar issues on GitHub and didn't find any.
required: true required: true
- label: Yes, I've included all information below (version, config, log, etc). - label: Yes, I've included all information below (version, **FULL** config, **FULL** log, etc).
required: true required: true
- type: textarea - type: textarea

View File

@@ -55,6 +55,27 @@ jobs:
- name: Run Test - name: Run Test
run: | run: |
go test -v ./... go test -v ./...
build_go118:
name: Debug build (Go 1.18)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.18.7
- name: Cache go module
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
key: go118-${{ hashFiles('**/go.sum') }}
- name: Run Test
run: |
go test -v ./...
cross: cross:
strategy: strategy:
matrix: matrix:
@@ -128,6 +149,9 @@ jobs:
- name: linux-mips64el - name: linux-mips64el
goos: linux goos: linux
goarch: mips64le goarch: mips64le
- name: linux-s390x
goos: linux
goarch: s390x
# darwin # darwin
- name: darwin-amd64 - name: darwin-amd64
goos: darwin goos: darwin
@@ -192,4 +216,4 @@ jobs:
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: sing-box-${{ matrix.name }} name: sing-box-${{ matrix.name }}
path: sing-box* path: sing-box*

View File

@@ -15,6 +15,8 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Setup Docker Buildx - name: Setup Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v1
- name: Setup QEMU for Docker Buildx
uses: docker/setup-qemu-action@v2
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v1 uses: docker/login-action@v1
with: with:
@@ -29,15 +31,18 @@ jobs:
- name: Get tag to build - name: Get tag to build
id: tag id: tag
run: | run: |
echo "latest=ghcr.io/sagernet/sing-box:latest" >> $GITHUB_OUTPUT
if [[ -z "${{ github.event.inputs.tag }}" ]]; then if [[ -z "${{ github.event.inputs.tag }}" ]]; then
echo ::set-output name=tag::ghcr.io/sagernet/sing-box:${{ github.ref_name }} echo "versioned=ghcr.io/sagernet/sing-box:${{ github.ref_name }}" >> $GITHUB_OUTPUT
else else
echo ::set-output name=tag::ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }} echo "versioned=ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
fi fi
- name: Build and release Docker images - name: Build and release Docker images
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
platforms: linux/386,linux/amd64 platforms: linux/386,linux/amd64,linux/arm64,linux/s390x
target: dist target: dist
tags: ${{ steps.tag.outputs.tag }} tags: |
push: true ${{ steps.tag.outputs.latest }}
${{ steps.tag.outputs.versioned }}
push: true

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@
/*.db /*.db
/site/ /site/
/bin/ /bin/
/dist/ /dist/
/sing-box

View File

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

View File

@@ -24,6 +24,7 @@ builds:
- linux_amd64_v3 - linux_amd64_v3
- linux_arm64 - linux_arm64
- linux_arm_7 - linux_arm_7
- linux_s390x
- windows_amd64_v1 - windows_amd64_v1
- windows_amd64_v3 - windows_amd64_v3
- windows_386 - windows_386

View File

@@ -64,7 +64,7 @@ test:
@go test -v . && \ @go test -v . && \
pushd test && \ pushd test && \
go mod tidy && \ go mod tidy && \
go test -v -tags with_quic,with_wireguard,with_grpc,with_ech,with_utls . && \ go test -v -tags with_quic,with_wireguard,with_grpc . && \
popd popd
clean: clean:

View File

@@ -9,22 +9,18 @@ import (
type ClashServer interface { type ClashServer interface {
Service Service
Mode() string TrafficController
StoreSelected() bool
CacheFile() ClashCacheFile
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
} }
type Tracker interface { type Tracker interface {
Leave() 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 { type OutboundGroup interface {
Now() string Now() string
All() []string All() []string

View File

@@ -39,9 +39,7 @@ type Router interface {
InterfaceMonitor() tun.DefaultInterfaceMonitor InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager PackageManager() tun.PackageManager
Rules() []Rule Rules() []Rule
SetTrafficController(controller TrafficController)
ClashServer() ClashServer
SetClashServer(controller ClashServer)
} }
type Rule interface { type Rule interface {

View File

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

2
box.go
View File

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

View File

@@ -112,10 +112,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
if options.TCPFastOpen { if options.TCPFastOpen {
warnTFOOnUnsupportedPlatform.Check() warnTFOOnUnsupportedPlatform.Check()
} }
if !options.UDPFragment {
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
}
var bindUDPAddr string var bindUDPAddr string
udpDialer := dialer udpDialer := dialer
var bindAddress netip.Addr var bindAddress netip.Addr

View File

@@ -1,26 +1,34 @@
package tls package dialer
import ( import (
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"net" "net"
"net/netip" "net/netip"
"os" "os"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
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 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 var serverName string
if options.ServerName != "" { if options.ServerName != "" {
serverName = options.ServerName serverName = options.ServerName
} else if serverAddress != "" { } else if serverAddress != "" {
if _, err := netip.ParseAddr(serverName); err != nil { if _, err := netip.ParseAddr(serverName); err == nil {
serverName = serverAddress serverName = serverAddress
} }
} }
@@ -54,14 +62,14 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
tlsConfig.NextProtos = options.ALPN tlsConfig.NextProtos = options.ALPN
} }
if options.MinVersion != "" { if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion) minVersion, err := option.ParseTLSVersion(options.MinVersion)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse min_version") return nil, E.Cause(err, "parse min_version")
} }
tlsConfig.MinVersion = minVersion tlsConfig.MinVersion = minVersion
} }
if options.MaxVersion != "" { if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion) maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse max_version") return nil, E.Cause(err, "parse max_version")
} }
@@ -96,13 +104,42 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
} }
tlsConfig.RootCAs = certPool tlsConfig.RootCAs = certPool
} }
return &stdClientConfig{&tlsConfig}, nil return &tlsConfig, nil
} }
func (s *stdClientConfig) Config() (*STDConfig, error) { func NewTLS(dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
return s.config, nil 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) Client(conn net.Conn) Conn { func (d *TLSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
return tls.Client(conn, s.config) 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 (d *TLSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return nil, os.ErrInvalid
}
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

@@ -329,6 +329,23 @@ func (c *ClientPacketConn) Write(b []byte) (n int, err error) {
return c.ExtendedConn.Write(b) return c.ExtendedConn.Write(b)
} }
func (c *ClientPacketConn) ReadBuffer(buffer *buf.Buffer) (err error) {
if !c.responseRead {
err = c.readResponse()
if err != nil {
return
}
c.responseRead = true
}
var length uint16
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
if err != nil {
return
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
return
}
func (c *ClientPacketConn) WriteBuffer(buffer *buf.Buffer) error { func (c *ClientPacketConn) WriteBuffer(buffer *buf.Buffer) error {
if !c.requestWrite { if !c.requestWrite {
defer buffer.Release() defer buffer.Release()
@@ -343,6 +360,11 @@ func (c *ClientPacketConn) FrontHeadroom() int {
return 2 return 2
} }
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
err = c.ReadBuffer(buffer)
return
}
func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
return c.WriteBuffer(buffer) return c.WriteBuffer(buffer)
} }

View File

@@ -43,7 +43,7 @@ func ParseProtocol(name string) (Protocol, error) {
func (p Protocol) newServer(conn net.Conn) (abstractSession, error) { func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
switch p { switch p {
case ProtocolSMux: case ProtocolSMux:
session, err := smux.Server(conn, nil) session, err := smux.Server(conn, smuxConfig())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -58,7 +58,7 @@ func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
func (p Protocol) newClient(conn net.Conn) (abstractSession, error) { func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
switch p { switch p {
case ProtocolSMux: case ProtocolSMux:
session, err := smux.Client(conn, nil) session, err := smux.Client(conn, smuxConfig())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -70,6 +70,12 @@ func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
} }
} }
func smuxConfig() *smux.Config {
config := smux.DefaultConfig()
config.KeepAliveDisabled = true
return config
}
func yaMuxConfig() *yamux.Config { func yaMuxConfig() *yamux.Config {
config := yamux.DefaultConfig() config := yamux.DefaultConfig()
config.LogOutput = io.Discard config.LogOutput = io.Discard

View File

@@ -5,6 +5,8 @@ import (
"encoding/binary" "encoding/binary"
"net/netip" "net/netip"
"os" "os"
"strconv"
"strings"
"syscall" "syscall"
"unsafe" "unsafe"
@@ -29,6 +31,22 @@ func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, so
return &Info{ProcessPath: processName, UserId: -1}, nil return &Info{ProcessPath: processName, UserId: -1}, nil
} }
var structSize = func() int {
value, _ := syscall.Sysctl("kern.osrelease")
major, _, _ := strings.Cut(value, ".")
n, _ := strconv.ParseInt(major, 10, 64)
switch true {
case n >= 22:
return 408
default:
// from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
// size/offset are round up (aligned) to 8 bytes in darwin
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
return 384
}
}()
func findProcessName(network string, ip netip.Addr, port int) (string, error) { func findProcessName(network string, ip netip.Addr, port int) (string, error) {
var spath string var spath string
switch network { switch network {
@@ -53,7 +71,7 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
// size/offset are round up (aligned) to 8 bytes in darwin // size/offset are round up (aligned) to 8 bytes in darwin
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) + // rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n)) // 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
itemSize := 384 itemSize := structSize
if network == N.NetworkTCP { if network == N.NetworkTCP {
// rup8(sizeof(xtcpcb_n)) // rup8(sizeof(xtcpcb_n))
itemSize += 208 itemSize += 208

View File

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

View File

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

View File

@@ -24,8 +24,7 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
if err != nil { if err != nil {
return nil, err return nil, err
} }
if typeByte&0x40 == 0 {
if typeByte&0x80 == 0 || typeByte&0x40 == 0 {
return nil, E.New("bad type byte") return nil, E.New("bad type byte")
} }
var versionNumber uint32 var versionNumber uint32
@@ -145,9 +144,6 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
default: default:
return nil, E.New("bad packet number length") return nil, E.New("bad packet number length")
} }
if packetNumber != 0 {
return nil, E.New("bad packet number: ", packetNumber)
}
extHdrLen := hdrLen + int(packetNumberLength) extHdrLen := hdrLen + int(packetNumberLength)
copy(newPacket[extHdrLen:hdrLen+4], packet[extHdrLen:]) copy(newPacket[extHdrLen:hdrLen+4], packet[extHdrLen:])
data := newPacket[extHdrLen : int(packetLen)+hdrLen] data := newPacket[extHdrLen : int(packetLen)+hdrLen]
@@ -172,37 +168,76 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
if err != nil { if err != nil {
return nil, err return nil, err
} }
var frameType byte
var frameLen uint64
var fragments []struct {
offset uint64
length uint64
payload []byte
}
decryptedReader := bytes.NewReader(decrypted) decryptedReader := bytes.NewReader(decrypted)
frameType, err := decryptedReader.ReadByte() for {
if err != nil {
return nil, err
}
for frameType == 0x0 {
// skip padding
frameType, err = decryptedReader.ReadByte() frameType, err = decryptedReader.ReadByte()
if err != nil { if err == io.EOF {
return nil, err break
}
switch frameType {
case 0x0:
continue
case 0x1:
continue
case 0x6:
var offset uint64
offset, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
}
var length uint64
length, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
}
index := len(decrypted) - decryptedReader.Len()
fragments = append(fragments, struct {
offset uint64
length uint64
payload []byte
}{offset, length, decrypted[index : index+int(length)]})
frameLen += length
_, err = decryptedReader.Seek(int64(length), io.SeekCurrent)
if err != nil {
return nil, err
}
default:
// ignore unknown frame type
} }
}
if frameType != 0x6 {
// not crypto frame
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, nil
}
_, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return nil, err
}
_, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return nil, err
} }
tlsHdr := make([]byte, 5) tlsHdr := make([]byte, 5)
tlsHdr[0] = 0x16 tlsHdr[0] = 0x16
binary.BigEndian.PutUint16(tlsHdr[1:], uint16(0x0303)) binary.BigEndian.PutUint16(tlsHdr[1:], uint16(0x0303))
binary.BigEndian.PutUint16(tlsHdr[3:], uint16(decryptedReader.Len())) binary.BigEndian.PutUint16(tlsHdr[3:], uint16(frameLen))
metadata, err := TLSClientHello(ctx, io.MultiReader(bytes.NewReader(tlsHdr), decryptedReader)) var index uint64
var length int
var readers []io.Reader
readers = append(readers, bytes.NewReader(tlsHdr))
find:
for {
for _, fragment := range fragments {
if fragment.offset == index {
readers = append(readers, bytes.NewReader(fragment.payload))
index = fragment.offset + fragment.length
length++
continue find
}
}
if length == len(fragments) {
break
}
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, E.New("bad fragments")
}
metadata, err := TLSClientHello(ctx, io.MultiReader(readers...))
if err != nil { if err != nil {
return nil, err return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
} }
metadata.Protocol = C.ProtocolQUIC metadata.Protocol = C.ProtocolQUIC
return metadata, nil return metadata, nil

View File

@@ -19,6 +19,15 @@ func TestSniffQUICv1(t *testing.T) {
require.Equal(t, metadata.Domain, "cloudflare-quic.com") require.Equal(t, metadata.Domain, "cloudflare-quic.com")
} }
func TestSniffQUICFragment(t *testing.T) {
t.Parallel()
pkt, err := hex.DecodeString("cc00000001082e3d5d1b64040c55000044d0ccea69e773f6631c1d18b04ae9ee75fcfc34ef74fa62533c93534338a86f101a05d70e0697fb483063fa85db1c59ccfbda5c35234931d8524d8aac37eaaad649470a67794cd754b23c98695238b8363452333bc8c4858376b4166e001da2006e35cf98a91e11a56419b2786775284942d0f7163982f7c248867d12dd374957481dbc564013ff785e1916195eef671f725908f761099d992d69231336ba81d9e25fe2fa3a6eff4318a6ccf10176fc841a1b315f7b35c5b292266fc869d76ca533e7d14e86d82db2e22eacd350977e47d2e012d8a5891c5aaf2a0f4c2b2dae897c161e5b68cbb4dee952472bdc1e21504b8f02534ec4366ce3f8bf86efc78e0232778fbd554457567112abdcafcf6d4d8fcf35083c25d9495679614aba21696e338c62b585046cc55ba8c09c844361d889a47c3ea703b4e23545a9ab2c0bb369693a9ddfb5daffa85cf80fdd6ad66738664e5b0a551729b4955cff7255afcb04dee88c2f072c9de7400947a1bd9327ac5d012a33000ada021d4c03d249fb017d6ac9200b2f9436beab8183ddfbe2d8aee31ffb7df9e1cc181c1af80c39a89965d18ed12da8e3ebe2ae1fbe4b348f83ba19e3e3d1c9b22bcf03ab6ad9b30fe180623faa291ebad83bcd71d7b57f2f5e2f3b8e81d24fb70b2f2159239e8f21ffafef2747aba47d97ab4081e603c018b10678cf99cab1fb42156a14486fa435153979d7279fd22cd40af7088bfc7eff41af2f4b3c0c8864d0040d74dff427f7bffdb8c278474ea00311326cf4925471a8cf596cb92119f19e0f789490ba9cb77b98015a987d93e0324cf1a38b55109f00c3e6ddc5180fb107bf468323afec9bb49fd6a86418569789d66cafe3b8253c2aebb3af3782c1c54dd560487d031d28e6a6e23e159581bb1d47efc4da3fe1d169f9ffb0ca9ba61af0a38a92fde5bc5e6ec026e8378a6315a7b95abf1d2da790a391306ce74d0baf8e2ce648ca74c487f2c0a76a28a80cdf5bd34316eb607684fe7e6d9e83824a00e07660d0b90e3cddd61ebf10748263474afa88c300549e64ce2e90560bb1a12dee7e9484f729a8a4ee7c5651adb5194b3b3ae38e501567c7dbf36e7bb37a2c20b74655f47f2d9af18e52e9d4c9c9eee8e63745779b8f0b06f3a09d846ba62eb978ad77c85de1ee2fee3fbb4c2d283c73e1ccba56a4658e48a2665d200f7f9342f8e84c2ba490094a4f94feec89e42d2f654f564c2beb2997bafa1fc2c68ad8e160b63587d49abc31b834878d52acfb05fb73d0e059b206162e3c90b40c4bc08407ffcb3c08431895b691a3fea923f1f3b48db75d3e6b91fd319ffe4d486e0e14bd5c6affc838dee63d9e0b80f169b5e6c02c7321dcb20deb2b8e707b60e345a308d505bbf26a93d8f18b39d62632e9a77cbe48b3b32eb8819d6311a49820d40f5acbf0273c91c36b2269a03e72ee64df3dfb10ddefe73c64ef60870b2b77bd99dea655f5fe791b538a929a14d99f6d69685d72431ea5f0f4b27a044f2f575ab474fcc3857895934de1ca2581798eaef2c17fe5aaf2e6add97fa32997c7026f15c1b1ad0e6043ae506027a7c0242546fdc851cca39a204e56879f2cef838be8ec66e0f2292f8c862e06f810eb9b80c7a467ce6e90155206352c7f82b1173ba3b98d35bb72c259a60db20dd1a43fe6d7aef0265e6eaa5caafd9b64b448ff745a2046acbdb65cf2a5007809808a4828dc99097feedc734c236260c584")
require.NoError(t, err)
metadata, err := sniff.QUICClientHello(context.Background(), pkt)
require.NoError(t, err)
require.Equal(t, metadata.Domain, "cloudflare-quic.com")
}
func FuzzSniffQUIC(f *testing.F) { func FuzzSniffQUIC(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) { f.Fuzz(func(t *testing.T, data []byte) {
sniff.QUICClientHello(context.Background(), data) sniff.QUICClientHello(context.Background(), data)

View File

@@ -5,7 +5,6 @@ import (
"context" "context"
"io" "io"
"net" "net"
"os"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -30,23 +29,25 @@ func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, sniffers
return nil, err return nil, err
} }
var metadata *adapter.InboundContext var metadata *adapter.InboundContext
var errors []error
for _, sniffer := range sniffers { for _, sniffer := range sniffers {
metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes())) metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes()))
if err != nil { if metadata != nil {
continue return metadata, nil
} }
return metadata, nil errors = append(errors, err)
} }
return nil, os.ErrInvalid return nil, E.Errors(errors...)
} }
func PeekPacket(ctx context.Context, packet []byte, sniffers ...PacketSniffer) (*adapter.InboundContext, error) { func PeekPacket(ctx context.Context, packet []byte, sniffers ...PacketSniffer) (*adapter.InboundContext, error) {
var errors []error
for _, sniffer := range sniffers { for _, sniffer := range sniffers {
sniffMetadata, err := sniffer(ctx, packet) metadata, err := sniffer(ctx, packet)
if err != nil { if metadata != nil {
continue return metadata, nil
} }
return sniffMetadata, nil errors = append(errors, err)
} }
return nil, os.ErrInvalid return nil, E.Errors(errors...)
} }

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) (net.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,46 +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 {
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
}
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,188 +0,0 @@
//go:build with_ech
package tls
import (
"context"
"crypto/x509"
"encoding/base64"
"net"
"net/netip"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/cloudflaretls"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
mDNS "github.com/miekg/dns"
"golang.org/x/net/dns/dnsmessage"
)
type echClientConfig struct {
config *tls.Config
}
func (e *echClientConfig) Config() (*STDConfig, error) {
return nil, E.New("unsupported usage for ECH")
}
func (e *echClientConfig) Client(conn net.Conn) Conn {
return tls.Client(conn, e.config)
}
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 tls.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 tls.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 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
}
// 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 := tls.UnmarshalECHConfigs(clientConfigContent)
if err != nil {
return nil, err
}
tlsConfig.ClientECHConfigs = clientConfig
} else {
tlsConfig.GetClientECHConfigs = fetchECHClientConfig(router)
}
return &echClientConfig{&tlsConfig}, nil
}
const typeHTTPS = 65
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]tls.ECHConfig, error) {
return func(ctx context.Context, serverName string) ([]tls.ECHConfig, error) {
message := &dnsmessage.Message{
Header: dnsmessage.Header{
RecursionDesired: true,
},
Questions: []dnsmessage.Question{
{
Name: dnsmessage.MustNewName(serverName + "."),
Type: typeHTTPS,
Class: dnsmessage.ClassINET,
},
},
}
response, err := router.Exchange(ctx, message)
if err != nil {
return nil, err
}
if response.RCode != dnsmessage.RCodeSuccess {
return nil, dns.RCodeError(response.RCode)
}
content, err := response.Pack()
if err != nil {
return nil, err
}
var mMsg mDNS.Msg
err = mMsg.Unpack(content)
if err != nil {
return nil, err
}
for _, rr := range mMsg.Answer {
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 tls.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,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,125 +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) 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 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,145 +0,0 @@
package trafficcontrol
import (
"io"
"net"
"sync"
"sync/atomic"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type Manager[U comparable] struct {
access sync.Mutex
users map[U]*Traffic
}
type Traffic struct {
Upload uint64
Download uint64
}
func NewManager[U comparable]() *Manager[U] {
return &Manager[U]{
users: make(map[U]*Traffic),
}
}
func (m *Manager[U]) Reset() {
m.users = make(map[U]*Traffic)
}
func (m *Manager[U]) TrackConnection(user U, conn net.Conn) net.Conn {
m.access.Lock()
defer m.access.Unlock()
var traffic *Traffic
if t, loaded := m.users[user]; loaded {
traffic = t
} else {
traffic = new(Traffic)
m.users[user] = traffic
}
return &TrackConn{conn, traffic}
}
func (m *Manager[U]) TrackPacketConnection(user U, conn N.PacketConn) N.PacketConn {
m.access.Lock()
defer m.access.Unlock()
var traffic *Traffic
if t, loaded := m.users[user]; loaded {
traffic = t
} else {
traffic = new(Traffic)
m.users[user] = traffic
}
return &TrackPacketConn{conn, traffic}
}
func (m *Manager[U]) ReadTraffics() map[U]Traffic {
m.access.Lock()
defer m.access.Unlock()
trafficMap := make(map[U]Traffic)
for user, traffic := range m.users {
upload := atomic.SwapUint64(&traffic.Upload, 0)
download := atomic.SwapUint64(&traffic.Download, 0)
if upload == 0 && download == 0 {
continue
}
trafficMap[user] = Traffic{
Upload: upload,
Download: download,
}
}
return trafficMap
}
type TrackConn struct {
net.Conn
*Traffic
}
func (c *TrackConn) Read(p []byte) (n int, err error) {
n, err = c.Conn.Read(p)
if n > 0 {
atomic.AddUint64(&c.Upload, uint64(n))
}
return
}
func (c *TrackConn) Write(p []byte) (n int, err error) {
n, err = c.Conn.Write(p)
if n > 0 {
atomic.AddUint64(&c.Download, uint64(n))
}
return
}
func (c *TrackConn) WriteTo(w io.Writer) (n int64, err error) {
n, err = bufio.Copy(w, c.Conn)
if n > 0 {
atomic.AddUint64(&c.Upload, uint64(n))
}
return
}
func (c *TrackConn) ReadFrom(r io.Reader) (n int64, err error) {
n, err = bufio.Copy(c.Conn, r)
if n > 0 {
atomic.AddUint64(&c.Download, uint64(n))
}
return
}
func (c *TrackConn) Upstream() any {
return c.Conn
}
type TrackPacketConn struct {
N.PacketConn
*Traffic
}
func (c *TrackPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
destination, err := c.PacketConn.ReadPacket(buffer)
if err == nil {
atomic.AddUint64(&c.Upload, uint64(buffer.Len()))
}
return destination, err
}
func (c *TrackPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
n := buffer.Len()
err := c.PacketConn.WritePacket(buffer, destination)
if err == nil {
atomic.AddUint64(&c.Download, uint64(n))
}
return err
}
func (c *TrackPacketConn) Upstream() any {
return c.PacketConn
}

View File

@@ -1,6 +1,6 @@
package constant package constant
var ( var (
Version = "1.1-beta2" Version = "1.0.7"
Commit = "" Commit = ""
) )

View File

@@ -1,71 +1,46 @@
#### 1.1-beta2 #### 1.0.7
* Add Clash mode and persistence support **1** * Add support for new x/h2 deadline
* Add TLS ECH and uTLS support for outbound TLS options **2** * Fix copy pipe
* Fix socks4 request * Fix decrypt xplus packet
* Fix processing empty dns result * Fix macOS Ventura process name match
* Fix smux keepalive
* Fix vmess request buffer
* Fix h2c transport
* Fix tor geoip
* Fix udp connect for mux client
* Fix default dns transport strategy
*1*: #### 1.0.6
Switching modes using the Clash API, and `store-selected` are now supported, * Fix ssh outbound
see [Experimental](/configuration/experimental). * Fix sniff fragmented quic client hello
* Fix naive overflow
* Check destination before udp connect
*2*: #### 1.0.5
ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello * Fix missing source address from transport connection
message, see [TLS#ECH](/configuration/shared/tls#ech). * Fix fqdn socks5 outbound connection
* Fix read source address from grpc-go
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance, #### 1.0.4
see [TLS#uTLS](/configuration/shared/tls#utls).
* Fix close grpc conn
* Fix port rule match logic
* Fix clash api proxy type
#### 1.0.3
* Fix socks4 client
* Fix hysteria inbound
* Fix concurrent write
#### 1.0.2 #### 1.0.2
* Fix socks4 request * Fix socks4 request
* Fix processing empty dns result * 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 #### 1.0.1
* Fix match 4in6 address in ip_cidr * Fix match 4in6 address in ip_cidr

View File

@@ -73,7 +73,6 @@
"user_id": [ "user_id": [
1000 1000
], ],
"clash_mode": "direct",
"invert": false, "invert": false,
"outbound": [ "outbound": [
"direct" "direct"
@@ -209,10 +208,6 @@ Match user name.
Match user id. Match user id.
#### clash_mode
Match Clash mode.
#### invert #### invert
Invert match result. Invert match result.

View File

@@ -72,7 +72,6 @@
"user_id": [ "user_id": [
1000 1000
], ],
"clash_mode": "direct",
"invert": false, "invert": false,
"outbound": [ "outbound": [
"direct" "direct"
@@ -208,10 +207,6 @@
匹配用户 ID。 匹配用户 ID。
#### clash_mode
匹配 Clash 模式。
#### invert #### invert
反选匹配结果。 反选匹配结果。

View File

@@ -8,10 +8,7 @@
"clash_api": { "clash_api": {
"external_controller": "127.0.0.1:9090", "external_controller": "127.0.0.1:9090",
"external_ui": "folder", "external_ui": "folder",
"secret": "", "secret": ""
"default_mode": "rule",
"store_selected": false,
"cache_file": "cache.db"
} }
} }
} }
@@ -29,7 +26,7 @@
#### external_controller #### external_controller
RESTful web API listening address. Clash API will be disabled if empty. RESTful web API listening address. Disabled if empty.
#### external_ui #### external_ui
@@ -41,22 +38,4 @@ serve it at `http://{{external-controller}}/ui`.
Secret for the RESTful API (optional) Secret for the RESTful API (optional)
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}` Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
ALWAYS set a secret if RESTful API is listening on 0.0.0.0 ALWAYS set a secret if RESTful API is listening on 0.0.0.0
#### 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.

View File

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

View File

@@ -11,8 +11,8 @@
"interface_name": "tun0", "interface_name": "tun0",
"inet4_address": "172.19.0.1/30", "inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126", "inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 9000, "mtu": 1500,
"auto_route": true, "auto_route": true,
"strict_route": true, "strict_route": true,
"endpoint_independent_nat": false, "endpoint_independent_nat": false,
@@ -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` 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 #### strict_route
Enforce strict routing rules in Linux when `auto_route` is enabled: Enforce strict routing rules in Linux when `auto_route` is enabled:
@@ -96,10 +92,6 @@ not be accessible by others.
#### endpoint_independent_nat #### 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. Enable endpoint-independent NAT.
Performance may degrade slightly, so it is not recommended to enable on when it is not needed. Performance may degrade slightly, so it is not recommended to enable on when it is not needed.
@@ -112,11 +104,10 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
TCP/IP stack. TCP/IP stack.
| Stack | Description | Status | | Stack | Upstream | Status |
|------------------|--------------------------------------------------------------------------------|-------------------| |------------------|-----------------------------------------------------------------------|-------------------|
| gVisor (default) | Based on [google/gvisor](https://github.com/google/gvisor) | recommended | | gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | recommended |
| system | Less compatibility and sometimes better performance. | recommended | | LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
!!! warning "" !!! warning ""

View File

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

View File

@@ -7,9 +7,8 @@
"server": "127.0.0.1", "server": "127.0.0.1",
"server_port": 1080, "server_port": 1080,
"system_interface": false,
"interface_name": "wg0",
"local_address": [ "local_address": [
"10.0.0.1",
"10.0.0.2/32" "10.0.0.2/32"
], ],
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=", "private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
@@ -40,21 +39,11 @@ The server address.
The server port. The server port.
#### system_interface
Use system tun support.
Requires privileges and cannot conflict with system interfaces.
#### interface_name
Custom device name when `system_interface` enabled.
#### local_address #### local_address
==Required== ==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 #### private_key

View File

@@ -7,9 +7,8 @@
"server": "127.0.0.1", "server": "127.0.0.1",
"server_port": 1080, "server_port": 1080,
"system_interface": false,
"interface_name": "wg0",
"local_address": [ "local_address": [
"10.0.0.1",
"10.0.0.2/32" "10.0.0.2/32"
], ],
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=", "private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
@@ -40,23 +39,13 @@
服务器端口。 服务器端口。
#### system_interface
使用系统 tun 支持。
需要特权且不能与系统接口冲突。
#### interface_name
启用 `system_interface` 时的自定义设备名称。
#### local_address #### local_address
==必填== ==必填==
接口的 IPv4/IPv6 地址或地址段的列表您。 接口的 IPv4/IPv6 地址或地址段的列表您。
要分配给接口的 IPv4 或 v6地址列表。 要分配给接口的 IPv4 或 v6地址列表(可以选择带有 CIDR 掩码)
#### private_key #### private_key

View File

@@ -10,7 +10,6 @@
"rules": [], "rules": [],
"final": "", "final": "",
"auto_detect_interface": false, "auto_detect_interface": false,
"override_android_vpn": false,
"default_interface": "en0", "default_interface": "en0",
"default_mark": 233 "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. 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. 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 #### default_interface
!!! error "" !!! error ""
Only supported on Linux, Windows and macOS. 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. Takes no effect if `auto_detect_interface` is set.

View File

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

View File

@@ -80,7 +80,6 @@
"user_id": [ "user_id": [
1000 1000
], ],
"clash_mode": "direct",
"invert": false, "invert": false,
"outbound": "direct" "outbound": "direct"
}, },
@@ -220,10 +219,6 @@ Match user name.
Match user id. Match user id.
#### clash_mode
Match Clash mode.
#### invert #### invert
Invert match result. Invert match result.

View File

@@ -78,7 +78,6 @@
"user_id": [ "user_id": [
1000 1000
], ],
"clash_mode": "direct",
"invert": false, "invert": false,
"outbound": "direct" "outbound": "direct"
}, },
@@ -218,10 +217,6 @@
匹配用户 ID。 匹配用户 ID。
#### clash_mode
匹配 Clash 模式。
#### invert #### invert
反选匹配结果。 反选匹配结果。

View File

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

View File

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

View File

@@ -5,27 +5,24 @@
"listen": "::", "listen": "::",
"listen_port": 5353, "listen_port": 5353,
"tcp_fast_open": false, "tcp_fast_open": false,
"udp_fragment": false,
"sniff": false, "sniff": false,
"sniff_override_destination": false, "sniff_override_destination": false,
"domain_strategy": "prefer_ipv6", "domain_strategy": "prefer_ipv6",
"udp_timeout": 300, "udp_timeout": 300,
"proxy_protocol": false, "proxy_protocol": false,
"proxy_protocol_accept_no_header": false,
"detour": "another-in" "detour": "another-in"
} }
``` ```
### Fields ### Fields
| Field | Available Context | | Field | Available Context |
|-----------------------------------|-------------------------------------------------------------------| |------------------|-------------------------------------------------------------------|
| `listen` | Needs to listen on TCP or UDP. | | `listen` | Needs to listen on TCP or UDP. |
| `listen_port` | Needs to listen on TCP or UDP. | | `listen_port` | Needs to listen on TCP or UDP. |
| `tcp_fast_open` | Needs to listen on TCP. | | `tcp_fast_open` | Needs to listen on TCP. |
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. | | `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
| `proxy_protocol` | Needs to listen on TCP. | | `proxy_protocol` | Needs to listen on TCP. |
| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled |
#### listen #### listen
@@ -39,11 +36,7 @@ Listen port.
#### tcp_fast_open #### tcp_fast_open
Enable TCP Fast Open. Enable tcp fast open for listener.
#### udp_fragment
Enable UDP fragmentation.
#### sniff #### 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. 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 #### detour
If set, connections will be forwarded to the specified inbound. If set, connections will be forwarded to the specified inbound.

View File

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

View File

@@ -30,6 +30,10 @@
} }
``` ```
!!! warning ""
ACME is not included by default, see [Installation](/#installation).
### Outbound ### Outbound
```json ```json
@@ -43,17 +47,7 @@
"max_version": "", "max_version": "",
"cipher_suites": [], "cipher_suites": [],
"certificate": "", "certificate": "",
"certificate_path": "", "certificate_path": ""
"ech": {
"enabled": false,
"pq_signature_schemes_enabled": false,
"dynamic_record_sizing_disabled": false,
"config": ""
},
"utls": {
"enabled": false,
"fingerprint": ""
}
} }
``` ```
@@ -161,48 +155,8 @@ The server private key, in PEM format.
The path to 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 ### ACME Fields
!!! warning ""
ACME is not included by default, see [Installation](/#installation).
#### domain #### domain
List of 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 The alternate port to use for the ACME TLS-ALPN challenge; the system must forward 443 to this port for challenge to
succeed. succeed.
### Reload
For server configuration, certificate and key will be automatically reloaded if modified.
#### external_account #### external_account
EAB (External Account Binding) contains information necessary to bind or map an ACME account to some other account known 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 #### external_account.mac_key
The MAC key. The MAC key.
### Reload
For server configuration, certificate and key will be automatically reloaded if modified.

View File

@@ -30,6 +30,10 @@
} }
``` ```
!!! warning ""
默认安装不包含 ACME参阅 [安装](/zh/#_2)。
### 出站 ### 出站
```json ```json
@@ -43,17 +47,7 @@
"max_version": "", "max_version": "",
"cipher_suites": [], "cipher_suites": [],
"certificate": "", "certificate": "",
"certificate_path": "", "certificate_path": ""
"ech": {
"enabled": false,
"pq_signature_schemes_enabled": false,
"dynamic_record_sizing_disabled": false,
"config": ""
},
"utls": {
"enabled": false,
"fingerprint": ""
}
} }
``` ```
@@ -161,47 +155,8 @@ TLS 版本值:
服务器 PEM 私钥路径。 服务器 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 字段 ### ACME 字段
!!! warning ""
默认安装不包含 ACME参阅 [安装](/zh/#_2)。
#### domain #### domain
一组域名。 一组域名。
@@ -248,6 +203,10 @@ ACME 数据目录。
用于 ACME TLS-ALPN 质询的备用端口; 系统必须将 443 转发到此端口以使质询成功。 用于 ACME TLS-ALPN 质询的备用端口; 系统必须将 443 转发到此端口以使质询成功。
### Reload
对于服务器配置,如果修改,证书和密钥将自动重新加载。
#### external_account #### external_account
EAB外部帐户绑定包含将 ACME 帐户绑定或映射到其他已知帐户所需的信息由 CA。 EAB外部帐户绑定包含将 ACME 帐户绑定或映射到其他已知帐户所需的信息由 CA。
@@ -263,8 +222,4 @@ EAB外部帐户绑定包含将 ACME 帐户绑定或映射到其他已知
#### external_account.mac_key #### external_account.mac_key
MAC 密钥。 MAC 密钥。
### 重载
对于服务器配置,如果修改,证书和密钥将自动重新加载。

View File

@@ -27,8 +27,6 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 dns transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). | | `with_quic` | Build with QUIC support, see [QUIC and HTTP3 dns transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). | | `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). | | `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
| `with_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_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). | | `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). | | `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |

View File

@@ -27,8 +27,6 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server)[Naive 入站](./configuration/inbound/naive)[Hysteria 入站](./configuration/inbound/hysteria)[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 | | `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server)[Naive 入站](./configuration/inbound/naive)[Hysteria 入站](./configuration/inbound/hysteria)[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 | | `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 | | `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
| `with_utls` | 启用 [uTLS](https://github.com/refraction-networking/utls) 支持, 参阅 [TLS](./configuration/shared/tls#utls)。 |
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 | | `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 | | `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 | | `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |

View File

@@ -10,5 +10,5 @@ import (
) )
func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { 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 ( import (
"net/http" "net/http"
"strings"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
@@ -10,11 +9,11 @@ import (
"github.com/go-chi/render" "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 := chi.NewRouter()
r.Get("/", getConfigs(server, logFactory)) r.Get("/", getConfigs(logFactory))
r.Put("/", updateConfigs) r.Put("/", updateConfigs)
r.Patch("/", patchConfigs(server, logger)) r.Patch("/", patchConfigs)
return r return r
} }
@@ -32,7 +31,7 @@ type configSchema struct {
Tun map[string]any `json:"tun"` 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) { return func(w http.ResponseWriter, r *http.Request) {
logLevel := logFactory.Level() logLevel := logFactory.Level()
if logLevel == log.LevelTrace { if logLevel == log.LevelTrace {
@@ -41,31 +40,15 @@ func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWrit
logLevel = log.LevelError logLevel = log.LevelError
} }
render.JSON(w, r, &configSchema{ render.JSON(w, r, &configSchema{
Mode: server.mode, Mode: "rule",
BindAddress: "*", BindAddress: "*",
LogLevel: log.FormatLevel(logLevel), LogLevel: log.FormatLevel(logLevel),
}) })
} }
} }
func patchConfigs(server *Server, logger log.Logger) func(w http.ResponseWriter, r *http.Request) { func patchConfigs(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { render.NoContent(w, r)
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 updateConfigs(w http.ResponseWriter, r *http.Request) { 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/common/json"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/websocket"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/gorilla/websocket"
) )
func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler { func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler {

View File

@@ -70,18 +70,26 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
case C.TypeSocks: case C.TypeSocks:
clashType = "Socks" clashType = "Socks"
case C.TypeHTTP: case C.TypeHTTP:
clashType = "Http" clashType = "HTTP"
case C.TypeShadowsocks: case C.TypeShadowsocks:
clashType = "Shadowsocks" clashType = "Shadowsocks"
case C.TypeVMess: case C.TypeVMess:
clashType = "Vmess" clashType = "VMess"
case C.TypeTrojan: case C.TypeTrojan:
clashType = "Trojan" clashType = "Trojan"
case C.TypeHysteria:
clashType = "Hysteria"
case C.TypeWireGuard:
clashType = "WireGuard"
case C.TypeTor:
clashType = "Tor"
case C.TypeSSH:
clashType = "SSH"
case C.TypeSelector: case C.TypeSelector:
clashType = "Selector" clashType = "Selector"
isGroup = true isGroup = true
default: default:
clashType = "Socks" clashType = "Direct"
} }
info.Put("type", clashType) info.Put("type", clashType)
info.Put("name", detour.Tag()) info.Put("name", detour.Tag())

View File

@@ -14,7 +14,6 @@ import (
"github.com/sagernet/sing-box/common/json" "github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing-box/common/urltest"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/clashapi/cachefile"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -22,11 +21,11 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/websocket"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/cors" "github.com/go-chi/cors"
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/gorilla/websocket"
) )
var _ adapter.ClashServer = (*Server)(nil) var _ adapter.ClashServer = (*Server)(nil)
@@ -38,12 +37,9 @@ type Server struct {
trafficManager *trafficontrol.Manager trafficManager *trafficontrol.Manager
urlTestHistory *urltest.HistoryStorage urlTestHistory *urltest.HistoryStorage
tcpListener net.Listener 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() trafficManager := trafficontrol.NewManager()
chiRouter := chi.NewRouter() chiRouter := chi.NewRouter()
server := &Server{ server := &Server{
@@ -55,21 +51,6 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
}, },
trafficManager: trafficManager, trafficManager: trafficManager,
urlTestHistory: urltest.NewHistoryStorage(), urlTestHistory: urltest.NewHistoryStorage(),
mode: strings.ToLower(options.DefaultMode),
}
if server.mode == "" {
server.mode = "rule"
}
if options.StoreSelected {
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{ cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, AllowedOrigins: []string{"*"},
@@ -84,7 +65,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
r.Get("/logs", getLogs(logFactory)) r.Get("/logs", getLogs(logFactory))
r.Get("/traffic", traffic(trafficManager)) r.Get("/traffic", traffic(trafficManager))
r.Get("/version", version) r.Get("/version", version)
r.Mount("/configs", configRouter(server, logFactory, server.logger)) r.Mount("/configs", configRouter(logFactory))
r.Mount("/proxies", proxyRouter(server, router)) r.Mount("/proxies", proxyRouter(server, router))
r.Mount("/rules", ruleRouter(router)) r.Mount("/rules", ruleRouter(router))
r.Mount("/connections", connectionRouter(trafficManager)) r.Mount("/connections", connectionRouter(trafficManager))
@@ -103,7 +84,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
}) })
}) })
} }
return server, nil return server
} }
func (s *Server) Start() error { func (s *Server) Start() error {
@@ -127,22 +108,9 @@ func (s *Server) Close() error {
common.PtrOrNil(s.httpServer), common.PtrOrNil(s.httpServer),
s.tcpListener, s.tcpListener,
s.trafficManager, 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) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) { func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) {
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule) tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule)
return tracker, tracker return tracker, tracker

32
go.mod
View File

@@ -4,7 +4,6 @@ go 1.18
require ( require (
berty.tech/go-libtor v1.0.385 berty.tech/go-libtor v1.0.385
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc
github.com/cretz/bine v0.2.0 github.com/cretz/bine v0.2.0
github.com/database64128/tfo-go v1.1.2 github.com/database64128/tfo-go v1.1.2
github.com/dustin/go-humanize v1.0.0 github.com/dustin/go-humanize v1.0.0
@@ -12,49 +11,44 @@ require (
github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2 github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.3.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/yamux v0.1.1 github.com/hashicorp/yamux v0.1.1
github.com/logrusorgru/aurora v2.0.3+incompatible github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.0.4 github.com/mholt/acmez v1.0.4
github.com/miekg/dns v1.1.50
github.com/oschwald/maxminddb-golang v1.10.0 github.com/oschwald/maxminddb-golang v1.10.0
github.com/pires/go-proxyproto v0.6.2 github.com/pires/go-proxyproto v0.6.2
github.com/refraction-networking/utls v1.1.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f github.com/sagernet/sing-vmess v0.0.0-20221121061945-608aaec6402b
github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a 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/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.10.0 go.uber.org/atomic v1.10.0
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 golang.org/x/net v0.2.0
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 golang.org/x/sys v0.2.0
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
google.golang.org/grpc v1.49.0 google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1 google.golang.org/protobuf v1.28.1
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
) )
//replace github.com/sagernet/sing => ../sing //replace github.com/sagernet/sing => ../sing
require ( require (
github.com/ajg/form v1.5.1 // indirect 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/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/libdns/libdns v0.2.1 // indirect github.com/libdns/libdns v0.2.1 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect github.com/marten-seemann/qpack v0.2.1 // indirect
@@ -65,16 +59,16 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // 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/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/spf13/pflag v1.0.5 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.22.0 // indirect go.uber.org/zap v1.22.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.4.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect golang.org/x/tools v0.1.12 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect

72
go.sum
View File

@@ -5,15 +5,11 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc/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-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/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -47,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-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 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 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.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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/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.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
@@ -77,16 +73,12 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 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.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 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
@@ -107,8 +99,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/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 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY= 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.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -129,8 +119,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/refraction-networking/utls v1.1.2 h1:a7GQauRt72VG+wtNm0lnrAaCGlyX47gEi1++dSsDBpw=
github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tSdX1+SqUB7rL0QnGqyg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34= github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
@@ -139,24 +127,26 @@ github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kx
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo= github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= 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 h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4= github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f h1:w1TJq7Lw3It35tDyMsZLtYz4T2msf1UK9JxC85L5+sk= github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ= github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY= github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM= 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 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb h1:/swVU2mgwDwZ9l67v1Sim1ias/ZmriGTxQLnMakPhtQ= github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM= 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-20221121061945-608aaec6402b h1:zG+tXOke49idJeqL8MmcWk7S8QlKH/W7AsgbEeBqUPc=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0= github.com/sagernet/sing-vmess v0.0.0-20221121061945-608aaec6402b/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a h1:GCNwsN8MEckpjGJjK3qjQBQ9qHsoXB9B/KHUWBvE1V4= github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8= 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 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= 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= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -173,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/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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 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.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.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
@@ -193,7 +181,6 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -224,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-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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-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-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -236,7 +221,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -249,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-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-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-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-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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -257,23 +240,23 @@ 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-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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-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= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -286,17 +269,16 @@ 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.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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/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.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
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= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ= golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg= golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -345,8 +327,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.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= 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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=

View File

@@ -3,7 +3,6 @@ package inbound
import ( import (
"context" "context"
"net" "net"
"sync"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/settings" "github.com/sagernet/sing-box/common/settings"
@@ -42,7 +41,6 @@ type myInboundAdapter struct {
tcpListener net.Listener tcpListener net.Listener
udpConn *net.UDPConn udpConn *net.UDPConn
udpAddr M.Socksaddr udpAddr M.Socksaddr
packetAccess sync.RWMutex
packetOutboundClosed chan struct{} packetOutboundClosed chan struct{}
packetOutbound chan *myInboundPacket packetOutbound chan *myInboundPacket
} }

View File

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

View File

@@ -10,7 +10,6 @@ import (
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -18,15 +17,11 @@ import (
func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) { func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) {
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort) bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
var lc net.ListenConfig udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
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())
if err != nil { if err != nil {
return nil, err return nil, err
} }
a.udpConn = udpConn.(*net.UDPConn) a.udpConn = udpConn
a.udpAddr = bindAddr a.udpAddr = bindAddr
a.logger.Info("udp server started at ", udpConn.LocalAddr()) a.logger.Info("udp server started at ", udpConn.LocalAddr())
return udpConn, err return udpConn, err
@@ -208,17 +203,12 @@ func (s *myInboundPacketAdapter) Upstream() any {
} }
func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { func (s *myInboundPacketAdapter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
s.packetAccess.RLock()
defer s.packetAccess.RUnlock()
select { select {
case s.packetOutbound <- &myInboundPacket{buffer, destination}:
return nil
case <-s.packetOutboundClosed: case <-s.packetOutboundClosed:
return os.ErrClosed return os.ErrClosed
default:
} }
s.packetOutbound <- &myInboundPacket{buffer, destination}
return nil
} }
func (s *myInboundPacketAdapter) Close() error { func (s *myInboundPacketAdapter) Close() error {

View File

@@ -3,11 +3,11 @@ package inbound
import ( import (
std_bufio "bufio" std_bufio "bufio"
"context" "context"
"crypto/tls"
"net" "net"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -26,7 +26,7 @@ var (
type HTTP struct { type HTTP struct {
myInboundAdapter myInboundAdapter
authenticator auth.Authenticator 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) { 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), authenticator: auth.NewAuthenticator(options.Users),
} }
if options.TLS != nil { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -67,13 +67,13 @@ func (h *HTTP) Start() error {
func (h *HTTP) Close() error { func (h *HTTP) Close() error {
return common.Close( return common.Close(
&h.myInboundAdapter, &h.myInboundAdapter,
h.tlsConfig, common.PtrOrNil(h.tlsConfig),
) )
} }
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.tlsConfig != nil { 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)) 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"
"github.com/sagernet/quic-go/congestion" "github.com/sagernet/quic-go/congestion"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -27,7 +26,7 @@ var _ adapter.Inbound = (*Hysteria)(nil)
type Hysteria struct { type Hysteria struct {
myInboundAdapter myInboundAdapter
quicConfig *quic.Config quicConfig *quic.Config
tlsConfig tls.ServerConfig tlsConfig *TLSConfig
authKey []byte authKey []byte
xplusKey []byte xplusKey []byte
sendBPS uint64 sendBPS uint64
@@ -117,7 +116,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
if len(options.TLS.ALPN) == 0 { if len(options.TLS.ALPN) == 0 {
options.TLS.ALPN = []string{hysteria.DefaultALPN} 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 { if err != nil {
return nil, err return nil, err
} }
@@ -138,11 +137,7 @@ func (h *Hysteria) Start() error {
if err != nil { if err != nil {
return err return err
} }
rawConfig, err := h.tlsConfig.Config() listener, err := quic.Listen(packetConn, h.tlsConfig.Config(), h.quicConfig)
if err != nil {
return err
}
listener, err := quic.Listen(packetConn, rawConfig, h.quicConfig)
if err != nil { if err != nil {
return err return err
} }
@@ -255,12 +250,6 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
if err != nil { if err != nil {
return err return err
} }
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
})
if err != nil {
return err
}
var metadata adapter.InboundContext var metadata adapter.InboundContext
metadata.Inbound = h.tag metadata.Inbound = h.tag
metadata.InboundType = C.TypeHysteria metadata.InboundType = C.TypeHysteria
@@ -270,7 +259,14 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()) metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr())
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()) metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr())
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port) metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port)
if !request.UDP { if !request.UDP {
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
})
if err != nil {
return err
}
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata) return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination), metadata)
} else { } else {
@@ -282,6 +278,13 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
h.udpSessions[id] = nCh h.udpSessions[id] = nCh
h.udpSessionId += 1 h.udpSessionId += 1
h.udpAccess.Unlock() h.udpAccess.Unlock()
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
OK: true,
UDPSessionID: id,
})
if err != nil {
return err
}
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error { packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
h.udpAccess.Lock() h.udpAccess.Lock()
if ch, ok := h.udpSessions[id]; ok { if ch, ok := h.udpSessions[id]; ok {
@@ -306,6 +309,6 @@ func (h *Hysteria) Close() error {
return common.Close( return common.Close(
&h.myInboundAdapter, &h.myInboundAdapter,
h.listener, h.listener,
h.tlsConfig, common.PtrOrNil(h.tlsConfig),
) )
} }

View File

@@ -2,6 +2,7 @@ package inbound
import ( import (
"context" "context"
"crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"io" "io"
@@ -13,7 +14,6 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -32,7 +32,7 @@ var _ adapter.Inbound = (*Naive)(nil)
type Naive struct { type Naive struct {
myInboundAdapter myInboundAdapter
authenticator auth.Authenticator authenticator auth.Authenticator
tlsConfig tls.ServerConfig tlsConfig *TLSConfig
httpServer *http.Server httpServer *http.Server
h3Server any h3Server any
} }
@@ -59,7 +59,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
return nil, E.New("missing users") return nil, E.New("missing users")
} }
if options.TLS != nil { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -69,16 +69,13 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
} }
func (n *Naive) Start() error { func (n *Naive) Start() error {
var tlsConfig *tls.STDConfig var tlsConfig *tls.Config
if n.tlsConfig != nil { if n.tlsConfig != nil {
err := n.tlsConfig.Start() err := n.tlsConfig.Start()
if err != nil { if err != nil {
return E.Cause(err, "create TLS config") return E.Cause(err, "create TLS config")
} }
tlsConfig, err = n.tlsConfig.Config() tlsConfig = n.tlsConfig.Config()
if err != nil {
return err
}
} }
if common.Contains(n.network, N.NetworkTCP) { if common.Contains(n.network, N.NetworkTCP) {
@@ -120,7 +117,7 @@ func (n *Naive) Close() error {
&n.myInboundAdapter, &n.myInboundAdapter,
common.PtrOrNil(n.httpServer), common.PtrOrNil(n.httpServer),
n.h3Server, n.h3Server,
n.tlsConfig, common.PtrOrNil(n.tlsConfig),
) )
} }
@@ -251,7 +248,14 @@ func (c *naiveH1Conn) read(p []byte) (n int, err error) {
c.paddingRemaining = 0 c.paddingRemaining = 0
} }
if c.readPadding < kFirstPaddings { if c.readPadding < kFirstPaddings {
paddingHdr := p[:3] var paddingHdr []byte
if len(p) >= 3 {
paddingHdr = p[:3]
} else {
_paddingHdr := make([]byte, 3)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err = io.ReadFull(c.Conn, paddingHdr) _, err = io.ReadFull(c.Conn, paddingHdr)
if err != nil { if err != nil {
return return
@@ -423,7 +427,14 @@ func (c *naiveH2Conn) read(p []byte) (n int, err error) {
c.paddingRemaining = 0 c.paddingRemaining = 0
} }
if c.readPadding < kFirstPaddings { if c.readPadding < kFirstPaddings {
paddingHdr := p[:3] var paddingHdr []byte
if len(p) >= 3 {
paddingHdr = p[:3]
} else {
_paddingHdr := make([]byte, 3)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err = io.ReadFull(c.reader, paddingHdr) _, err = io.ReadFull(c.reader, paddingHdr)
if err != nil { if err != nil {
return return

View File

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

View File

@@ -1,94 +0,0 @@
package inbound
import (
"encoding/json"
"io"
"net/http"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)
func (h *ShadowsocksMulti) createHandler() http.Handler {
router := chi.NewRouter()
router.Get("/", h.handleHello)
router.Put("/users", h.handleUpdateUsers)
router.Get("/traffics", h.handleReadTraffics)
return router
}
func (h *ShadowsocksMulti) handleHello(writer http.ResponseWriter, request *http.Request) {
render.JSON(writer, request, render.M{
"server": "sing-box",
"version": C.Version,
})
}
func (h *ShadowsocksMulti) handleUpdateUsers(writer http.ResponseWriter, request *http.Request) {
var users []option.ShadowsocksUser
err := readRequest(request, &users)
if err != nil {
h.newError(E.Cause(err, "controller: update users: parse request"))
writer.WriteHeader(http.StatusBadRequest)
writer.Write([]byte(F.ToString(err)))
return
}
users = append([]option.ShadowsocksUser{{
Name: "control",
Password: h.users[0].Password,
}}, users...)
err = h.service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user option.ShadowsocksUser) int {
return index
}), common.Map(users, func(user option.ShadowsocksUser) string {
return user.Password
}))
if err != nil {
h.newError(E.Cause(err, "controller: update users"))
writer.WriteHeader(http.StatusBadRequest)
writer.Write([]byte(F.ToString(err)))
return
}
h.users = users
h.trafficManager.Reset()
writer.WriteHeader(http.StatusNoContent)
h.logger.Info("controller: updated ", len(users)-1, " users")
}
type ShadowsocksUserTraffic struct {
Name string `json:"name,omitempty"`
Upload uint64 `json:"upload,omitempty"`
Download uint64 `json:"download,omitempty"`
}
func (h *ShadowsocksMulti) handleReadTraffics(writer http.ResponseWriter, request *http.Request) {
h.logger.Debug("controller: traffics sent")
trafficMap := h.trafficManager.ReadTraffics()
if len(trafficMap) == 0 {
writer.WriteHeader(http.StatusNoContent)
return
}
traffics := make([]ShadowsocksUserTraffic, 0, len(trafficMap))
for user, traffic := range trafficMap {
traffics = append(traffics, ShadowsocksUserTraffic{
Name: h.users[user].Name,
Upload: traffic.Upload,
Download: traffic.Download,
})
}
render.JSON(writer, request, traffics)
}
func readRequest(request *http.Request, v any) error {
defer request.Body.Close()
content, err := io.ReadAll(request.Body)
if err != nil {
return err
}
return json.Unmarshal(content, v)
}

View File

@@ -3,12 +3,9 @@ package inbound
import ( import (
"context" "context"
"net" "net"
"net/http"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/pipelistener"
"github.com/sagernet/sing-box/common/trafficcontrol"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -16,7 +13,6 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@@ -28,12 +24,8 @@ var (
type ShadowsocksMulti struct { type ShadowsocksMulti struct {
myInboundAdapter myInboundAdapter
service *shadowaead_2022.MultiService[int] service *shadowaead_2022.MultiService[int]
users []option.ShadowsocksUser users []option.ShadowsocksUser
controlEnabled bool
controller *http.Server
controllerPipe *pipelistener.Listener
trafficManager *trafficcontrol.Manager[int]
} }
func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) { func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) {
@@ -62,20 +54,7 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
udpTimeout, udpTimeout,
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
) )
users := options.Users err = service.UpdateUsersWithPasswords(common.MapIndexed(options.Users, func(index int, user option.ShadowsocksUser) int {
if options.ControlPassword != "" {
inbound.controlEnabled = true
users = append([]option.ShadowsocksUser{{
Name: "control",
Password: options.ControlPassword,
}}, users...)
inbound.controller = &http.Server{Handler: inbound.createHandler()}
inbound.trafficManager = trafficcontrol.NewManager[int]()
}
if err != nil {
return nil, err
}
err = service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user option.ShadowsocksUser) int {
return index return index
}), common.Map(options.Users, func(user option.ShadowsocksUser) string { }), common.Map(options.Users, func(user option.ShadowsocksUser) string {
return user.Password return user.Password
@@ -85,30 +64,10 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
} }
inbound.service = service inbound.service = service
inbound.packetUpstream = service inbound.packetUpstream = service
inbound.users = users inbound.users = options.Users
return inbound, err return inbound, err
} }
func (h *ShadowsocksMulti) Start() error {
if h.controlEnabled {
h.controllerPipe = pipelistener.New(16)
go func() {
err := h.controller.Serve(h.controllerPipe)
if err != nil {
h.newError(E.Cause(err, "controller serve error"))
}
}()
}
return h.myInboundAdapter.Start()
}
func (h *ShadowsocksMulti) Close() error {
if h.controlEnabled {
h.controllerPipe.Close()
}
return h.myInboundAdapter.Close()
}
func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
} }
@@ -126,11 +85,6 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met
if !loaded { if !loaded {
return os.ErrInvalid return os.ErrInvalid
} }
if userIndex == 0 && h.controlEnabled {
h.logger.InfoContext(ctx, "inbound control connection")
h.controllerPipe.Serve(conn)
return nil
}
user := h.users[userIndex].Name user := h.users[userIndex].Name
if user == "" { if user == "" {
user = F.ToString(userIndex) user = F.ToString(userIndex)

View File

@@ -1,9 +1,8 @@
package tls package inbound
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"net"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -15,7 +14,9 @@ import (
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
) )
type STDServerConfig struct { var _ adapter.Service = (*TLSConfig)(nil)
type TLSConfig struct {
config *tls.Config config *tls.Config
logger log.Logger logger log.Logger
acmeService adapter.Service acmeService adapter.Service
@@ -26,7 +27,105 @@ type STDServerConfig struct {
watcher *fsnotify.Watcher watcher *fsnotify.Watcher
} }
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { func (c *TLSConfig) Config() *tls.Config {
return c.config
}
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
}
}
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 (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 { if !options.Enabled {
return nil, nil return nil, nil
} }
@@ -48,14 +147,14 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...) tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...)
} }
if options.MinVersion != "" { if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion) minVersion, err := option.ParseTLSVersion(options.MinVersion)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse min_version") return nil, E.Cause(err, "parse min_version")
} }
tlsConfig.MinVersion = minVersion tlsConfig.MinVersion = minVersion
} }
if options.MaxVersion != "" { if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion) maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse max_version") return nil, E.Cause(err, "parse max_version")
} }
@@ -106,7 +205,7 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
} }
tlsConfig.Certificates = []tls.Certificate{keyPair} tlsConfig.Certificates = []tls.Certificate{keyPair}
} }
return &STDServerConfig{ return &TLSConfig{
config: tlsConfig, config: tlsConfig,
logger: logger, logger: logger,
acmeService: acmeService, acmeService: acmeService,
@@ -116,109 +215,3 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
keyPath: options.KeyPath, keyPath: options.KeyPath,
}, nil }, 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,6 +1,6 @@
//go:build with_acme //go:build with_acme
package tls package inbound
import ( import (
"context" "context"

View File

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

View File

@@ -2,11 +2,11 @@ package inbound
import ( import (
"context" "context"
"crypto/tls"
"net" "net"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -29,7 +29,7 @@ type Trojan struct {
myInboundAdapter myInboundAdapter
service *trojan.Service[int] service *trojan.Service[int]
users []option.TrojanUser users []option.TrojanUser
tlsConfig tls.ServerConfig tlsConfig *TLSConfig
fallbackAddr M.Socksaddr fallbackAddr M.Socksaddr
fallbackAddrTLSNextProto map[string]M.Socksaddr fallbackAddrTLSNextProto map[string]M.Socksaddr
transport adapter.V2RayServerTransport transport adapter.V2RayServerTransport
@@ -49,7 +49,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
users: options.Users, users: options.Users,
} }
if options.TLS != nil { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -89,7 +89,11 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
return nil, err return nil, err
} }
if options.Transport != nil { 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 { if err != nil {
return nil, E.Cause(err, "create server transport: ", options.Transport.Type) 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 { func (h *Trojan) Close() error {
return common.Close( return common.Close(
&h.myInboundAdapter, &h.myInboundAdapter,
h.tlsConfig, common.PtrOrNil(h.tlsConfig),
h.transport, 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 { func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if h.tlsConfig != nil && h.transport == nil { 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)) 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 { func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
var fallbackAddr M.Socksaddr var fallbackAddr M.Socksaddr
if len(h.fallbackAddrTLSNextProto) > 0 { 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() connectionState := tlsConn.ConnectionState()
if connectionState.NegotiatedProtocol != "" { if connectionState.NegotiatedProtocol != "" {
if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded { 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) { func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
tunName := options.InterfaceName tunName := options.InterfaceName
if tunName == "" { if tunName == "" {
tunName = tun.CalculateInterfaceName("") tunName = tun.DefaultInterfaceName()
} }
tunMTU := options.MTU tunMTU := options.MTU
if tunMTU == 0 { if tunMTU == 0 {
tunMTU = 9000 tunMTU = 1500
} }
var udpTimeout int64 var udpTimeout int64
if options.UDPTimeout != 0 { if options.UDPTimeout != 0 {
@@ -77,8 +77,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
tunOptions: tun.Options{ tunOptions: tun.Options{
Name: tunName, Name: tunName,
MTU: tunMTU, MTU: tunMTU,
Inet4Address: common.Map(options.Inet4Address, option.ListenPrefix.Build), Inet4Address: options.Inet4Address.Build(),
Inet6Address: common.Map(options.Inet6Address, option.ListenPrefix.Build), Inet6Address: options.Inet6Address.Build(),
AutoRoute: options.AutoRoute, AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute, StrictRoute: options.StrictRoute,
IncludeUID: includeUID, IncludeUID: includeUID,
@@ -86,8 +86,6 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
IncludeAndroidUser: options.IncludeAndroidUser, IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage, IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage, ExcludePackage: options.ExcludePackage,
InterfaceMonitor: router.InterfaceMonitor(),
TableIndex: 2022,
}, },
endpointIndependentNat: options.EndpointIndependentNat, endpointIndependentNat: options.EndpointIndependentNat,
udpTimeout: udpTimeout, udpTimeout: udpTimeout,
@@ -144,18 +142,7 @@ func (t *Tun) Start() error {
return E.Cause(err, "configure tun interface") return E.Cause(err, "configure tun interface")
} }
t.tunIf = tunIf t.tunIf = tunIf
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{ t.tunStack, err = tun.NewStack(t.ctx, t.stack, tunIf, t.tunOptions.MTU, t.endpointIndependentNat, t.udpTimeout, t)
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,
})
if err != nil { if err != nil {
return err return err
} }

View File

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

View File

@@ -4,10 +4,6 @@ type ClashAPIOptions struct {
ExternalController string `json:"external_controller,omitempty"` ExternalController string `json:"external_controller,omitempty"`
ExternalUI string `json:"external_ui,omitempty"` ExternalUI string `json:"external_ui,omitempty"`
Secret string `json:"secret,omitempty"` Secret string `json:"secret,omitempty"`
DefaultMode string `json:"default_mode,omitempty"`
StoreSelected bool `json:"store_selected,omitempty"`
CacheFile string `json:"cache_file,omitempty"`
} }
type SelectorOutboundOptions struct { type SelectorOutboundOptions struct {

View File

@@ -20,7 +20,7 @@ type _Options struct {
type Options _Options type Options _Options
func (o *Options) UnmarshalJSON(content []byte) error { func (o *Options) UnmarshalJSON(content []byte) error {
decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content))) decoder := json.NewDecoder(bytes.NewReader(content))
decoder.DisallowUnknownFields() decoder.DisallowUnknownFields()
err := decoder.Decode((*_Options)(o)) err := decoder.Decode((*_Options)(o))
if err == nil { if err == nil {

View File

@@ -99,7 +99,6 @@ type DefaultDNSRule struct {
User Listable[string] `json:"user,omitempty"` User Listable[string] `json:"user,omitempty"`
UserID Listable[int32] `json:"user_id,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"`
Outbound Listable[string] `json:"outbound,omitempty"` Outbound Listable[string] `json:"outbound,omitempty"`
ClashMode string `json:"clash_mode,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"` Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"` DisableCache bool `json:"disable_cache,omitempty"`

View File

@@ -111,13 +111,11 @@ type InboundOptions struct {
} }
type ListenOptions struct { type ListenOptions struct {
Listen ListenAddress `json:"listen"` Listen ListenAddress `json:"listen"`
ListenPort uint16 `json:"listen_port,omitempty"` ListenPort uint16 `json:"listen_port,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPFragment bool `json:"udp_fragment,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"` ProxyProtocol bool `json:"proxy_protocol,omitempty"`
ProxyProtocol bool `json:"proxy_protocol,omitempty"` Detour string `json:"detour,omitempty"`
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
Detour string `json:"detour,omitempty"`
InboundOptions InboundOptions
} }

View File

@@ -113,7 +113,6 @@ type DialerOptions struct {
ReuseAddr bool `json:"reuse_addr,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"` ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPFragment bool `json:"udp_fragment,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
FallbackDelay Duration `json:"fallback_delay,omitempty"` FallbackDelay Duration `json:"fallback_delay,omitempty"`
} }

View File

@@ -16,7 +16,6 @@ type RouteOptions struct {
Final string `json:"final,omitempty"` Final string `json:"final,omitempty"`
FindProcess bool `json:"find_process,omitempty"` FindProcess bool `json:"find_process,omitempty"`
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"` AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
DefaultInterface string `json:"default_interface,omitempty"` DefaultInterface string `json:"default_interface,omitempty"`
DefaultMark int `json:"default_mark,omitempty"` DefaultMark int `json:"default_mark,omitempty"`
} }
@@ -101,7 +100,6 @@ type DefaultRule struct {
PackageName Listable[string] `json:"package_name,omitempty"` PackageName Listable[string] `json:"package_name,omitempty"`
User Listable[string] `json:"user,omitempty"` User Listable[string] `json:"user,omitempty"`
UserID Listable[int32] `json:"user_id,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"`
ClashMode string `json:"clash_mode,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Outbound string `json:"outbound,omitempty"` Outbound string `json:"outbound,omitempty"`
} }

View File

@@ -1,5 +1,11 @@
package option package option
import (
"crypto/tls"
E "github.com/sagernet/sing/common/exceptions"
)
type InboundTLSOptions struct { type InboundTLSOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
ServerName string `json:"server_name,omitempty"` ServerName string `json:"server_name,omitempty"`
@@ -15,28 +21,29 @@ type InboundTLSOptions struct {
} }
type OutboundTLSOptions struct { type OutboundTLSOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
DisableSNI bool `json:"disable_sni,omitempty"` DisableSNI bool `json:"disable_sni,omitempty"`
ServerName string `json:"server_name,omitempty"` ServerName string `json:"server_name,omitempty"`
Insecure bool `json:"insecure,omitempty"` Insecure bool `json:"insecure,omitempty"`
ALPN Listable[string] `json:"alpn,omitempty"` ALPN Listable[string] `json:"alpn,omitempty"`
MinVersion string `json:"min_version,omitempty"` MinVersion string `json:"min_version,omitempty"`
MaxVersion string `json:"max_version,omitempty"` MaxVersion string `json:"max_version,omitempty"`
CipherSuites Listable[string] `json:"cipher_suites,omitempty"` CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
Certificate string `json:"certificate,omitempty"` Certificate string `json:"certificate,omitempty"`
CertificatePath string `json:"certificate_path,omitempty"` CertificatePath string `json:"certificate_path,omitempty"`
ECH *OutboundECHOptions `json:"ech,omitempty"`
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
} }
type OutboundECHOptions struct { func ParseTLSVersion(version string) (uint16, error) {
Enabled bool `json:"enabled,omitempty"` switch version {
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` case "1.0":
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` return tls.VersionTLS10, nil
Config string `json:"config,omitempty"` case "1.1":
} return tls.VersionTLS11, nil
case "1.2":
type OutboundUTLSOptions struct { return tls.VersionTLS12, nil
Enabled bool `json:"enabled,omitempty"` case "1.3":
Fingerprint string `json:"fingerprint,omitempty"` return tls.VersionTLS13, nil
default:
return 0, E.New("unknown tls version:", version)
}
} }

View File

@@ -1,21 +1,21 @@
package option package option
type TunInboundOptions struct { type TunInboundOptions struct {
InterfaceName string `json:"interface_name,omitempty"` InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"` MTU uint32 `json:"mtu,omitempty"`
Inet4Address Listable[ListenPrefix] `json:"inet4_address,omitempty"` Inet4Address *ListenPrefix `json:"inet4_address,omitempty"`
Inet6Address Listable[ListenPrefix] `json:"inet6_address,omitempty"` Inet6Address *ListenPrefix `json:"inet6_address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"` AutoRoute bool `json:"auto_route,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"` StrictRoute bool `json:"strict_route,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"` IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"` IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"` ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"` ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"` IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
IncludePackage Listable[string] `json:"include_package,omitempty"` IncludePackage Listable[string] `json:"include_package,omitempty"`
ExcludePackage Listable[string] `json:"exclude_package,omitempty"` ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"` Stack string `json:"stack,omitempty"`
InboundOptions InboundOptions
} }

View File

@@ -184,6 +184,9 @@ func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
return nil return nil
} }
func (p ListenPrefix) Build() netip.Prefix { func (p *ListenPrefix) Build() netip.Prefix {
return netip.Prefix(p) if p == nil {
return netip.Prefix{}
}
return netip.Prefix(*p)
} }

View File

@@ -3,12 +3,10 @@ package option
type WireGuardOutboundOptions struct { type WireGuardOutboundOptions struct {
DialerOptions DialerOptions
ServerOptions ServerOptions
SystemInterface bool `json:"system_interface,omitempty"` LocalAddress Listable[string] `json:"local_address"`
InterfaceName string `json:"interface_name,omitempty"` PrivateKey string `json:"private_key"`
LocalAddress Listable[ListenPrefix] `json:"local_address"` PeerPublicKey string `json:"peer_public_key"`
PrivateKey string `json:"private_key"` PreSharedKey string `json:"pre_shared_key,omitempty"`
PeerPublicKey string `json:"peer_public_key"` MTU uint32 `json:"mtu,omitempty"`
PreSharedKey string `json:"pre_shared_key,omitempty"` Network NetworkList `json:"network,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Network NetworkList `json:"network,omitempty"`
} }

View File

@@ -3,6 +3,7 @@ package outbound
import ( import (
"context" "context"
"net" "net"
"os"
"runtime" "runtime"
"time" "time"
@@ -79,7 +80,9 @@ func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metad
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error { func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
switch metadata.Protocol { switch metadata.Protocol {
case C.ProtocolQUIC, C.ProtocolDNS: case C.ProtocolQUIC, C.ProtocolDNS:
return connectPacketConnection(ctx, this, conn, metadata) if !metadata.Destination.Addr.IsUnspecified() {
return connectPacketConnection(ctx, this, conn, metadata)
}
} }
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
var outConn net.PacketConn var outConn net.PacketConn
@@ -134,22 +137,25 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
_payload := buf.StackNew() _payload := buf.StackNew()
payload := common.Dup(_payload) payload := common.Dup(_payload)
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout)) err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
if err != nil { if err != os.ErrInvalid {
return err if err != nil {
} return err
_, err = payload.ReadOnceFrom(conn) }
if err != nil && !E.IsTimeout(err) { _, err = payload.ReadOnceFrom(conn)
return E.Cause(err, "read payload") if err != nil && !E.IsTimeout(err) {
} return E.Cause(err, "read payload")
err = conn.SetReadDeadline(time.Time{}) }
if err != nil { err = conn.SetReadDeadline(time.Time{})
payload.Release() if err != nil {
return err payload.Release()
return err
}
} }
_, err = serverConn.Write(payload.Bytes()) _, err = serverConn.Write(payload.Bytes())
if err != nil { if err != nil {
return N.HandshakeFailure(conn, err) return N.HandshakeFailure(conn, err)
} }
runtime.KeepAlive(_payload) runtime.KeepAlive(_payload)
payload.Release()
return bufio.CopyConn(ctx, conn, serverConn) return bufio.CopyConn(ctx, conn, serverConn)
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -25,7 +24,7 @@ type HTTP struct {
} }
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) { func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
detour, err := tls.NewDialerFromOptions(router, dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS)) detour, err := dialer.NewTLS(dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -4,6 +4,7 @@ package outbound
import ( import (
"context" "context"
"crypto/tls"
"net" "net"
"sync" "sync"
@@ -11,7 +12,6 @@ import (
"github.com/sagernet/quic-go/congestion" "github.com/sagernet/quic-go/congestion"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -30,7 +30,7 @@ type Hysteria struct {
ctx context.Context ctx context.Context
dialer N.Dialer dialer N.Dialer
serverAddr M.Socksaddr serverAddr M.Socksaddr
tlsConfig *tls.STDConfig tlsConfig *tls.Config
quicConfig *quic.Config quicConfig *quic.Config
authKey []byte authKey []byte
xplusKey []byte xplusKey []byte
@@ -47,11 +47,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
if options.TLS == nil || !options.TLS.Enabled { if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired return nil, C.ErrTLSRequired
} }
abstractTLSConfig, err := tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) tlsConfig, err := dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
tlsConfig, err := abstractTLSConfig.Config()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -59,30 +59,15 @@ func (s *Selector) Start() error {
} }
s.outbounds[tag] = detour s.outbounds[tag] = detour
} }
if s.tag != "" {
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
selected := clashServer.CacheFile().LoadSelected(s.tag)
if selected != "" {
detour, loaded := s.outbounds[selected]
if loaded {
s.selected = detour
return nil
}
}
}
}
if s.defaultTag != "" { if s.defaultTag != "" {
detour, loaded := s.outbounds[s.defaultTag] detour, loaded := s.outbounds[s.defaultTag]
if !loaded { if !loaded {
return E.New("default outbound not found: ", s.defaultTag) return E.New("default outbound not found: ", s.defaultTag)
} }
s.selected = detour s.selected = detour
return nil } else {
s.selected = s.outbounds[s.tags[0]]
} }
s.selected = s.outbounds[s.tags[0]]
return nil return nil
} }
@@ -100,14 +85,6 @@ func (s *Selector) SelectOutbound(tag string) bool {
return false return false
} }
s.selected = detour s.selected = detour
if s.tag != "" {
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
err := clashServer.CacheFile().StoreSelected(s.tag, tag)
if err != nil {
s.logger.Error("store selected: ", err)
}
}
}
return true return true
} }

View File

@@ -2,12 +2,12 @@ package outbound
import ( import (
"context" "context"
"crypto/tls"
"net" "net"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -22,7 +22,7 @@ type ShadowTLS struct {
myOutboundAdapter myOutboundAdapter
dialer N.Dialer dialer N.Dialer
serverAddr M.Socksaddr serverAddr M.Socksaddr
tlsConfig tls.Config tlsConfig *tls.Config
} }
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) { func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) {
@@ -43,7 +43,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
options.TLS.MinVersion = "1.2" options.TLS.MinVersion = "1.2"
options.TLS.MaxVersion = "1.2" options.TLS.MaxVersion = "1.2"
var err error var err error
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -60,7 +60,15 @@ func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination
if err != nil { if err != nil {
return nil, err return nil, err
} }
return tls.ClientHandshake(ctx, conn, s.tlsConfig) tlsConn, err := dialer.TLSClient(ctx, conn, s.tlsConfig)
if err != nil {
return nil, err
}
err = tlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err
}
return conn, nil
} }
func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {

View File

@@ -20,8 +20,9 @@ var _ adapter.Outbound = (*Socks)(nil)
type Socks struct { type Socks struct {
myOutboundAdapter myOutboundAdapter
client *socks.Client client *socks.Client
uot bool resolve bool
uot bool
} }
func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) { func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) {
@@ -45,6 +46,7 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio
tag: tag, tag: tag,
}, },
socks.NewClient(detour, options.ServerOptions.Build(), version, options.Username, options.Password), socks.NewClient(detour, options.ServerOptions.Build(), version, options.Username, options.Password),
version == socks.Version4,
options.UoT, options.UoT,
}, nil }, nil
} }
@@ -72,6 +74,13 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
default: default:
return nil, E.Extend(N.ErrUnknownNetwork, network) return nil, E.Extend(N.ErrUnknownNetwork, network)
} }
if h.resolve && destination.IsFqdn() {
addrs, err := h.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err
}
return N.DialSerial(ctx, h.client, network, destination, addrs)
}
return h.client.DialContext(ctx, network, destination) return h.client.DialContext(ctx, network, destination)
} }

View File

@@ -122,6 +122,9 @@ func (s *SSH) connect() (*ssh.Client, error) {
Auth: s.authMethod, Auth: s.authMethod,
ClientVersion: s.clientVersion, ClientVersion: s.clientVersion,
HostKeyAlgorithms: s.hostKeyAlgorithms, HostKeyAlgorithms: s.hostKeyAlgorithms,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
} }
clientConn, chans, reqs, err := ssh.NewClientConn(conn, s.serverAddr.Addr.String(), config) clientConn, chans, reqs, err := ssh.NewClientConn(conn, s.serverAddr.Addr.String(), config)
if err != nil { if err != nil {

View File

@@ -41,9 +41,18 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
startConf := newConfig() startConf := newConfig()
startConf.DataDir = os.ExpandEnv(options.DataDirectory) startConf.DataDir = os.ExpandEnv(options.DataDirectory)
startConf.TempDataDirBase = os.TempDir() startConf.TempDataDirBase = os.TempDir()
startConf.ExtraArgs = options.ExtraArgs
if options.DataDirectory != "" {
dataDirAbs, _ := filepath.Abs(startConf.DataDir)
if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.FileExists(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
}
if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.FileExists(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
}
}
if options.ExecutablePath != "" { if options.ExecutablePath != "" {
startConf.ExePath = options.ExecutablePath startConf.ExePath = options.ExecutablePath
startConf.ExtraArgs = options.ExtraArgs
startConf.ProcessCreator = nil startConf.ProcessCreator = nil
startConf.UseEmbeddedControlConn = false startConf.UseEmbeddedControlConn = false
} }

View File

@@ -2,12 +2,12 @@ package outbound
import ( import (
"context" "context"
"crypto/tls"
"net" "net"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/mux"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -28,7 +28,7 @@ type Trojan struct {
serverAddr M.Socksaddr serverAddr M.Socksaddr
key [56]byte key [56]byte
multiplexDialer N.Dialer multiplexDialer N.Dialer
tlsConfig tls.Config tlsConfig *tls.Config
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
} }
@@ -47,7 +47,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
} }
var err error var err error
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -116,7 +116,7 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
if err == nil && h.tlsConfig != nil { if err == nil && h.tlsConfig != nil {
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
} }
} }
if err != nil { if err != nil {

View File

@@ -2,12 +2,12 @@ package outbound
import ( import (
"context" "context"
"crypto/tls"
"net" "net"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/mux"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -28,7 +28,7 @@ type VMess struct {
client *vmess.Client client *vmess.Client
serverAddr M.Socksaddr serverAddr M.Socksaddr
multiplexDialer N.Dialer multiplexDialer N.Dialer
tlsConfig tls.Config tlsConfig *tls.Config
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
packetAddr bool packetAddr bool
} }
@@ -47,7 +47,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
} }
var err error var err error
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -142,7 +142,7 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
if err == nil && h.tlsConfig != nil { if err == nil && h.tlsConfig != nil {
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
} }
} }
if err != nil { if err != nil {
@@ -169,7 +169,7 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
} else { } else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
if err == nil && h.tlsConfig != nil { if err == nil && h.tlsConfig != nil {
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig) conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
} }
} }
if err != nil { if err != nil {

View File

@@ -8,30 +8,49 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"net" "net"
"net/netip"
"os"
"strings" "strings"
"sync"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/wireguard"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug" "github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
) )
var _ adapter.Outbound = (*WireGuard)(nil) var _ adapter.Outbound = (*WireGuard)(nil)
type WireGuard struct { type WireGuard struct {
myOutboundAdapter myOutboundAdapter
bind *wireguard.ClientBind ctx context.Context
device *device.Device serverAddr M.Socksaddr
tunDevice wireguard.Device dialer N.Dialer
endpoint conn.Endpoint
device *device.Device
tunDevice *wireTunDevice
connAccess sync.Mutex
conn *wireConn
} }
func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) { func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) {
@@ -43,13 +62,39 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
logger: logger, logger: logger,
tag: tag, tag: tag,
}, },
ctx: ctx,
serverAddr: options.ServerOptions.Build(),
dialer: dialer.New(router, options.DialerOptions),
} }
peerAddr := options.ServerOptions.Build() var endpointIp netip.Addr
outbound.bind = wireguard.NewClientBind(ctx, dialer.New(router, options.DialerOptions), peerAddr) if !outbound.serverAddr.IsFqdn() {
localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build) endpointIp = outbound.serverAddr.Addr
if len(localPrefixes) == 0 { } else {
endpointIp = netip.AddrFrom4([4]byte{127, 0, 0, 1})
}
outbound.endpoint = conn.StdNetEndpoint(netip.AddrPortFrom(endpointIp, outbound.serverAddr.Port))
localAddress := make([]tcpip.AddressWithPrefix, len(options.LocalAddress))
if len(localAddress) == 0 {
return nil, E.New("missing local address") return nil, E.New("missing local address")
} }
for index, address := range options.LocalAddress {
if strings.Contains(address, "/") {
prefix, err := netip.ParsePrefix(address)
if err != nil {
return nil, E.Cause(err, "parse local address prefix ", address)
}
localAddress[index] = tcpip.AddressWithPrefix{
Address: tcpip.Address(prefix.Addr().AsSlice()),
PrefixLen: prefix.Bits(),
}
} else {
addr, err := netip.ParseAddr(address)
if err != nil {
return nil, E.Cause(err, "parse local address ", address)
}
localAddress[index] = tcpip.Address(addr.AsSlice()).WithPrefix()
}
}
var privateKey, peerPublicKey, preSharedKey string var privateKey, peerPublicKey, preSharedKey string
{ {
bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey) bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey)
@@ -74,13 +119,13 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
} }
ipcConf := "private_key=" + privateKey ipcConf := "private_key=" + privateKey
ipcConf += "\npublic_key=" + peerPublicKey ipcConf += "\npublic_key=" + peerPublicKey
ipcConf += "\nendpoint=" + peerAddr.String() ipcConf += "\nendpoint=" + outbound.endpoint.DstToString()
if preSharedKey != "" { if preSharedKey != "" {
ipcConf += "\npreshared_key=" + preSharedKey ipcConf += "\npreshared_key=" + preSharedKey
} }
var has4, has6 bool var has4, has6 bool
for _, address := range localPrefixes { for _, address := range localAddress {
if address.Addr().Is4() { if address.Address.To4() != "" {
has4 = true has4 = true
} else { } else {
has6 = true has6 = true
@@ -96,17 +141,11 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
if mtu == 0 { if mtu == 0 {
mtu = 1408 mtu = 1408
} }
var wireTunDevice wireguard.Device wireDevice, err := newWireDevice(localAddress, mtu)
var err error
if !options.SystemInterface {
wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu)
} else {
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, localPrefixes, mtu)
}
if err != nil { if err != nil {
return nil, E.Cause(err, "create WireGuard device") return nil, err
} }
wgDevice := device.NewDevice(wireTunDevice, outbound.bind, &device.Logger{ wgDevice := device.NewDevice(wireDevice, (*wireClientBind)(outbound), &device.Logger{
Verbosef: func(format string, args ...interface{}) { Verbosef: func(format string, args ...interface{}) {
logger.Debug(fmt.Sprintf(strings.ToLower(format), args...)) logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
}, },
@@ -122,7 +161,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
return nil, E.Cause(err, "setup wireguard") return nil, E.Cause(err, "setup wireguard")
} }
outbound.device = wgDevice outbound.device = wgDevice
outbound.tunDevice = wireTunDevice outbound.tunDevice = wireDevice
return outbound, nil return outbound, nil
} }
@@ -133,19 +172,54 @@ func (w *WireGuard) DialContext(ctx context.Context, network string, destination
case N.NetworkUDP: case N.NetworkUDP:
w.logger.InfoContext(ctx, "outbound packet connection to ", destination) w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
} }
addr := tcpip.FullAddress{
NIC: defaultNIC,
Port: destination.Port,
}
if destination.IsFqdn() { if destination.IsFqdn() {
addrs, err := w.router.LookupDefault(ctx, destination.Fqdn) addrs, err := w.router.LookupDefault(ctx, destination.Fqdn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return N.DialSerial(ctx, w.tunDevice, network, destination, addrs) addr.Addr = tcpip.Address(addrs[0].AsSlice())
} else {
addr.Addr = tcpip.Address(destination.Addr.AsSlice())
}
bind := tcpip.FullAddress{
NIC: defaultNIC,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = w.tunDevice.addr4
} else {
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = w.tunDevice.addr6
}
switch N.NetworkName(network) {
case N.NetworkTCP:
return gonet.DialTCPWithBind(ctx, w.tunDevice.stack, bind, addr, networkProtocol)
case N.NetworkUDP:
return gonet.DialUDP(w.tunDevice.stack, &bind, &addr, networkProtocol)
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
} }
return w.tunDevice.DialContext(ctx, network, destination)
} }
func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
w.logger.InfoContext(ctx, "outbound packet connection to ", destination) w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return w.tunDevice.ListenPacket(ctx, destination) bind := tcpip.FullAddress{
NIC: defaultNIC,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() || w.tunDevice.addr6 == "" {
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = w.tunDevice.addr4
} else {
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = w.tunDevice.addr6
}
return gonet.DialUDP(w.tunDevice.stack, &bind, nil, networkProtocol)
} }
func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
@@ -157,13 +231,300 @@ func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn,
} }
func (w *WireGuard) Start() error { func (w *WireGuard) Start() error {
return w.tunDevice.Start() w.tunDevice.events <- tun.EventUp
return nil
} }
func (w *WireGuard) Close() error { func (w *WireGuard) Close() error {
return common.Close( return common.Close(
w.tunDevice, common.PtrOrNil(w.tunDevice),
common.PtrOrNil(w.device), common.PtrOrNil(w.device),
common.PtrOrNil(w.bind), common.PtrOrNil(w.conn),
) )
} }
var _ conn.Bind = (*wireClientBind)(nil)
type wireClientBind WireGuard
func (c *wireClientBind) connect() (*wireConn, error) {
c.connAccess.Lock()
defer c.connAccess.Unlock()
if c.conn != nil {
select {
case <-c.conn.done:
default:
return c.conn, nil
}
}
udpConn, err := c.dialer.DialContext(c.ctx, "udp", c.serverAddr)
if err != nil {
return nil, &wireError{err}
}
c.conn = &wireConn{
Conn: udpConn,
done: make(chan struct{}),
}
return c.conn, nil
}
func (c *wireClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
return []conn.ReceiveFunc{c.receive}, 0, nil
}
func (c *wireClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
udpConn, err := c.connect()
if err != nil {
err = &wireError{err}
return
}
n, err = udpConn.Read(b)
if err != nil {
udpConn.Close()
err = &wireError{err}
}
ep = c.endpoint
return
}
func (c *wireClientBind) Close() error {
c.connAccess.Lock()
defer c.connAccess.Unlock()
common.Close(common.PtrOrNil(c.conn))
return nil
}
func (c *wireClientBind) SetMark(mark uint32) error {
return nil
}
func (c *wireClientBind) Send(b []byte, ep conn.Endpoint) error {
udpConn, err := c.connect()
if err != nil {
return err
}
_, err = udpConn.Write(b)
if err != nil {
udpConn.Close()
}
return err
}
func (c *wireClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return c.endpoint, nil
}
type wireError struct {
cause error
}
func (w *wireError) Error() string {
return w.cause.Error()
}
func (w *wireError) Timeout() bool {
if cause, causeNet := w.cause.(net.Error); causeNet {
return cause.Timeout()
}
return false
}
func (w *wireError) Temporary() bool {
return true
}
type wireConn struct {
net.Conn
access sync.Mutex
done chan struct{}
}
func (w *wireConn) Close() error {
w.access.Lock()
defer w.access.Unlock()
select {
case <-w.done:
return net.ErrClosed
default:
}
w.Conn.Close()
close(w.done)
return nil
}
var _ tun.Device = (*wireTunDevice)(nil)
const defaultNIC tcpip.NICID = 1
type wireTunDevice struct {
stack *stack.Stack
mtu uint32
events chan tun.Event
outbound chan *stack.PacketBuffer
dispatcher stack.NetworkDispatcher
done chan struct{}
addr4 tcpip.Address
addr6 tcpip.Address
}
func newWireDevice(localAddresses []tcpip.AddressWithPrefix, mtu uint32) (*wireTunDevice, error) {
ipStack := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
HandleLocal: true,
})
tunDevice := &wireTunDevice{
stack: ipStack,
mtu: mtu,
events: make(chan tun.Event, 4),
outbound: make(chan *stack.PacketBuffer, 256),
done: make(chan struct{}),
}
err := ipStack.CreateNIC(defaultNIC, (*wireEndpoint)(tunDevice))
if err != nil {
return nil, E.New(err.String())
}
for _, addr := range localAddresses {
var protoAddr tcpip.ProtocolAddress
if len(addr.Address) == net.IPv4len {
tunDevice.addr4 = addr.Address
protoAddr = tcpip.ProtocolAddress{
Protocol: ipv4.ProtocolNumber,
AddressWithPrefix: addr,
}
} else {
tunDevice.addr6 = addr.Address
protoAddr = tcpip.ProtocolAddress{
Protocol: ipv6.ProtocolNumber,
AddressWithPrefix: addr,
}
}
err = ipStack.AddProtocolAddress(defaultNIC, protoAddr, stack.AddressProperties{})
if err != nil {
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", err.String())
}
}
sOpt := tcpip.TCPSACKEnabled(true)
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
cOpt := tcpip.CongestionControlOption("cubic")
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &cOpt)
ipStack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: defaultNIC})
ipStack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: defaultNIC})
return tunDevice, nil
}
func (w *wireTunDevice) File() *os.File {
return nil
}
func (w *wireTunDevice) Read(p []byte, offset int) (n int, err error) {
packetBuffer, ok := <-w.outbound
if !ok {
return 0, os.ErrClosed
}
defer packetBuffer.DecRef()
p = p[offset:]
for _, slice := range packetBuffer.AsSlices() {
n += copy(p[n:], slice)
}
return
}
func (w *wireTunDevice) Write(p []byte, offset int) (n int, err error) {
p = p[offset:]
if len(p) == 0 {
return
}
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(p) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(p),
})
defer packetBuffer.DecRef()
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
n = len(p)
return
}
func (w *wireTunDevice) Flush() error {
return nil
}
func (w *wireTunDevice) MTU() (int, error) {
return int(w.mtu), nil
}
func (w *wireTunDevice) Name() (string, error) {
return "sing-box", nil
}
func (w *wireTunDevice) Events() chan tun.Event {
return w.events
}
func (w *wireTunDevice) Close() error {
select {
case <-w.done:
return os.ErrClosed
default:
}
close(w.done)
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()
}
w.stack.Wait()
close(w.outbound)
return nil
}
var _ stack.LinkEndpoint = (*wireEndpoint)(nil)
type wireEndpoint wireTunDevice
func (ep *wireEndpoint) MTU() uint32 {
return ep.mtu
}
func (ep *wireEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (ep *wireEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityNone
}
func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
ep.dispatcher = dispatcher
}
func (ep *wireEndpoint) IsAttached() bool {
return ep.dispatcher != nil
}
func (ep *wireEndpoint) Wait() {
}
func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (ep *wireEndpoint) AddHeader(buffer *stack.PacketBuffer) {
}
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
for _, packetBuffer := range list.AsSlice() {
packetBuffer.IncRef()
ep.outbound <- packetBuffer
}
return list.Len(), nil
}

View File

@@ -16,4 +16,3 @@ popd
sudo systemctl stop sing-box sudo systemctl stop sing-box
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/ sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
sudo systemctl start sing-box sudo systemctl start sing-box
sudo journalctl -u sing-box --output cat -f

View File

@@ -93,7 +93,7 @@ type Router struct {
networkMonitor tun.NetworkUpdateMonitor networkMonitor tun.NetworkUpdateMonitor
interfaceMonitor tun.DefaultInterfaceMonitor interfaceMonitor tun.DefaultInterfaceMonitor
packageManager tun.PackageManager packageManager tun.PackageManager
clashServer adapter.ClashServer trafficController adapter.TrafficController
processSearcher process.Searcher processSearcher process.Searcher
} }
@@ -244,7 +244,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
needInterfaceMonitor := options.AutoDetectInterface || needInterfaceMonitor := options.AutoDetectInterface ||
C.IsDarwin && common.Any(inbounds, func(inbound option.Inbound) bool { C.IsDarwin && common.Any(inbounds, func(inbound option.Inbound) bool {
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || C.IsAndroid && inbound.TunOptions.AutoRoute return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy
}) })
if router.interfaceBindManager != nil || needInterfaceMonitor { if router.interfaceBindManager != nil || needInterfaceMonitor {
@@ -258,24 +258,12 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
} }
if router.networkMonitor != nil && needInterfaceMonitor { if router.networkMonitor != nil && needInterfaceMonitor {
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{ interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor)
OverrideAndroidVPN: options.OverrideAndroidVPN,
})
if err != nil { if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform") return nil, E.New("auto_detect_interface unsupported on current platform")
} }
interfaceMonitor.RegisterCallback(func(event int) error { interfaceMonitor.RegisterCallback(func() error {
if C.IsAndroid { router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
var vpnStatus string
if router.interfaceMonitor.AndroidVPNEnabled() {
vpnStatus = "enabled"
} else {
vpnStatus = "disabled"
}
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
} else {
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
}
return nil return nil
}) })
router.interfaceMonitor = interfaceMonitor router.interfaceMonitor = interfaceMonitor
@@ -553,8 +541,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
if metadata.SniffEnabled { if metadata.SniffEnabled {
buffer := buf.NewPacket() buffer := buf.NewPacket()
buffer.FullReset() buffer.FullReset()
sniffMetadata, err := sniff.PeekStream(ctx, conn, buffer, sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost) sniffMetadata, _ := sniff.PeekStream(ctx, conn, buffer, sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
if err == nil { if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) { if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
@@ -588,8 +576,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
conn.Close() conn.Close()
return E.New("missing supported outbound, closing connection") return E.New("missing supported outbound, closing connection")
} }
if r.clashServer != nil { if r.trafficController != nil {
trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, matchedRule) trackerConn, tracker := r.trafficController.RoutedConnection(ctx, conn, metadata, matchedRule)
defer tracker.Leave() defer tracker.Leave()
conn = trackerConn conn = trackerConn
} }
@@ -630,8 +618,8 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
buffer.Release() buffer.Release()
return err return err
} }
sniffMetadata, err := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage) sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if err == nil { if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) { if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
@@ -661,8 +649,8 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
conn.Close() conn.Close()
return E.New("missing supported outbound, closing packet connection") return E.New("missing supported outbound, closing packet connection")
} }
if r.clashServer != nil { if r.trafficController != nil {
trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, matchedRule) trackerConn, tracker := r.trafficController.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
defer tracker.Leave() defer tracker.Leave()
conn = trackerConn conn = trackerConn
} }
@@ -679,12 +667,12 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
} }
processInfo, err := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination) processInfo, err := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination)
if err != nil { if err != nil {
r.logger.InfoContext(ctx, "failed to search process: ", err) r.logger.DebugContext(ctx, "failed to search process: ", err)
} else { } else {
if processInfo.ProcessPath != "" { if processInfo.ProcessPath != "" {
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath) r.logger.DebugContext(ctx, "found process path: ", processInfo.ProcessPath)
} else if processInfo.PackageName != "" { } else if processInfo.PackageName != "" {
r.logger.InfoContext(ctx, "found package name: ", processInfo.PackageName) r.logger.DebugContext(ctx, "found package name: ", processInfo.PackageName)
} else if processInfo.UserId != -1 { } else if processInfo.UserId != -1 {
if /*needUserName &&*/ true { if /*needUserName &&*/ true {
osUser, _ := user.LookupId(F.ToString(processInfo.UserId)) osUser, _ := user.LookupId(F.ToString(processInfo.UserId))
@@ -693,9 +681,9 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
} }
} }
if processInfo.User != "" { if processInfo.User != "" {
r.logger.InfoContext(ctx, "found user: ", processInfo.User) r.logger.DebugContext(ctx, "found user: ", processInfo.User)
} else { } else {
r.logger.InfoContext(ctx, "found user id: ", processInfo.UserId) r.logger.DebugContext(ctx, "found user id: ", processInfo.UserId)
} }
} }
metadata.ProcessInfo = processInfo metadata.ProcessInfo = processInfo
@@ -746,12 +734,8 @@ func (r *Router) PackageManager() tun.PackageManager {
return r.packageManager return r.packageManager
} }
func (r *Router) ClashServer() adapter.ClashServer { func (r *Router) SetTrafficController(controller adapter.TrafficController) {
return r.clashServer r.trafficController = controller
}
func (r *Router) SetClashServer(controller adapter.ClashServer) {
r.clashServer = controller
} }
func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool { func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool {

View File

@@ -37,7 +37,11 @@ func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport,
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour) r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
} }
} }
return ctx, r.defaultTransport, r.defaultDomainStrategy if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
return ctx, r.defaultTransport, domainStrategy
} else {
return ctx, r.defaultTransport, r.defaultDomainStrategy
}
} }
func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) {

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