Compare commits

..

66 Commits

Author SHA1 Message Date
世界
951d83cf97 Fix shadowsocks none client 2023-06-05 13:22:01 +08:00
世界
13b36ed75b Fix TLS 1.2 support for shadow-tls client 2023-06-05 13:22:01 +08:00
世界
db4d86e5ea Use API to create windows firewall rule 2023-05-20 12:15:38 +08:00
世界
1ba267ed23 Fix using v2ray websocket transport with detour 2023-05-19 18:31:59 +08:00
世界
80c5cc8d54 documentation: Update changelog 2023-05-17 21:46:59 +08:00
世界
a7758f32c6 Fix h2mux race 2023-05-17 21:46:59 +08:00
世界
fae2729f04 Improve read waiter interface 2023-05-12 12:19:16 +08:00
世界
925f89b3ea Fix DNS outbound 2023-05-12 12:19:15 +08:00
世界
f499295c2a Add cache_id option for Clash cache file 2023-05-12 12:19:15 +08:00
世界
e2c7ae77be Fix uTLS ALPN 2023-05-12 12:19:15 +08:00
世界
f6efe31756 Fix shadowsocks AEAD UDP server 2023-05-12 12:19:15 +08:00
世界
ff1c1ef4c9 Add experimental_fix_windows_firewall option for system tun stack 2023-05-12 12:19:15 +08:00
世界
57f87eab87 Add fakeip example 2023-05-12 12:19:15 +08:00
世界
e02317a348 documentation: Update changelog 2023-05-03 11:00:44 +08:00
世界
e556dfad68 Update dependencies 2023-05-03 10:53:00 +08:00
世界
b0a978d4b6 Fix write cached packets 2023-05-01 12:45:30 +08:00
世界
c6e6621ecf Fix shadowsocks key length error message 2023-05-01 11:29:05 +08:00
世界
d3b41dfc01 documentation: Update changelog 2023-04-30 17:00:56 +08:00
XYenon
08f4384579 Fix incorrect use of sort.Slice 2023-04-30 16:58:07 +08:00
世界
d08b82b71c Reimplemented shadowsocks client 2023-04-30 16:38:12 +08:00
世界
d2d3c82ccf Fix wait copy packet 2023-04-28 11:30:19 +08:00
世界
841ef1acaf Set TCP keepalive for WireGuard gVisor TCP connections 2023-04-26 19:34:20 +08:00
Weltolk
115507fb2a documentation: Fix fakeip link broken 2023-04-26 19:34:20 +08:00
Larvan2
c5067af884 Enable mkdocs search in documentation
Signed-off-by: Larvan2 <78135608+Larvan2@users.noreply.github.com>
2023-04-26 19:34:20 +08:00
Hellojack
974b2a3165 Fix UVariantLen usage 2023-04-26 19:34:20 +08:00
世界
955028d4dc Fix cached packets order 2023-04-26 19:34:18 +08:00
世界
7050011802 Improve DNS caching 2023-04-26 19:34:11 +08:00
世界
2a76b8fbeb Fix documentation 2023-04-26 04:56:26 +08:00
世界
e3286d62ce Fix h2mux buffer to large 2023-04-26 04:56:26 +08:00
世界
37657851ae Improve direct copy 2023-04-26 04:56:25 +08:00
世界
26bfcbd33c clash-api: Reset outbounds in DELETE /connections 2023-04-24 19:01:18 +08:00
世界
50827bcff1 Add multiplexer for VLESS outbound 2023-04-24 19:01:18 +08:00
世界
6b64ebd3c0 Migrate multiplex to library 2023-04-24 19:01:17 +08:00
世界
bec606ee88 documentation: Update changelog 2023-04-23 13:47:27 +08:00
世界
8545e41b2f Improve multiplex 2023-04-23 13:46:00 +08:00
世界
d8810b6e8f Update gVisor to 20230417.0 2023-04-23 13:46:00 +08:00
世界
f568bb9fe3 Add debug http server 2023-04-22 19:51:38 +08:00
世界
ccb872a41e Add filemanager api 2023-04-22 19:51:37 +08:00
世界
9c287094e2 Improve multiplex 2023-04-22 19:51:37 +08:00
世界
f61c5600e0 Update wireguard-go 2023-04-22 19:51:36 +08:00
世界
2e98777f82 Ignore system tun stack bind interface error 2023-04-22 19:51:36 +08:00
世界
73e72e9aec Improve VLESS request 2023-04-22 19:51:36 +08:00
世界
922acced94 Update badtls 2023-04-22 19:51:35 +08:00
世界
988d7331c6 Add deadline interface 2023-04-22 19:51:35 +08:00
世界
c0d6dde95b shadowsocks: Multi-user support for legacy AEAD inbound
Signed-off-by: wwqgtxx <wwqgtxx@gmail.com>
2023-04-22 19:51:35 +08:00
世界
4dbf95875b Add headers option for HTTP outbound 2023-04-22 19:51:34 +08:00
世界
81c4312be8 URLTest improvements 2023-04-22 19:51:34 +08:00
世界
cc94dfaa4b Fix wireguard reconnect 2023-04-22 19:51:33 +08:00
世界
c6fc411164 Use HTTPS URLTest source 2023-04-22 19:51:33 +08:00
世界
be00e19162 clash-api: Add Clash.Meta APIs 2023-04-22 19:51:32 +08:00
世界
eb57cbc4ad clash api: download clash-dashboard if external-ui directory is empty 2023-04-22 19:51:31 +08:00
世界
f98cfdf5e4 Add multi-peer support for wireguard outbound 2023-04-22 19:51:31 +08:00
世界
cbf0099681 Add fakeip support 2023-04-22 19:51:30 +08:00
世界
a86afa0e5b Add L3 routing support 2023-04-22 19:51:30 +08:00
世界
b5d2062359 Add dns reverse mapping 2023-04-22 19:51:30 +08:00
世界
e8dad1afeb Fix grpc request 2023-04-22 19:51:04 +08:00
世界
6ce4e31fc8 documentation: Update changelog 2023-04-22 08:26:09 +08:00
世界
d2d4faf520 Revert LRU cache changes 2023-04-22 08:23:49 +08:00
世界
438de36749 Make v2ray http2 conn public 2023-04-22 08:14:55 +08:00
世界
df0eef770e Fix http response check
Co-authored-by: armv9 <48624112+arm64v8a@users.noreply.github.com>
2023-04-22 08:14:55 +08:00
世界
bbdd495ed5 Fix v2ray-plugin TLS server name
Co-authored-by: armv9 <48624112+arm64v8a@users.noreply.github.com>
2023-04-21 17:48:36 +08:00
世界
d686172854 Fix grpc lite request host
Co-authored-by: armv9 <48624112+arm64v8a@users.noreply.github.com>
2023-04-21 17:48:36 +08:00
H1JK
e1d96cb64e Add BaseContext to http servers 2023-04-21 17:48:29 +08:00
H1JK
d5f94b65b7 Fix gRPC service name escape 2023-04-21 17:48:29 +08:00
Hellojack
ec2d0b6b3c Remove TLS requirement for gRPC client 2023-04-21 17:48:29 +08:00
世界
3a92bf993d platform: Add UsePlatformAutoDetectInterfaceControl 2023-04-21 17:47:17 +08:00
93 changed files with 1438 additions and 1852 deletions

View File

@@ -22,7 +22,7 @@ install:
fmt: fmt:
@gofumpt -l -w . @gofumpt -l -w .
@gofmt -s -w . @gofmt -s -w .
@gci write --custom-order -s "standard,prefix(github.com/sagernet/),default" . @gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
fmt_install: fmt_install:
go install -v mvdan.cc/gofumpt@latest go install -v mvdan.cc/gofumpt@latest

View File

@@ -56,6 +56,8 @@ type Router interface {
V2RayServer() V2RayServer V2RayServer() V2RayServer
SetV2RayServer(server V2RayServer) SetV2RayServer(server V2RayServer)
ResetNetwork() error
} }
type routerContextKey struct{} type routerContextKey struct{}

3
box.go
View File

@@ -62,6 +62,7 @@ func New(options Options) (*Box, error) {
defaultLogWriter = io.Discard defaultLogWriter = io.Discard
} }
logFactory, err := log.New(log.Options{ logFactory, err := log.New(log.Options{
Context: ctx,
Options: common.PtrValueOrDefault(options.Log), Options: common.PtrValueOrDefault(options.Log),
Observable: needClashAPI, Observable: needClashAPI,
DefaultWriter: defaultLogWriter, DefaultWriter: defaultLogWriter,
@@ -142,7 +143,7 @@ func New(options Options) (*Box, error) {
preServices := make(map[string]adapter.Service) preServices := make(map[string]adapter.Service)
postServices := make(map[string]adapter.Service) postServices := make(map[string]adapter.Service)
if needClashAPI { if needClashAPI {
clashServer, err := experimental.NewClashServer(router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI)) clashServer, err := experimental.NewClashServer(ctx, router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
if err != nil { if err != nil {
return nil, E.Cause(err, "create clash api server") return nil, E.Cause(err, "create clash api server")
} }

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
_ "github.com/sagernet/gomobile/event/key" _ "github.com/sagernet/gomobile/event/key"
"github.com/sagernet/sing-box/cmd/internal/build_shared" "github.com/sagernet/sing-box/cmd/internal/build_shared"
@@ -38,18 +39,23 @@ func main() {
var ( var (
sharedFlags []string sharedFlags []string
debugFlags []string debugFlags []string
sharedTags []string
debugTags []string
) )
func init() { func init() {
sharedFlags = append(sharedFlags, "-trimpath") sharedFlags = append(sharedFlags, "-trimpath")
sharedFlags = append(sharedFlags, "-ldflags") sharedFlags = append(sharedFlags, "-ldflags")
currentTag, err := build_shared.ReadTag() currentTag, err := build_shared.ReadTag()
if err != nil { if err != nil {
currentTag = "unknown" currentTag = "unknown"
} }
sharedFlags = append(sharedFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=") sharedFlags = append(sharedFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
debugFlags = append(debugFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag) debugFlags = append(debugFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api")
sharedTags = append(sharedTags, "test_sing_shadowsocks2")
debugTags = append(debugTags, "debug")
} }
func buildAndroid() { func buildAndroid() {
@@ -70,9 +76,9 @@ func buildAndroid() {
args = append(args, "-tags") args = append(args, "-tags")
if !debugEnabled { if !debugEnabled {
args = append(args, "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api") args = append(args, strings.Join(sharedTags, ","))
} else { } else {
args = append(args, "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,debug") args = append(args, strings.Join(append(sharedTags, debugTags...), ","))
} }
args = append(args, "./experimental/libbox") args = append(args, "./experimental/libbox")
@@ -109,11 +115,12 @@ func buildiOS() {
args = append(args, debugFlags...) args = append(args, debugFlags...)
} }
tags := append(sharedTags, "with_low_memory", "with_conntrack")
args = append(args, "-tags") args = append(args, "-tags")
if !debugEnabled { if !debugEnabled {
args = append(args, "with_gvisor,with_quic,with_utls,with_clash_api,with_low_memory,with_conntrack") args = append(args, strings.Join(tags, ","))
} else { } else {
args = append(args, "with_gvisor,with_quic,with_utls,with_clash_api,with_low_memory,with_conntrack,debug") args = append(args, strings.Join(append(tags, debugTags...), ","))
} }
args = append(args, "./experimental/libbox") args = append(args, "./experimental/libbox")

View File

@@ -1,44 +0,0 @@
//go:build debug
package main
import (
"encoding/json"
"net/http"
_ "net/http/pprof"
"runtime"
"runtime/debug"
"github.com/sagernet/sing-box/common/badjson"
"github.com/sagernet/sing-box/log"
"github.com/dustin/go-humanize"
)
func init() {
http.HandleFunc("/debug/gc", func(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNoContent)
go debug.FreeOSMemory()
})
http.HandleFunc("/debug/memory", func(writer http.ResponseWriter, request *http.Request) {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
var memObject badjson.JSONObject
memObject.Put("heap", humanize.IBytes(memStats.HeapInuse))
memObject.Put("stack", humanize.IBytes(memStats.StackInuse))
memObject.Put("idle", humanize.IBytes(memStats.HeapIdle-memStats.HeapReleased))
memObject.Put("goroutines", runtime.NumGoroutine())
memObject.Put("rss", rusageMaxRSS())
encoder := json.NewEncoder(writer)
encoder.SetIndent("", " ")
encoder.Encode(memObject)
})
go func() {
err := http.ListenAndServe("0.0.0.0:8964", nil)
if err != nil {
log.Debug(err)
}
}()
}

View File

@@ -4,6 +4,7 @@ import (
"io" "io"
"net" "net"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/common/x/list"
) )
@@ -42,7 +43,7 @@ func (c *PacketConn) Close() error {
} }
func (c *PacketConn) Upstream() any { func (c *PacketConn) Upstream() any {
return c.PacketConn return bufio.NewPacketConn(c.PacketConn)
} }
func (c *PacketConn) ReaderReplaceable() bool { func (c *PacketConn) ReaderReplaceable() bool {

View File

@@ -1,535 +1,21 @@
package mux package mux
import ( import (
"context"
"encoding/binary"
"io"
"net"
"sync"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing-mux"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/x/list"
) )
var _ N.Dialer = (*Client)(nil) func NewClientWithOptions(dialer N.Dialer, options option.MultiplexOptions) (*Client, error) {
type Client struct {
access sync.Mutex
connections list.List[abstractSession]
ctx context.Context
dialer N.Dialer
protocol Protocol
maxConnections int
minStreams int
maxStreams int
}
func NewClient(ctx context.Context, dialer N.Dialer, protocol Protocol, maxConnections int, minStreams int, maxStreams int) *Client {
return &Client{
ctx: ctx,
dialer: dialer,
protocol: protocol,
maxConnections: maxConnections,
minStreams: minStreams,
maxStreams: maxStreams,
}
}
func NewClientWithOptions(ctx context.Context, dialer N.Dialer, options option.MultiplexOptions) (N.Dialer, error) {
if !options.Enabled { if !options.Enabled {
return nil, nil return nil, nil
} }
if options.MaxConnections == 0 && options.MaxStreams == 0 { return mux.NewClient(mux.Options{
options.MinStreams = 8 Dialer: dialer,
} Protocol: options.Protocol,
protocol, err := ParseProtocol(options.Protocol) MaxConnections: options.MaxConnections,
if err != nil { MinStreams: options.MinStreams,
return nil, err MaxStreams: options.MaxStreams,
} Padding: options.Padding,
return NewClient(ctx, dialer, protocol, options.MaxConnections, options.MinStreams, options.MaxStreams), nil })
}
func (c *Client) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
switch N.NetworkName(network) {
case N.NetworkTCP:
stream, err := c.openStream()
if err != nil {
return nil, err
}
return &ClientConn{Conn: stream, destination: destination}, nil
case N.NetworkUDP:
stream, err := c.openStream()
if err != nil {
return nil, err
}
return bufio.NewUnbindPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
}
func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
stream, err := c.openStream()
if err != nil {
return nil, err
}
return &ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}, nil
}
func (c *Client) openStream() (net.Conn, error) {
var (
session abstractSession
stream net.Conn
err error
)
for attempts := 0; attempts < 2; attempts++ {
session, err = c.offer()
if err != nil {
continue
}
stream, err = session.Open()
if err != nil {
continue
}
break
}
if err != nil {
return nil, err
}
return &wrapStream{stream}, nil
}
func (c *Client) offer() (abstractSession, error) {
c.access.Lock()
defer c.access.Unlock()
sessions := make([]abstractSession, 0, c.maxConnections)
for element := c.connections.Front(); element != nil; {
if element.Value.IsClosed() {
nextElement := element.Next()
c.connections.Remove(element)
element = nextElement
continue
}
sessions = append(sessions, element.Value)
element = element.Next()
}
sLen := len(sessions)
if sLen == 0 {
return c.offerNew()
}
session := common.MinBy(sessions, abstractSession.NumStreams)
numStreams := session.NumStreams()
if numStreams == 0 {
return session, nil
}
if c.maxConnections > 0 {
if sLen >= c.maxConnections || numStreams < c.minStreams {
return session, nil
}
} else {
if c.maxStreams > 0 && numStreams < c.maxStreams {
return session, nil
}
}
return c.offerNew()
}
func (c *Client) offerNew() (abstractSession, error) {
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, Destination)
if err != nil {
return nil, err
}
if vectorisedWriter, isVectorised := bufio.CreateVectorisedWriter(conn); isVectorised {
conn = &vectorisedProtocolConn{protocolConn{Conn: conn, protocol: c.protocol}, vectorisedWriter}
} else {
conn = &protocolConn{Conn: conn, protocol: c.protocol}
}
session, err := c.protocol.newClient(conn)
if err != nil {
return nil, err
}
c.connections.PushBack(session)
return session, nil
}
func (c *Client) Close() error {
c.access.Lock()
defer c.access.Unlock()
for _, session := range c.connections.Array() {
session.Close()
}
return nil
}
type ClientConn struct {
net.Conn
destination M.Socksaddr
requestWrite bool
responseRead bool
}
func (c *ClientConn) readResponse() error {
response, err := ReadStreamResponse(c.Conn)
if err != nil {
return err
}
if response.Status == statusError {
return E.New("remote error: ", response.Message)
}
return nil
}
func (c *ClientConn) Read(b []byte) (n int, err error) {
if !c.responseRead {
err = c.readResponse()
if err != nil {
return
}
c.responseRead = true
}
return c.Conn.Read(b)
}
func (c *ClientConn) Write(b []byte) (n int, err error) {
if c.requestWrite {
return c.Conn.Write(b)
}
request := StreamRequest{
Network: N.NetworkTCP,
Destination: c.destination,
}
_buffer := buf.StackNewSize(requestLen(request) + len(b))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeStreamRequest(request, buffer)
buffer.Write(b)
_, err = c.Conn.Write(buffer.Bytes())
if err != nil {
return
}
c.requestWrite = true
return len(b), nil
}
func (c *ClientConn) ReadFrom(r io.Reader) (n int64, err error) {
if !c.requestWrite {
return bufio.ReadFrom0(c, r)
}
return bufio.Copy(c.Conn, r)
}
func (c *ClientConn) WriteTo(w io.Writer) (n int64, err error) {
if !c.responseRead {
return bufio.WriteTo0(c, w)
}
return bufio.Copy(w, c.Conn)
}
func (c *ClientConn) LocalAddr() net.Addr {
return c.Conn.LocalAddr()
}
func (c *ClientConn) RemoteAddr() net.Addr {
return c.destination.TCPAddr()
}
func (c *ClientConn) ReaderReplaceable() bool {
return c.responseRead
}
func (c *ClientConn) WriterReplaceable() bool {
return c.requestWrite
}
func (c *ClientConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientConn) Upstream() any {
return c.Conn
}
type ClientPacketConn struct {
N.ExtendedConn
destination M.Socksaddr
requestWrite bool
responseRead bool
}
func (c *ClientPacketConn) readResponse() error {
response, err := ReadStreamResponse(c.ExtendedConn)
if err != nil {
return err
}
if response.Status == statusError {
return E.New("remote error: ", response.Message)
}
return nil
}
func (c *ClientPacketConn) Read(b []byte) (n int, 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
}
if cap(b) < int(length) {
return 0, io.ErrShortBuffer
}
return io.ReadFull(c.ExtendedConn, b[:length])
}
func (c *ClientPacketConn) writeRequest(payload []byte) (n int, err error) {
request := StreamRequest{
Network: N.NetworkUDP,
Destination: c.destination,
}
rLen := requestLen(request)
if len(payload) > 0 {
rLen += 2 + len(payload)
}
_buffer := buf.StackNewSize(rLen)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeStreamRequest(request, buffer)
if len(payload) > 0 {
common.Must(
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
common.Error(buffer.Write(payload)),
)
}
_, err = c.ExtendedConn.Write(buffer.Bytes())
if err != nil {
return
}
c.requestWrite = true
return len(payload), nil
}
func (c *ClientPacketConn) Write(b []byte) (n int, err error) {
if !c.requestWrite {
return c.writeRequest(b)
}
err = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(b)))
if err != nil {
return
}
return c.ExtendedConn.Write(b)
}
func (c *ClientPacketConn) ReadBuffer(buffer *buf.Buffer) (err error) {
if !c.responseRead {
err = c.readResponse()
if err != nil {
return
}
c.responseRead = true
}
var length uint16
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
if err != nil {
return
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
return
}
func (c *ClientPacketConn) WriteBuffer(buffer *buf.Buffer) error {
if !c.requestWrite {
defer buffer.Release()
return common.Error(c.writeRequest(buffer.Bytes()))
}
bLen := buffer.Len()
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(bLen))
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ClientPacketConn) FrontHeadroom() int {
return 2
}
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
err = c.ReadBuffer(buffer)
return
}
func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
return c.WriteBuffer(buffer)
}
func (c *ClientPacketConn) LocalAddr() net.Addr {
return c.ExtendedConn.LocalAddr()
}
func (c *ClientPacketConn) RemoteAddr() net.Addr {
return c.destination.UDPAddr()
}
func (c *ClientPacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientPacketConn) Upstream() any {
return c.ExtendedConn
}
var _ N.NetPacketConn = (*ClientPacketAddrConn)(nil)
type ClientPacketAddrConn struct {
N.ExtendedConn
destination M.Socksaddr
requestWrite bool
responseRead bool
}
func (c *ClientPacketAddrConn) readResponse() error {
response, err := ReadStreamResponse(c.ExtendedConn)
if err != nil {
return err
}
if response.Status == statusError {
return E.New("remote error: ", response.Message)
}
return nil
}
func (c *ClientPacketAddrConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
if !c.responseRead {
err = c.readResponse()
if err != nil {
return
}
c.responseRead = true
}
destination, err := M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
if err != nil {
return
}
if destination.IsFqdn() {
addr = destination
} else {
addr = destination.UDPAddr()
}
var length uint16
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
if err != nil {
return
}
if cap(p) < int(length) {
return 0, nil, io.ErrShortBuffer
}
n, err = io.ReadFull(c.ExtendedConn, p[:length])
return
}
func (c *ClientPacketAddrConn) writeRequest(payload []byte, destination M.Socksaddr) (n int, err error) {
request := StreamRequest{
Network: N.NetworkUDP,
Destination: c.destination,
PacketAddr: true,
}
rLen := requestLen(request)
if len(payload) > 0 {
rLen += M.SocksaddrSerializer.AddrPortLen(destination) + 2 + len(payload)
}
_buffer := buf.StackNewSize(rLen)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeStreamRequest(request, buffer)
if len(payload) > 0 {
common.Must(
M.SocksaddrSerializer.WriteAddrPort(buffer, destination),
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
common.Error(buffer.Write(payload)),
)
}
_, err = c.ExtendedConn.Write(buffer.Bytes())
if err != nil {
return
}
c.requestWrite = true
return len(payload), nil
}
func (c *ClientPacketAddrConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
if !c.requestWrite {
return c.writeRequest(p, M.SocksaddrFromNet(addr))
}
err = M.SocksaddrSerializer.WriteAddrPort(c.ExtendedConn, M.SocksaddrFromNet(addr))
if err != nil {
return
}
err = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(p)))
if err != nil {
return
}
return c.ExtendedConn.Write(p)
}
func (c *ClientPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
if !c.responseRead {
err = c.readResponse()
if err != nil {
return
}
c.responseRead = true
}
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
if err != nil {
return
}
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 *ClientPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
if !c.requestWrite {
defer buffer.Release()
return common.Error(c.writeRequest(buffer.Bytes(), destination))
}
bLen := buffer.Len()
header := buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination) + 2))
common.Must(
M.SocksaddrSerializer.WriteAddrPort(header, destination),
binary.Write(header, binary.BigEndian, uint16(bLen)),
)
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ClientPacketAddrConn) LocalAddr() net.Addr {
return c.ExtendedConn.LocalAddr()
}
func (c *ClientPacketAddrConn) FrontHeadroom() int {
return 2 + M.MaxSocksaddrLength
}
func (c *ClientPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientPacketAddrConn) Upstream() any {
return c.ExtendedConn
} }

View File

@@ -1,240 +1,14 @@
package mux package mux
import ( import (
"encoding/binary" "github.com/sagernet/sing-mux"
"io"
"net"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/smux"
"github.com/hashicorp/yamux"
) )
var Destination = M.Socksaddr{ type (
Fqdn: "sp.mux.sing-box.arpa", Client = mux.Client
Port: 444,
}
const (
ProtocolSMux Protocol = iota
ProtocolYAMux
) )
type Protocol byte var (
Destination = mux.Destination
func ParseProtocol(name string) (Protocol, error) { HandleConnection = mux.HandleConnection
switch name {
case "", "smux":
return ProtocolSMux, nil
case "yamux":
return ProtocolYAMux, nil
default:
return ProtocolYAMux, E.New("unknown multiplex protocol: ", name)
}
}
func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
switch p {
case ProtocolSMux:
session, err := smux.Server(conn, smuxConfig())
if err != nil {
return nil, err
}
return &smuxSession{session}, nil
case ProtocolYAMux:
return yamux.Server(conn, yaMuxConfig())
default:
panic("unknown protocol")
}
}
func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
switch p {
case ProtocolSMux:
session, err := smux.Client(conn, smuxConfig())
if err != nil {
return nil, err
}
return &smuxSession{session}, nil
case ProtocolYAMux:
return yamux.Client(conn, yaMuxConfig())
default:
panic("unknown protocol")
}
}
func smuxConfig() *smux.Config {
config := smux.DefaultConfig()
config.KeepAliveDisabled = true
return config
}
func yaMuxConfig() *yamux.Config {
config := yamux.DefaultConfig()
config.LogOutput = io.Discard
config.StreamCloseTimeout = C.TCPTimeout
config.StreamOpenTimeout = C.TCPTimeout
return config
}
func (p Protocol) String() string {
switch p {
case ProtocolSMux:
return "smux"
case ProtocolYAMux:
return "yamux"
default:
return "unknown"
}
}
const (
version0 = 0
) )
type Request struct {
Protocol Protocol
}
func ReadRequest(reader io.Reader) (*Request, error) {
version, err := rw.ReadByte(reader)
if err != nil {
return nil, err
}
if version != version0 {
return nil, E.New("unsupported version: ", version)
}
protocol, err := rw.ReadByte(reader)
if err != nil {
return nil, err
}
if protocol > byte(ProtocolYAMux) {
return nil, E.New("unsupported protocol: ", protocol)
}
return &Request{Protocol: Protocol(protocol)}, nil
}
func EncodeRequest(buffer *buf.Buffer, request Request) {
buffer.WriteByte(version0)
buffer.WriteByte(byte(request.Protocol))
}
const (
flagUDP = 1
flagAddr = 2
statusSuccess = 0
statusError = 1
)
type StreamRequest struct {
Network string
Destination M.Socksaddr
PacketAddr bool
}
func ReadStreamRequest(reader io.Reader) (*StreamRequest, error) {
var flags uint16
err := binary.Read(reader, binary.BigEndian, &flags)
if err != nil {
return nil, err
}
destination, err := M.SocksaddrSerializer.ReadAddrPort(reader)
if err != nil {
return nil, err
}
var network string
var udpAddr bool
if flags&flagUDP == 0 {
network = N.NetworkTCP
} else {
network = N.NetworkUDP
udpAddr = flags&flagAddr != 0
}
return &StreamRequest{network, destination, udpAddr}, nil
}
func requestLen(request StreamRequest) int {
var rLen int
rLen += 1 // version
rLen += 2 // flags
rLen += M.SocksaddrSerializer.AddrPortLen(request.Destination)
return rLen
}
func EncodeStreamRequest(request StreamRequest, buffer *buf.Buffer) {
destination := request.Destination
var flags uint16
if request.Network == N.NetworkUDP {
flags |= flagUDP
}
if request.PacketAddr {
flags |= flagAddr
if !destination.IsValid() {
destination = Destination
}
}
common.Must(
binary.Write(buffer, binary.BigEndian, flags),
M.SocksaddrSerializer.WriteAddrPort(buffer, destination),
)
}
type StreamResponse struct {
Status uint8
Message string
}
func ReadStreamResponse(reader io.Reader) (*StreamResponse, error) {
var response StreamResponse
status, err := rw.ReadByte(reader)
if err != nil {
return nil, err
}
response.Status = status
if status == statusError {
response.Message, err = rw.ReadVString(reader)
if err != nil {
return nil, err
}
}
return &response, nil
}
type wrapStream struct {
net.Conn
}
func (w *wrapStream) Read(p []byte) (n int, err error) {
n, err = w.Conn.Read(p)
err = wrapError(err)
return
}
func (w *wrapStream) Write(p []byte) (n int, err error) {
n, err = w.Conn.Write(p)
err = wrapError(err)
return
}
func (w *wrapStream) WriteIsThreadUnsafe() {
}
func (w *wrapStream) Upstream() any {
return w.Conn
}
func wrapError(err error) error {
switch err {
case yamux.ErrStreamClosed:
return io.EOF
default:
return err
}
}

View File

@@ -1,269 +0,0 @@
package mux
import (
"context"
"encoding/binary"
"net"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/common/task"
)
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
request, err := ReadRequest(conn)
if err != nil {
return err
}
session, err := request.Protocol.newServer(conn)
if err != nil {
return err
}
var group task.Group
group.Append0(func(ctx context.Context) error {
var stream net.Conn
for {
stream, err = session.Accept()
if err != nil {
return err
}
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
}
})
group.Cleanup(func() {
session.Close()
})
return group.Run(ctx)
}
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
stream = &wrapStream{stream}
request, err := ReadStreamRequest(stream)
if err != nil {
logger.ErrorContext(ctx, err)
return
}
metadata.Destination = request.Destination
if request.Network == N.NetworkTCP {
logger.InfoContext(ctx, "inbound multiplex connection to ", metadata.Destination)
hErr := router.RouteConnection(ctx, &ServerConn{ExtendedConn: bufio.NewExtendedConn(stream)}, metadata)
stream.Close()
if hErr != nil {
errorHandler.NewError(ctx, hErr)
}
} else {
var packetConn N.PacketConn
if !request.PacketAddr {
logger.InfoContext(ctx, "inbound multiplex packet connection to ", metadata.Destination)
packetConn = &ServerPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: request.Destination}
} else {
logger.InfoContext(ctx, "inbound multiplex packet connection")
packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)}
}
hErr := router.RoutePacketConnection(ctx, packetConn, metadata)
stream.Close()
if hErr != nil {
errorHandler.NewError(ctx, hErr)
}
}
}
var _ N.HandshakeConn = (*ServerConn)(nil)
type ServerConn struct {
N.ExtendedConn
responseWrite bool
}
func (c *ServerConn) HandshakeFailure(err error) error {
errMessage := err.Error()
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
common.Must(
buffer.WriteByte(statusError),
rw.WriteVString(_buffer, errMessage),
)
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ServerConn) Write(b []byte) (n int, err error) {
if c.responseWrite {
return c.ExtendedConn.Write(b)
}
_buffer := buf.StackNewSize(1 + len(b))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
common.Must(
buffer.WriteByte(statusSuccess),
common.Error(buffer.Write(b)),
)
_, err = c.ExtendedConn.Write(buffer.Bytes())
if err != nil {
return
}
c.responseWrite = true
return len(b), nil
}
func (c *ServerConn) WriteBuffer(buffer *buf.Buffer) error {
if c.responseWrite {
return c.ExtendedConn.WriteBuffer(buffer)
}
buffer.ExtendHeader(1)[0] = statusSuccess
c.responseWrite = true
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ServerConn) FrontHeadroom() int {
if !c.responseWrite {
return 1
}
return 0
}
func (c *ServerConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerConn) Upstream() any {
return c.ExtendedConn
}
var (
_ N.HandshakeConn = (*ServerPacketConn)(nil)
_ N.PacketConn = (*ServerPacketConn)(nil)
)
type ServerPacketConn struct {
N.ExtendedConn
destination M.Socksaddr
responseWrite bool
}
func (c *ServerPacketConn) HandshakeFailure(err error) error {
errMessage := err.Error()
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
common.Must(
buffer.WriteByte(statusError),
rw.WriteVString(_buffer, errMessage),
)
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ServerPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
var length uint16
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
if err != nil {
return
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
if err != nil {
return
}
destination = c.destination
return
}
func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
pLen := buffer.Len()
common.Must(binary.Write(buf.With(buffer.ExtendHeader(2)), binary.BigEndian, uint16(pLen)))
if !c.responseWrite {
buffer.ExtendHeader(1)[0] = statusSuccess
c.responseWrite = true
}
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ServerPacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketConn) Upstream() any {
return c.ExtendedConn
}
func (c *ServerPacketConn) FrontHeadroom() int {
if !c.responseWrite {
return 3
}
return 2
}
var (
_ N.HandshakeConn = (*ServerPacketAddrConn)(nil)
_ N.PacketConn = (*ServerPacketAddrConn)(nil)
)
type ServerPacketAddrConn struct {
N.ExtendedConn
responseWrite bool
}
func (c *ServerPacketAddrConn) HandshakeFailure(err error) error {
errMessage := err.Error()
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
common.Must(
buffer.WriteByte(statusError),
rw.WriteVString(_buffer, errMessage),
)
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ServerPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
if err != nil {
return
}
var length uint16
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
if err != nil {
return
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
if err != nil {
return
}
return
}
func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
pLen := buffer.Len()
common.Must(binary.Write(buf.With(buffer.ExtendHeader(2)), binary.BigEndian, uint16(pLen)))
common.Must(M.SocksaddrSerializer.WriteAddrPort(buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination))), destination))
if !c.responseWrite {
buffer.ExtendHeader(1)[0] = statusSuccess
c.responseWrite = true
}
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *ServerPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketAddrConn) Upstream() any {
return c.ExtendedConn
}
func (c *ServerPacketAddrConn) FrontHeadroom() int {
if !c.responseWrite {
return 3 + M.MaxSocksaddrLength
}
return 2 + M.MaxSocksaddrLength
}

View File

@@ -1,91 +0,0 @@
package mux
import (
"io"
"net"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/smux"
)
type abstractSession interface {
Open() (net.Conn, error)
Accept() (net.Conn, error)
NumStreams() int
Close() error
IsClosed() bool
}
var _ abstractSession = (*smuxSession)(nil)
type smuxSession struct {
*smux.Session
}
func (s *smuxSession) Open() (net.Conn, error) {
return s.OpenStream()
}
func (s *smuxSession) Accept() (net.Conn, error) {
return s.AcceptStream()
}
type protocolConn struct {
net.Conn
protocol Protocol
protocolWritten bool
}
func (c *protocolConn) Write(p []byte) (n int, err error) {
if c.protocolWritten {
return c.Conn.Write(p)
}
_buffer := buf.StackNewSize(2 + len(p))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeRequest(buffer, Request{
Protocol: c.protocol,
})
common.Must(common.Error(buffer.Write(p)))
n, err = c.Conn.Write(buffer.Bytes())
if err == nil {
n--
}
c.protocolWritten = true
return n, err
}
func (c *protocolConn) ReadFrom(r io.Reader) (n int64, err error) {
if !c.protocolWritten {
return bufio.ReadFrom0(c, r)
}
return bufio.Copy(c.Conn, r)
}
func (c *protocolConn) Upstream() any {
return c.Conn
}
type vectorisedProtocolConn struct {
protocolConn
N.VectorisedWriter
}
func (c *vectorisedProtocolConn) WriteVectorised(buffers []*buf.Buffer) error {
if c.protocolWritten {
return c.VectorisedWriter.WriteVectorised(buffers)
}
c.protocolWritten = true
_buffer := buf.StackNewSize(2)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeRequest(buffer, Request{
Protocol: c.protocol,
})
return c.VectorisedWriter.WriteVectorised(append([]*buf.Buffer{buffer}, buffers...))
}

View File

@@ -111,6 +111,16 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(uConfig.NextProtos) > 0 {
for _, extension := range uConn.Extensions {
if alpnExtension, isALPN := extension.(*utls.ALPNExtension); isALPN {
alpnExtension.AlpnProtocols = uConfig.NextProtos
break
}
}
}
hello := uConn.HandshakeState.Hello hello := uConn.HandshakeState.Hello
hello.SessionId = make([]byte, 32) hello.SessionId = make([]byte, 32)
copy(hello.Raw[39:], hello.SessionId) copy(hello.Raw[39:], hello.SessionId)

View File

@@ -3,6 +3,7 @@
package tls package tls
import ( import (
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"math/rand" "math/rand"
@@ -47,7 +48,7 @@ func (e *UTLSClientConfig) Config() (*STDConfig, error) {
} }
func (e *UTLSClientConfig) Client(conn net.Conn) (Conn, error) { func (e *UTLSClientConfig) Client(conn net.Conn) (Conn, error) {
return &utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}, nil return &utlsALPNWrapper{utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}, e.config.NextProtos}, nil
} }
func (e *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) { func (e *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {
@@ -87,6 +88,31 @@ func (c *utlsConnWrapper) Upstream() any {
return c.UConn return c.UConn
} }
type utlsALPNWrapper struct {
utlsConnWrapper
nextProtocols []string
}
func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error {
if len(c.nextProtocols) > 0 {
err := c.BuildHandshakeState()
if err != nil {
return err
}
for _, extension := range c.Extensions {
if alpnExtension, isALPN := extension.(*utls.ALPNExtension); isALPN {
alpnExtension.AlpnProtocols = c.nextProtocols
err = c.BuildHandshakeState()
if err != nil {
return err
}
break
}
}
}
return c.UConn.HandshakeContext(ctx)
}
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (*UTLSClientConfig, error) { func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (*UTLSClientConfig, error) {
var serverName string var serverName string
if options.ServerName != "" { if options.ServerName != "" {

View File

@@ -3,40 +3,13 @@ package constant
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
) )
const dirName = "sing-box" const dirName = "sing-box"
var ( var resourcePaths []string
basePath string
tempPath string
resourcePaths []string
)
func BasePath(name string) string {
if basePath == "" || strings.HasPrefix(name, "/") {
return name
}
return filepath.Join(basePath, name)
}
func CreateTemp(pattern string) (*os.File, error) {
if tempPath == "" {
tempPath = os.TempDir()
}
return os.CreateTemp(tempPath, pattern)
}
func SetBasePath(path string) {
basePath = path
}
func SetTempPath(path string) {
tempPath = path
}
func FindPath(name string) (string, bool) { func FindPath(name string) (string, bool) {
name = os.ExpandEnv(name) name = os.ExpandEnv(name)

View File

@@ -10,6 +10,7 @@ import (
) )
func applyDebugOptions(options option.DebugOptions) { func applyDebugOptions(options option.DebugOptions) {
applyDebugListenOption(options)
if options.GCPercent != nil { if options.GCPercent != nil {
debug.SetGCPercent(*options.GCPercent) debug.SetGCPercent(*options.GCPercent)
} }

View File

@@ -10,6 +10,7 @@ import (
) )
func applyDebugOptions(options option.DebugOptions) { func applyDebugOptions(options option.DebugOptions) {
applyDebugListenOption(options)
if options.GCPercent != nil { if options.GCPercent != nil {
debug.SetGCPercent(*options.GCPercent) debug.SetGCPercent(*options.GCPercent)
} }

67
debug_http.go Normal file
View File

@@ -0,0 +1,67 @@
package box
import (
"net/http"
"net/http/pprof"
"runtime"
"runtime/debug"
"github.com/sagernet/sing-box/common/badjson"
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/dustin/go-humanize"
"github.com/go-chi/chi/v5"
)
var debugHTTPServer *http.Server
func applyDebugListenOption(options option.DebugOptions) {
if debugHTTPServer != nil {
debugHTTPServer.Close()
debugHTTPServer = nil
}
if options.Listen == "" {
return
}
r := chi.NewMux()
r.Route("/debug", func(r chi.Router) {
r.Get("/gc", func(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNoContent)
go debug.FreeOSMemory()
})
r.Get("/memory", func(writer http.ResponseWriter, request *http.Request) {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
var memObject badjson.JSONObject
memObject.Put("heap", humanize.IBytes(memStats.HeapInuse))
memObject.Put("stack", humanize.IBytes(memStats.StackInuse))
memObject.Put("idle", humanize.IBytes(memStats.HeapIdle-memStats.HeapReleased))
memObject.Put("goroutines", runtime.NumGoroutine())
memObject.Put("rss", rusageMaxRSS())
encoder := json.NewEncoder(writer)
encoder.SetIndent("", " ")
encoder.Encode(memObject)
})
r.HandleFunc("/pprof", pprof.Index)
r.HandleFunc("/pprof/*", pprof.Index)
r.HandleFunc("/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/pprof/profile", pprof.Profile)
r.HandleFunc("/pprof/symbol", pprof.Symbol)
r.HandleFunc("/pprof/trace", pprof.Trace)
})
debugHTTPServer = &http.Server{
Addr: options.Listen,
Handler: r,
}
go func() {
err := debugHTTPServer.ListenAndServe()
if err != nil && !E.IsClosed(err) {
log.Error(E.Cause(err, "serve debug HTTP server"))
}
}()
}

View File

@@ -1,6 +1,4 @@
//go:build debug package box
package main
import ( import (
"runtime" "runtime"

View File

@@ -1,6 +1,6 @@
//go:build debug && !linux //go:build !linux
package main package box
func rusageMaxRSS() float64 { func rusageMaxRSS() float64 {
return -1 return -1

View File

@@ -1,3 +1,46 @@
#### 1.3-beta12
* Automatically add Windows firewall rules in order for the system tun stack to work
* Fix TLS 1.2 support for shadow-tls client
* Add `cache_id` [option](/configuration/experimental#cache_id) for Clash cache file
* Fixes and improvements
#### 1.3-beta11
* Fix bugs and update dependencies
#### 1.3-beta10
* Improve direct copy **1**
* Improve DNS caching
* Add `independent_cache` [option](/configuration/dns#independent_cache) for DNS
* Reimplemented shadowsocks client **2**
* Add multiplex support for VLESS outbound
* Set TCP keepalive for WireGuard gVisor TCP connections
* Fixes and improvements
**1**:
* Make splice work with traffic statistics systems like Clash API
* Significantly reduces memory usage of idle connections
**2**:
Improved performance and reduced memory usage.
#### 1.3-beta9
* Improve multiplex **1**
* Fixes and improvements
*1*:
Added new `h2mux` multiplex protocol and `padding` multiplex option, see [Multiplex](/configuration/shared/multiplex).
#### 1.2.6
* Fix bugs and update dependencies
#### 1.3-beta8 #### 1.3-beta8
* Fix `system` tun stack for ios * Fix `system` tun stack for ios
@@ -7,7 +50,7 @@
*1: *1:
As in Xray, this is an incompatible update for XUDP in VLESS if vision flow is enabled. This is an incompatible update for XUDP in VLESS if vision flow is enabled.
#### 1.3-beta7 #### 1.3-beta7

View File

@@ -11,6 +11,7 @@
"strategy": "", "strategy": "",
"disable_cache": false, "disable_cache": false,
"disable_expire": false, "disable_expire": false,
"independent_cache": false,
"reverse_mapping": false, "reverse_mapping": false,
"fakeip": {} "fakeip": {}
} }
@@ -48,6 +49,10 @@ Disable dns cache.
Disable dns cache expire. Disable dns cache expire.
#### independent_cache
Make each DNS server's cache independent for special purposes. If enabled, will slightly degrade performance.
#### reverse_mapping #### reverse_mapping
Stores a reverse mapping of IP addresses after responding to a DNS query in order to provide domain names when routing. Stores a reverse mapping of IP addresses after responding to a DNS query in order to provide domain names when routing.

View File

@@ -11,6 +11,7 @@
"strategy": "", "strategy": "",
"disable_cache": false, "disable_cache": false,
"disable_expire": false, "disable_expire": false,
"independent_cache": false,
"reverse_mapping": false, "reverse_mapping": false,
"fakeip": {} "fakeip": {}
} }
@@ -47,6 +48,10 @@
禁用 DNS 缓存过期。 禁用 DNS 缓存过期。
#### independent_cache
使每个 DNS 服务器的缓存独立,以满足特殊目的。如果启用,将轻微降低性能。
#### reverse_mapping #### reverse_mapping
在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。 在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。

View File

@@ -30,18 +30,18 @@ The tag of the dns server.
The address of the dns server. The address of the dns server.
| Protocol | Format | | Protocol | Format |
|---------------------|-------------------------------| |-------------------------------------|-------------------------------|
| `System` | `local` | | `System` | `local` |
| `TCP` | `tcp://1.0.0.1` | | `TCP` | `tcp://1.0.0.1` |
| `UDP` | `8.8.8.8` `udp://8.8.4.4` | | `UDP` | `8.8.8.8` `udp://8.8.4.4` |
| `TLS` | `tls://dns.google` | | `TLS` | `tls://dns.google` |
| `HTTPS` | `https://1.1.1.1/dns-query` | | `HTTPS` | `https://1.1.1.1/dns-query` |
| `QUIC` | `quic://dns.adguard.com` | | `QUIC` | `quic://dns.adguard.com` |
| `HTTP3` | `h3://8.8.8.8/dns-query` | | `HTTP3` | `h3://8.8.8.8/dns-query` |
| `RCode` | `rcode://refused` | | `RCode` | `rcode://refused` |
| `DHCP` | `dhcp://auto` or `dhcp://en0` | | `DHCP` | `dhcp://auto` or `dhcp://en0` |
| [FakeIP](./fakeip) | `fakeip` | | [FakeIP](/configuration/dns/fakeip) | `fakeip` |
!!! warning "" !!! warning ""

View File

@@ -30,18 +30,18 @@ DNS 服务器的标签。
DNS 服务器的地址。 DNS 服务器的地址。
| 协议 | 格式 | | 协议 | 格式 |
|--------------------|------------------------------| |-------------------------------------|------------------------------|
| `System` | `local` | | `System` | `local` |
| `TCP` | `tcp://1.0.0.1` | | `TCP` | `tcp://1.0.0.1` |
| `UDP` | `8.8.8.8` `udp://8.8.4.4` | | `UDP` | `8.8.8.8` `udp://8.8.4.4` |
| `TLS` | `tls://dns.google` | | `TLS` | `tls://dns.google` |
| `HTTPS` | `https://1.1.1.1/dns-query` | | `HTTPS` | `https://1.1.1.1/dns-query` |
| `QUIC` | `quic://dns.adguard.com` | | `QUIC` | `quic://dns.adguard.com` |
| `HTTP3` | `h3://8.8.8.8/dns-query` | | `HTTP3` | `h3://8.8.8.8/dns-query` |
| `RCode` | `rcode://refused` | | `RCode` | `rcode://refused` |
| `DHCP` | `dhcp://auto``dhcp://en0` | | `DHCP` | `dhcp://auto``dhcp://en0` |
| [FakeIP](./fakeip) | `fakeip` | | [FakeIP](/configuration/dns/fakeip) | `fakeip` |
!!! warning "" !!! warning ""

View File

@@ -7,13 +7,14 @@
"experimental": { "experimental": {
"clash_api": { "clash_api": {
"external_controller": "127.0.0.1:9090", "external_controller": "127.0.0.1:9090",
"external_ui": "folder", "external_ui": "",
"external_ui_download_url": "", "external_ui_download_url": "",
"external_ui_download_detour": "", "external_ui_download_detour": "",
"secret": "", "secret": "",
"default_mode": "rule", "default_mode": "",
"store_selected": false, "store_selected": false,
"cache_file": "cache.db" "cache_file": "",
"cache_id": ""
}, },
"v2ray_api": { "v2ray_api": {
"listen": "127.0.0.1:8080", "listen": "127.0.0.1:8080",
@@ -91,6 +92,12 @@ Store selected outbound for the `Selector` outbound in cache file.
Cache file path, `cache.db` will be used if empty. Cache file path, `cache.db` will be used if empty.
#### cache_id
Cache ID.
If not empty, `store_selected` will use a separate store keyed by it.
### V2Ray API Fields ### V2Ray API Fields
!!! error "" !!! error ""

View File

@@ -7,13 +7,14 @@
"experimental": { "experimental": {
"clash_api": { "clash_api": {
"external_controller": "127.0.0.1:9090", "external_controller": "127.0.0.1:9090",
"external_ui": "folder", "external_ui": "",
"external_ui_download_url": "", "external_ui_download_url": "",
"external_ui_download_detour": "", "external_ui_download_detour": "",
"secret": "", "secret": "",
"default_mode": "rule", "default_mode": "",
"store_selected": false, "store_selected": false,
"cache_file": "cache.db" "cache_file": "",
"cache_id": ""
}, },
"v2ray_api": { "v2ray_api": {
"listen": "127.0.0.1:8080", "listen": "127.0.0.1:8080",
@@ -89,6 +90,12 @@ Clash 中的默认模式,默认使用 `rule`。
缓存文件路径,默认使用`cache.db` 缓存文件路径,默认使用`cache.db`
#### cache_id
缓存 ID。
如果不为空,`store_selected` 将会使用以此为键的独立存储。
### V2Ray API 字段 ### V2Ray API 字段
!!! error "" !!! error ""

View File

@@ -10,7 +10,8 @@
"protocol": "smux", "protocol": "smux",
"max_connections": 4, "max_connections": 4,
"min_streams": 4, "min_streams": 4,
"max_streams": 0 "max_streams": 0,
"padding": false
} }
``` ```
@@ -28,8 +29,9 @@ Multiplex protocol.
|----------|------------------------------------| |----------|------------------------------------|
| smux | https://github.com/xtaci/smux | | smux | https://github.com/xtaci/smux |
| yamux | https://github.com/hashicorp/yamux | | yamux | https://github.com/hashicorp/yamux |
| h2mux | https://golang.org/x/net/http2 |
SMux is used by default. h2mux is used by default.
#### max_connections #### max_connections
@@ -48,3 +50,12 @@ Conflict with `max_streams`.
Maximum multiplexed streams in a connection before opening a new connection. Maximum multiplexed streams in a connection before opening a new connection.
Conflict with `max_connections` and `min_streams`. Conflict with `max_connections` and `min_streams`.
#### padding
!!! info
Requires sing-box server version 1.3-beta9 or later.
Enable padding.

View File

@@ -28,8 +28,9 @@
|-------|------------------------------------| |-------|------------------------------------|
| smux | https://github.com/xtaci/smux | | smux | https://github.com/xtaci/smux |
| yamux | https://github.com/hashicorp/yamux | | yamux | https://github.com/hashicorp/yamux |
| h2mux | https://golang.org/x/net/http2 |
默认使用 SMux。 默认使用 h2mux。
#### max_connections #### max_connections
@@ -48,3 +49,12 @@
在打开新连接之前,连接中的最大多路复用流数量。 在打开新连接之前,连接中的最大多路复用流数量。
`max_connections``min_streams` 冲突。 `max_connections``min_streams` 冲突。
#### padding
!!! info
需要 sing-box 服务器版本 1.3-beta9 或更高。
启用填充。

105
docs/examples/fakeip.md Normal file
View File

@@ -0,0 +1,105 @@
```json
{
"dns": {
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
},
{
"tag": "local",
"address": "223.5.5.5",
"detour": "direct"
},
{
"tag": "remote",
"address": "fakeip"
},
{
"tag": "block",
"address": "rcode://success"
}
],
"rules": [
{
"geosite": "category-ads-all",
"server": "block",
"disable_cache": true
},
{
"outbound": "any",
"server": "local"
},
{
"geosite": "cn",
"server": "local"
},
{
"query_type": [
"A",
"AAAA"
],
"server": "remote"
}
],
"fakeip": {
"enabled": true,
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18"
},
"strategy": "ipv4_only"
},
"inbounds": [
{
"type": "tun",
"inet4_address": "172.19.0.1/30",
"auto_route": true,
"sniff": true,
"domain_strategy": "ipv4_only" // remove this line if you want to resolve the domain remotely (if the server is not sing-box, UDP may not work due to wrong behavior).
}
],
"outbounds": [
{
"type": "shadowsocks",
"tag": "proxy",
"server": "mydomain.com",
"server_port": 8080,
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg=="
},
{
"type": "direct",
"tag": "direct"
},
{
"type": "block",
"tag": "block"
},
{
"type": "dns",
"tag": "dns-out"
}
],
"route": {
"rules": [
{
"protocol": "dns",
"outbound": "dns-out"
},
{
"geosite": "cn",
"geoip": [
"private",
"cn"
],
"outbound": "direct"
},
{
"geosite": "category-ads-all",
"outbound": "block"
}
],
"auto_detect_interface": true
}
}
```

105
docs/examples/fakeip.zh.md Normal file
View File

@@ -0,0 +1,105 @@
```json
{
"dns": {
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
},
{
"tag": "local",
"address": "223.5.5.5",
"detour": "direct"
},
{
"tag": "remote",
"address": "fakeip"
},
{
"tag": "block",
"address": "rcode://success"
}
],
"rules": [
{
"geosite": "category-ads-all",
"server": "block",
"disable_cache": true
},
{
"outbound": "any",
"server": "local"
},
{
"geosite": "cn",
"server": "local"
},
{
"query_type": [
"A",
"AAAA"
],
"server": "remote"
}
],
"fakeip": {
"enabled": true,
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18"
},
"strategy": "ipv4_only"
},
"inbounds": [
{
"type": "tun",
"inet4_address": "172.19.0.1/30",
"auto_route": true,
"sniff": true,
"domain_strategy": "ipv4_only" // 如果您想在远程解析域,删除此行 (如果服务器程序不为 sing-box可能由于错误的行为导致 UDP 无法使用)。
}
],
"outbounds": [
{
"type": "shadowsocks",
"tag": "proxy",
"server": "mydomain.com",
"server_port": 8080,
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg=="
},
{
"type": "direct",
"tag": "direct"
},
{
"type": "block",
"tag": "block"
},
{
"type": "dns",
"tag": "dns-out"
}
],
"route": {
"rules": [
{
"protocol": "dns",
"outbound": "dns-out"
},
{
"geosite": "cn",
"geoip": [
"private",
"cn"
],
"outbound": "direct"
},
{
"geosite": "category-ads-all",
"outbound": "block"
}
],
"auto_detect_interface": true
}
}
```

View File

@@ -9,3 +9,4 @@ Configuration examples for sing-box.
* [ShadowTLS](./shadowtls) * [ShadowTLS](./shadowtls)
* [Clash API](./clash-api) * [Clash API](./clash-api)
* [WireGuard Direct](./wireguard-direct) * [WireGuard Direct](./wireguard-direct)
* [FakeIP](./fakeip)

View File

@@ -9,3 +9,4 @@ sing-box 的配置示例。
* [ShadowTLS](./shadowtls) * [ShadowTLS](./shadowtls)
* [Clash API](./clash-api) * [Clash API](./clash-api)
* [WireGuard Direct](./wireguard-direct) * [WireGuard Direct](./wireguard-direct)
* [FakeIP](./fakeip)

View File

@@ -13,7 +13,7 @@ Experimental iOS client for sing-box.
#### Note #### Note
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)` * User Agent in remote profile request is `SFI/$version ($version_code; sing-box $sing_box_version)`
* Crash logs is located in `Settings` -> `View Service Log` * Crash logs is located in `Settings` -> `View Service Log`
#### Privacy policy #### Privacy policy

View File

@@ -1,6 +1,7 @@
package experimental package experimental
import ( import (
"context"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -8,7 +9,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
) )
type ClashServerConstructor = func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) type ClashServerConstructor = func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
var clashServerConstructor ClashServerConstructor var clashServerConstructor ClashServerConstructor
@@ -16,9 +17,9 @@ func RegisterClashServerConstructor(constructor ClashServerConstructor) {
clashServerConstructor = constructor clashServerConstructor = constructor
} }
func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
if clashServerConstructor == nil { if clashServerConstructor == nil {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
return clashServerConstructor(router, logFactory, options) return clashServerConstructor(ctx, router, logFactory, options)
} }

View File

@@ -14,10 +14,11 @@ var bucketSelected = []byte("selected")
var _ adapter.ClashCacheFile = (*CacheFile)(nil) var _ adapter.ClashCacheFile = (*CacheFile)(nil)
type CacheFile struct { type CacheFile struct {
DB *bbolt.DB DB *bbolt.DB
cacheID []byte
} }
func Open(path string) (*CacheFile, error) { func Open(path string, cacheID string) (*CacheFile, error) {
const fileMode = 0o666 const fileMode = 0o666
options := bbolt.Options{Timeout: time.Second} options := bbolt.Options{Timeout: time.Second}
db, err := bbolt.Open(path, fileMode, &options) db, err := bbolt.Open(path, fileMode, &options)
@@ -31,13 +32,39 @@ func Open(path string) (*CacheFile, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &CacheFile{db}, nil var cacheIDBytes []byte
if cacheID != "" {
cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
}
return &CacheFile{db, cacheIDBytes}, nil
}
func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket {
if c.cacheID == nil {
return t.Bucket(key)
}
bucket := t.Bucket(c.cacheID)
if bucket == nil {
return nil
}
return bucket.Bucket(key)
}
func (c *CacheFile) createBucket(t *bbolt.Tx, key []byte) (*bbolt.Bucket, error) {
if c.cacheID == nil {
return t.CreateBucketIfNotExists(key)
}
bucket, err := t.CreateBucketIfNotExists(c.cacheID)
if bucket == nil {
return nil, err
}
return bucket.CreateBucketIfNotExists(key)
} }
func (c *CacheFile) LoadSelected(group string) string { func (c *CacheFile) LoadSelected(group string) string {
var selected string var selected string
c.DB.View(func(t *bbolt.Tx) error { c.DB.View(func(t *bbolt.Tx) error {
bucket := t.Bucket(bucketSelected) bucket := c.bucket(t, bucketSelected)
if bucket == nil { if bucket == nil {
return nil return nil
} }
@@ -52,7 +79,7 @@ func (c *CacheFile) LoadSelected(group string) string {
func (c *CacheFile) StoreSelected(group, selected string) error { func (c *CacheFile) StoreSelected(group, selected string) error {
return c.DB.Batch(func(t *bbolt.Tx) error { return c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := t.CreateBucketIfNotExists(bucketSelected) bucket, err := c.createBucket(t, bucketSelected)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/sagernet/sing-box/adapter"
"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/sagernet/websocket"
@@ -14,10 +15,10 @@ import (
"github.com/go-chi/render" "github.com/go-chi/render"
) )
func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler { func connectionRouter(router adapter.Router, trafficManager *trafficontrol.Manager) http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getConnections(trafficManager)) r.Get("/", getConnections(trafficManager))
r.Delete("/", closeAllConnections(trafficManager)) r.Delete("/", closeAllConnections(router, trafficManager))
r.Delete("/{id}", closeConnection(trafficManager)) r.Delete("/{id}", closeConnection(trafficManager))
return r return r
} }
@@ -86,12 +87,13 @@ func closeConnection(trafficManager *trafficontrol.Manager) func(w http.Response
} }
} }
func closeAllConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { func closeAllConnections(router adapter.Router, trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
snapshot := trafficManager.Snapshot() snapshot := trafficManager.Snapshot()
for _, c := range snapshot.Connections { for _, c := range snapshot.Connections {
c.Close() c.Close()
} }
router.ResetNetwork()
render.NoContent(w, r) render.NoContent(w, r)
} }
} }

View File

@@ -134,7 +134,7 @@ func getProxies(server *Server, router adapter.Router) func(w http.ResponseWrite
defaultTag = allProxies[0] defaultTag = allProxies[0]
} }
sort.Slice(allProxies, func(i, j int) bool { sort.SliceStable(allProxies, func(i, j int) bool {
return allProxies[i] == defaultTag return allProxies[i] == defaultTag
}) })

View File

@@ -23,6 +23,7 @@ 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/sing/service/filemanager"
"github.com/sagernet/websocket" "github.com/sagernet/websocket"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
@@ -37,6 +38,7 @@ func init() {
var _ adapter.ClashServer = (*Server)(nil) var _ adapter.ClashServer = (*Server)(nil)
type Server struct { type Server struct {
ctx context.Context
router adapter.Router router adapter.Router
logger log.Logger logger log.Logger
httpServer *http.Server httpServer *http.Server
@@ -46,6 +48,7 @@ type Server struct {
storeSelected bool storeSelected bool
storeFakeIP bool storeFakeIP bool
cacheFilePath string cacheFilePath string
cacheID string
cacheFile adapter.ClashCacheFile cacheFile adapter.ClashCacheFile
externalUI string externalUI string
@@ -53,10 +56,11 @@ type Server struct {
externalUIDownloadDetour string externalUIDownloadDetour string
} }
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { func NewServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
trafficManager := trafficontrol.NewManager() trafficManager := trafficontrol.NewManager()
chiRouter := chi.NewRouter() chiRouter := chi.NewRouter()
server := &Server{ server := &Server{
ctx: ctx,
router: router, router: router,
logger: logFactory.NewLogger("clash-api"), logger: logFactory.NewLogger("clash-api"),
httpServer: &http.Server{ httpServer: &http.Server{
@@ -82,9 +86,10 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
if foundPath, loaded := C.FindPath(cachePath); loaded { if foundPath, loaded := C.FindPath(cachePath); loaded {
cachePath = foundPath cachePath = foundPath
} else { } else {
cachePath = C.BasePath(cachePath) cachePath = filemanager.BasePath(ctx, cachePath)
} }
server.cacheFilePath = cachePath server.cacheFilePath = cachePath
server.cacheID = options.CacheID
} }
cors := cors.New(cors.Options{ cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, AllowedOrigins: []string{"*"},
@@ -102,7 +107,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
r.Mount("/configs", configRouter(server, logFactory, server.logger)) r.Mount("/configs", configRouter(server, logFactory, server.logger))
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(router, trafficManager))
r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/proxies", proxyProviderRouter())
r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter())
r.Mount("/script", scriptRouter()) r.Mount("/script", scriptRouter())
@@ -113,7 +118,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
server.setupMetaAPI(r) server.setupMetaAPI(r)
}) })
if options.ExternalUI != "" { if options.ExternalUI != "" {
server.externalUI = C.BasePath(os.ExpandEnv(options.ExternalUI)) server.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
chiRouter.Group(func(r chi.Router) { chiRouter.Group(func(r chi.Router) {
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(server.externalUI))) fs := http.StripPrefix("/ui", http.FileServer(http.Dir(server.externalUI)))
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP) r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
@@ -127,7 +132,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
func (s *Server) PreStart() error { func (s *Server) PreStart() error {
if s.cacheFilePath != "" { if s.cacheFilePath != "" {
cacheFile, err := cachefile.Open(s.cacheFilePath) cacheFile, err := cachefile.Open(s.cacheFilePath, s.cacheID)
if err != nil { if err != nil {
return E.Cause(err, "open cache file") return E.Cause(err, "open cache file")
} }

View File

@@ -12,11 +12,11 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
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"
"github.com/sagernet/sing/service/filemanager"
) )
func (s *Server) checkAndDownloadExternalUI() { func (s *Server) checkAndDownloadExternalUI() {
@@ -79,7 +79,7 @@ func (s *Server) downloadExternalUI() error {
} }
func (s *Server) downloadZIP(name string, body io.Reader, output string) error { func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
tempFile, err := C.CreateTemp(name) tempFile, err := filemanager.CreateTemp(s.ctx, name)
if err != nil { if err != nil {
return err return err
} }
@@ -112,7 +112,7 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
return err return err
} }
savePath := filepath.Join(saveDirectory, pathElements[len(pathElements)-1]) savePath := filepath.Join(saveDirectory, pathElements[len(pathElements)-1])
err = downloadZIPEntry(file, savePath) err = downloadZIPEntry(s.ctx, file, savePath)
if err != nil { if err != nil {
return err return err
} }
@@ -120,8 +120,8 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
return nil return nil
} }
func downloadZIPEntry(zipFile *zip.File, savePath string) error { func downloadZIPEntry(ctx context.Context, zipFile *zip.File, savePath string) error {
saveFile, err := os.Create(savePath) saveFile, err := filemanager.Create(ctx, savePath)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,9 +7,9 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/trackerconn"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic" "github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
@@ -115,13 +115,13 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
download := new(atomic.Int64) download := new(atomic.Int64)
t := &tcpTracker{ t := &tcpTracker{
ExtendedConn: trackerconn.NewHook(conn, func(n int64) { ExtendedConn: bufio.NewCounterConn(conn, []N.CountFunc{func(n int64) {
upload.Add(n) upload.Add(n)
manager.PushUploaded(n) manager.PushUploaded(n)
}, func(n int64) { }}, []N.CountFunc{func(n int64) {
download.Add(n) download.Add(n)
manager.PushDownloaded(n) manager.PushDownloaded(n)
}), }}),
manager: manager, manager: manager,
trackerInfo: &trackerInfo{ trackerInfo: &trackerInfo{
UUID: uuid, UUID: uuid,
@@ -202,13 +202,13 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, route
download := new(atomic.Int64) download := new(atomic.Int64)
ut := &udpTracker{ ut := &udpTracker{
PacketConn: trackerconn.NewHookPacket(conn, func(n int64) { PacketConn: bufio.NewCounterPacketConn(conn, []N.CountFunc{func(n int64) {
upload.Add(n) upload.Add(n)
manager.PushUploaded(n) manager.PushUploaded(n)
}, func(n int64) { }}, []N.CountFunc{func(n int64) {
download.Add(n) download.Add(n)
manager.PushDownloaded(n) manager.PushDownloaded(n)
}), }}),
manager: manager, manager: manager,
trackerInfo: &trackerInfo{ trackerInfo: &trackerInfo{
UUID: uuid, UUID: uuid,

View File

@@ -27,3 +27,27 @@ func RedirectStderr(path string) error {
stderrFile = outputFile stderrFile = outputFile
return nil return nil
} }
func RedirectStderrAsUser(path string, uid, gid int) error {
if stats, err := os.Stat(path); err == nil && stats.Size() > 0 {
_ = os.Rename(path, path+".old")
}
outputFile, err := os.Create(path)
if err != nil {
return err
}
err = outputFile.Chown(uid, gid)
if err != nil {
outputFile.Close()
os.Remove(outputFile.Name())
return err
}
err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
outputFile.Close()
os.Remove(outputFile.Name())
return err
}
stderrFile = outputFile
return nil
}

View File

@@ -5,6 +5,7 @@ import (
) )
type PlatformInterface interface { type PlatformInterface interface {
UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl(fd int32) error AutoDetectInterfaceControl(fd int32) error
OpenTun(options TunOptions) (int32, error) OpenTun(options TunOptions) (int32, error)
WriteLog(message string) WriteLog(message string)
@@ -17,6 +18,7 @@ type PlatformInterface interface {
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
UsePlatformInterfaceGetter() bool UsePlatformInterfaceGetter() bool
GetInterfaces() (NetworkInterfaceIterator, error) GetInterfaces() (NetworkInterfaceIterator, error)
UnderNetworkExtension() bool
} }
type TunInterface interface { type TunInterface interface {

View File

@@ -15,12 +15,14 @@ import (
type Interface interface { type Interface interface {
Initialize(ctx context.Context, router adapter.Router) error Initialize(ctx context.Context, router adapter.Router) error
UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl() control.Func AutoDetectInterfaceControl() control.Func
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
UsePlatformDefaultInterfaceMonitor() bool UsePlatformDefaultInterfaceMonitor() bool
CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor
UsePlatformInterfaceGetter() bool UsePlatformInterfaceGetter() bool
Interfaces() ([]NetworkInterface, error) Interfaces() ([]NetworkInterface, error)
UnderNetworkExtension() bool
process.Searcher process.Searcher
io.Writer io.Writer
} }

View File

@@ -16,6 +16,7 @@ import (
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service/filemanager"
) )
type BoxService struct { type BoxService struct {
@@ -30,6 +31,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
return nil, err return nil, err
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
ctx = filemanager.WithDefault(ctx, sBasePath, sTempPath, sUserID, sGroupID)
instance, err := box.New(box.Options{ instance, err := box.New(box.Options{
Context: ctx, Context: ctx,
Options: options, Options: options,
@@ -68,6 +70,10 @@ func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapte
return nil return nil
} }
func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool {
return w.iif.UsePlatformAutoDetectInterfaceControl()
}
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func { func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
return func(network, address string, conn syscall.RawConn) error { return func(network, address string, conn syscall.RawConn) error {
return control.Raw(conn, func(fd uintptr) error { return control.Raw(conn, func(fd uintptr) error {
@@ -163,3 +169,7 @@ func (w *platformInterfaceWrapper) Interfaces() ([]platform.NetworkInterface, er
} }
return interfaces, nil return interfaces, nil
} }
func (w *platformInterfaceWrapper) UnderNetworkExtension() bool {
return w.iif.UnderNetworkExtension()
}

View File

@@ -1,17 +1,31 @@
package libbox package libbox
import ( import (
"os"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
) )
func SetBasePath(path string) { var (
C.SetBasePath(path) sBasePath string
} sTempPath string
sUserID int
sGroupID int
)
func SetTempPath(path string) { func Setup(basePath string, tempPath string, userID int, groupID int) {
C.SetTempPath(path) sBasePath = basePath
sTempPath = tempPath
sUserID = userID
sGroupID = groupID
if sUserID == -1 {
sUserID = os.Getuid()
}
if sGroupID == -1 {
sGroupID = os.Getgid()
}
} }
func Version() string { func Version() string {

View File

@@ -1,108 +0,0 @@
package trackerconn
import (
"net"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network"
)
func New(conn net.Conn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *Conn {
return &Conn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
}
func NewHook(conn net.Conn, readCounter func(n int64), writeCounter func(n int64)) *HookConn {
return &HookConn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
}
type Conn struct {
N.ExtendedConn
readCounter []*atomic.Int64
writeCounter []*atomic.Int64
}
func (c *Conn) Read(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Read(p)
for _, counter := range c.readCounter {
counter.Add(int64(n))
}
return n, err
}
func (c *Conn) ReadBuffer(buffer *buf.Buffer) error {
err := c.ExtendedConn.ReadBuffer(buffer)
if err != nil {
return err
}
for _, counter := range c.readCounter {
counter.Add(int64(buffer.Len()))
}
return nil
}
func (c *Conn) Write(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Write(p)
for _, counter := range c.writeCounter {
counter.Add(int64(n))
}
return n, err
}
func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
dataLen := int64(buffer.Len())
err := c.ExtendedConn.WriteBuffer(buffer)
if err != nil {
return err
}
for _, counter := range c.writeCounter {
counter.Add(dataLen)
}
return nil
}
func (c *Conn) Upstream() any {
return c.ExtendedConn
}
type HookConn struct {
N.ExtendedConn
readCounter func(n int64)
writeCounter func(n int64)
}
func (c *HookConn) Read(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Read(p)
c.readCounter(int64(n))
return n, err
}
func (c *HookConn) ReadBuffer(buffer *buf.Buffer) error {
err := c.ExtendedConn.ReadBuffer(buffer)
if err != nil {
return err
}
c.readCounter(int64(buffer.Len()))
return nil
}
func (c *HookConn) Write(p []byte) (n int, err error) {
n, err = c.ExtendedConn.Write(p)
c.writeCounter(int64(n))
return n, err
}
func (c *HookConn) WriteBuffer(buffer *buf.Buffer) error {
dataLen := int64(buffer.Len())
err := c.ExtendedConn.WriteBuffer(buffer)
if err != nil {
return err
}
c.writeCounter(dataLen)
return nil
}
func (c *HookConn) Upstream() any {
return c.ExtendedConn
}

View File

@@ -1,76 +0,0 @@
package trackerconn
import (
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
func NewPacket(conn N.PacketConn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *PacketConn {
return &PacketConn{conn, readCounter, writeCounter}
}
func NewHookPacket(conn N.PacketConn, readCounter func(n int64), writeCounter func(n int64)) *HookPacketConn {
return &HookPacketConn{conn, readCounter, writeCounter}
}
type PacketConn struct {
N.PacketConn
readCounter []*atomic.Int64
writeCounter []*atomic.Int64
}
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = c.PacketConn.ReadPacket(buffer)
if err == nil {
for _, counter := range c.readCounter {
counter.Add(int64(buffer.Len()))
}
}
return
}
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
dataLen := int64(buffer.Len())
err := c.PacketConn.WritePacket(buffer, destination)
if err != nil {
return err
}
for _, counter := range c.writeCounter {
counter.Add(dataLen)
}
return nil
}
func (c *PacketConn) Upstream() any {
return c.PacketConn
}
type HookPacketConn struct {
N.PacketConn
readCounter func(n int64)
writeCounter func(n int64)
}
func (c *HookPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
destination, err = c.PacketConn.ReadPacket(buffer)
if err == nil {
c.readCounter(int64(buffer.Len()))
}
return
}
func (c *HookPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
dataLen := int64(buffer.Len())
err := c.PacketConn.WritePacket(buffer, destination)
if err != nil {
return err
}
c.writeCounter(dataLen)
return nil
}
func (c *HookPacketConn) Upstream() any {
return c.PacketConn
}

View File

@@ -10,9 +10,9 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/trackerconn"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/atomic" "github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@@ -83,7 +83,7 @@ func (s *StatsService) RoutedConnection(inbound string, outbound string, user st
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink")) writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
} }
s.access.Unlock() s.access.Unlock()
return trackerconn.New(conn, readCounter, writeCounter) return bufio.NewInt64CounterConn(conn, readCounter, writeCounter)
} }
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn { func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn {
@@ -109,7 +109,7 @@ func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, u
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink")) writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
} }
s.access.Unlock() s.access.Unlock()
return trackerconn.NewPacket(conn, readCounter, writeCounter) return bufio.NewInt64CounterPacketConn(conn, readCounter, writeCounter)
} }
func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {

30
go.mod
View File

@@ -4,7 +4,7 @@ go 1.18
require ( require (
berty.tech/go-libtor v1.0.385 berty.tech/go-libtor v1.0.385
github.com/Dreamacro/clash v1.15.0 github.com/Dreamacro/clash v1.15.1
github.com/caddyserver/certmagic v0.17.2 github.com/caddyserver/certmagic v0.17.2
github.com/cretz/bine v0.2.0 github.com/cretz/bine v0.2.0
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
@@ -13,11 +13,10 @@ require (
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/v5 v5.0.0 github.com/gofrs/uuid/v5 v5.0.0
github.com/hashicorp/yamux v0.1.1
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
github.com/logrusorgru/aurora v2.0.3+incompatible github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.1.0 github.com/mholt/acmez v1.1.0
github.com/miekg/dns v1.1.53 github.com/miekg/dns v1.1.54
github.com/ooni/go-libtor v1.1.7 github.com/ooni/go-libtor v1.1.7
github.com/oschwald/maxminddb-golang v1.10.0 github.com/oschwald/maxminddb-golang v1.10.0
github.com/pires/go-proxyproto v0.7.0 github.com/pires/go-proxyproto v0.7.0
@@ -25,30 +24,32 @@ require (
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3 github.com/sagernet/sing v0.2.5-0.20230517124404-be6013893626
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-shadowsocks v0.2.2-0.20230509053848-d83f8fe1194c
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 github.com/sagernet/sing-shadowsocks2 v0.0.0-20230605050515-45409ce8283d
github.com/sagernet/sing-shadowtls v0.1.2-0.20230531025805-ebadc7615da3
github.com/sagernet/sing-tun v0.1.5-0.20230520041100-b02f2529160e
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
go.uber.org/zap v1.24.0 go.uber.org/zap v1.24.0
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
golang.org/x/crypto v0.8.0 golang.org/x/crypto v0.8.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
golang.org/x/net v0.9.0 golang.org/x/net v0.9.0
golang.org/x/sys v0.7.0 golang.org/x/sys v0.7.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
google.golang.org/grpc v1.54.0 google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.30.0 google.golang.org/protobuf v1.30.0
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523
) )
//replace github.com/sagernet/sing => ../sing //replace github.com/sagernet/sing => ../sing
@@ -58,11 +59,13 @@ require (
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-ole/go-ole v1.2.6 // 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/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/compress v1.15.15 // indirect
@@ -78,6 +81,7 @@ require (
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect github.com/quic-go/qtls-go1-20 v0.1.0 // 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-20220905062125-8043b4a9aa97 // indirect
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
@@ -85,7 +89,7 @@ require (
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect

57
go.sum
View File

@@ -1,7 +1,7 @@
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE= github.com/Dreamacro/clash v1.15.1 h1:AwwtrVYbeXg9ZCxpPxAurGXwaR3FxDdHv3rB/vAGXj4=
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE= github.com/Dreamacro/clash v1.15.1/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
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.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
@@ -31,6 +31,8 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2 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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
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/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
@@ -70,8 +72,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY= github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs= github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
@@ -111,16 +113,20 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
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.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3 h1:dkH6SEs3yZlUjSXUAn64LUlFAfAgJqiThaWiBKWJ0Q0= github.com/sagernet/sing v0.2.5-0.20230517124404-be6013893626 h1:2TlOqs05+PXadWOubVBhXM27/UaI1If2k/ISjtYZiPE=
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing v0.2.5-0.20230517124404-be6013893626/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI= github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223 h1:L4eMuM07iSHY3UCknFnuFuHoe5clZuF2Xnf2wwA6Lwc=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI= github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU= github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646 h1:X3ADfMqeGns1Q1FlXc9kaL9FwW1UM6D6tEQo8jFstpc=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q= github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowsocks v0.2.2-0.20230509053848-d83f8fe1194c h1:EiQ+i4gdPpSI8D2YUlOeBZA3R1ZGi0ShSLSXoSd/13A=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-shadowsocks v0.2.2-0.20230509053848-d83f8fe1194c/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog=
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI= github.com/sagernet/sing-shadowsocks2 v0.0.0-20230605050515-45409ce8283d h1:obf7pm6aRav465lIGPGN8fx8pD8iWg15RQhwGwkyNNc=
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q= github.com/sagernet/sing-shadowsocks2 v0.0.0-20230605050515-45409ce8283d/go.mod h1:i6Eoor37Cy4JKBcelMMFfUVBNjqRYuvcqOBjUI3Zw80=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230531025805-ebadc7615da3 h1:PNwJs1F+3e/iZguYQR7YzxsH8Sm0Eu7vVuHawD89r34=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230531025805-ebadc7615da3/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
github.com/sagernet/sing-tun v0.1.5-0.20230520041100-b02f2529160e h1:fivkzQowiOPBafklJmNePBu/sr83rsY5SuSakvoJ2A0=
github.com/sagernet/sing-tun v0.1.5-0.20230520041100-b02f2529160e/go.mod h1:6yju6cNdTow38IZHwcbFA3lHnHeSd5HG/zCzHyaxScI=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
@@ -131,8 +137,10 @@ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfI
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
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=
@@ -170,8 +178,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
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.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -188,6 +196,7 @@ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -208,8 +217,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/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=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -219,8 +228,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-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/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde h1:ybF7AMzIUikL9x4LgwEmzhXtzRpKNqngme1VGDWz+Nk= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
@@ -236,7 +245,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20230415003630-3981d5d5e523 h1:zUQYeyyPLnSR6yMvLSOmLH37xDWCZ7BqlpE69fE5K3Q=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

View File

@@ -53,7 +53,7 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
} else { } else {
udpTimeout = int64(C.UDPTimeout.Seconds()) udpTimeout = int64(C.UDPTimeout.Seconds())
} }
inbound.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) inbound.udpNat = udpnat.New[netip.AddrPort](udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
inbound.connHandler = inbound inbound.connHandler = inbound
inbound.packetHandler = inbound inbound.packetHandler = inbound
inbound.packetUpstream = inbound.udpNat inbound.packetUpstream = inbound.udpNat

View File

@@ -64,11 +64,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
var err error var err error
switch { switch {
case options.Method == shadowsocks.MethodNone: case options.Method == shadowsocks.MethodNone:
inbound.service = shadowsocks.NewNoneService(ctx, options.UDPTimeout, inbound.upstreamContextHandler()) inbound.service = shadowsocks.NewNoneService(options.UDPTimeout, inbound.upstreamContextHandler())
case common.Contains(shadowaead.List, options.Method): case common.Contains(shadowaead.List, options.Method):
inbound.service, err = shadowaead.NewService(ctx, options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler()) inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
case common.Contains(shadowaead_2022.List, options.Method): case common.Contains(shadowaead_2022.List, options.Method):
inbound.service, err = shadowaead_2022.NewServiceWithPassword(ctx, options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc()) inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
default: default:
err = E.New("unsupported method: ", options.Method) err = E.New("unsupported method: ", options.Method)
} }

View File

@@ -57,7 +57,6 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
) )
if common.Contains(shadowaead_2022.List, options.Method) { if common.Contains(shadowaead_2022.List, options.Method) {
service, err = shadowaead_2022.NewMultiServiceWithPassword[int]( service, err = shadowaead_2022.NewMultiServiceWithPassword[int](
ctx,
options.Method, options.Method,
options.Password, options.Password,
udpTimeout, udpTimeout,
@@ -66,7 +65,6 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
) )
} else if common.Contains(shadowaead.List, options.Method) { } else if common.Contains(shadowaead.List, options.Method) {
service, err = shadowaead.NewMultiService[int]( service, err = shadowaead.NewMultiService[int](
ctx,
options.Method, options.Method,
udpTimeout, udpTimeout,
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))

View File

@@ -50,7 +50,6 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
udpTimeout = int64(C.UDPTimeout.Seconds()) udpTimeout = int64(C.UDPTimeout.Seconds())
} }
service, err := shadowaead_2022.NewRelayServiceWithPassword[int]( service, err := shadowaead_2022.NewRelayServiceWithPassword[int](
ctx,
options.Method, options.Method,
options.Password, options.Password,
udpTimeout, udpTimeout,

View File

@@ -45,7 +45,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
} }
tproxy.connHandler = tproxy tproxy.connHandler = tproxy
tproxy.oobPacketHandler = tproxy tproxy.oobPacketHandler = tproxy
tproxy.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, tproxy.upstreamContextHandler()) tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler())
tproxy.packetUpstream = tproxy.udpNat tproxy.packetUpstream = tproxy.udpNat
return tproxy return tproxy
} }

View File

@@ -177,7 +177,8 @@ func (t *Tun) Start() error {
Router: tunRouter, Router: tunRouter,
Handler: t, Handler: t,
Logger: t.logger, Logger: t.logger,
UnderPlatform: t.platformInterface != nil, ForwarderBindInterface: t.platformInterface != nil,
InterfaceFinder: t.router.InterfaceFinder(),
}) })
if err != nil { if err != nil {
return err return err

View File

@@ -3,6 +3,8 @@
package include package include
import ( import (
"context"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental" "github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
@@ -11,7 +13,7 @@ import (
) )
func init() { func init() {
experimental.RegisterClashServerConstructor(func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { experimental.RegisterClashServerConstructor(func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`) return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`)
}) })
} }

View File

@@ -1,14 +1,15 @@
package log package log
import ( import (
"context"
"io" "io"
"os" "os"
"time" "time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service/filemanager"
) )
type factoryWithFile struct { type factoryWithFile struct {
@@ -36,6 +37,7 @@ func (f *observableFactoryWithFile) Close() error {
} }
type Options struct { type Options struct {
Context context.Context
Options option.LogOptions Options option.LogOptions
Observable bool Observable bool
DefaultWriter io.Writer DefaultWriter io.Writer
@@ -65,7 +67,7 @@ func New(options Options) (Factory, error) {
logWriter = os.Stdout logWriter = os.Stdout
default: default:
var err error var err error
logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) logFile, err = filemanager.OpenFile(options.Context, logOptions.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -115,6 +115,7 @@ nav:
- ShadowTLS: examples/shadowtls.md - ShadowTLS: examples/shadowtls.md
- Clash API: examples/clash-api.md - Clash API: examples/clash-api.md
- WireGuard Direct: examples/wireguard-direct.md - WireGuard Direct: examples/wireguard-direct.md
- FakeIP: examples/fakeip.md
- Contributing: - Contributing:
- contributing/index.md - contributing/index.md
- Developing: - Developing:
@@ -147,6 +148,7 @@ extra:
link: https://github.com/SagerNet/sing-box link: https://github.com/SagerNet/sing-box
generator: false generator: false
plugins: plugins:
- search
- i18n: - i18n:
default_language: en default_language: en
languages: languages:

View File

@@ -10,6 +10,7 @@ type ClashAPIOptions struct {
StoreSelected bool `json:"store_selected,omitempty"` StoreSelected bool `json:"store_selected,omitempty"`
StoreFakeIP bool `json:"store_fakeip,omitempty"` StoreFakeIP bool `json:"store_fakeip,omitempty"`
CacheFile string `json:"cache_file,omitempty"` CacheFile string `json:"cache_file,omitempty"`
CacheID string `json:"cache_id,omitempty"`
} }
type SelectorOutboundOptions struct { type SelectorOutboundOptions struct {

View File

@@ -7,6 +7,7 @@ import (
) )
type DebugOptions struct { type DebugOptions struct {
Listen string `json:"listen,omitempty"`
GCPercent *int `json:"gc_percent,omitempty"` GCPercent *int `json:"gc_percent,omitempty"`
MaxStack *int `json:"max_stack,omitempty"` MaxStack *int `json:"max_stack,omitempty"`
MaxThreads *int `json:"max_threads,omitempty"` MaxThreads *int `json:"max_threads,omitempty"`

View File

@@ -20,9 +20,10 @@ type DNSServerOptions struct {
} }
type DNSClientOptions struct { type DNSClientOptions struct {
Strategy DomainStrategy `json:"strategy,omitempty"` Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"` DisableCache bool `json:"disable_cache,omitempty"`
DisableExpire bool `json:"disable_expire,omitempty"` DisableExpire bool `json:"disable_expire,omitempty"`
IndependentCache bool `json:"independent_cache,omitempty"`
} }
type DNSFakeIPOptions struct { type DNSFakeIPOptions struct {

View File

@@ -150,4 +150,5 @@ type MultiplexOptions struct {
MaxConnections int `json:"max_connections,omitempty"` MaxConnections int `json:"max_connections,omitempty"`
MinStreams int `json:"min_streams,omitempty"` MinStreams int `json:"min_streams,omitempty"`
MaxStreams int `json:"max_streams,omitempty"` MaxStreams int `json:"max_streams,omitempty"`
Padding bool `json:"padding,omitempty"`
} }

View File

@@ -20,6 +20,7 @@ type VLESSOutboundOptions struct {
Flow string `json:"flow,omitempty"` Flow string `json:"flow,omitempty"`
Network NetworkList `json:"network,omitempty"` Network NetworkList `json:"network,omitempty"`
TLS *OutboundTLSOptions `json:"tls,omitempty"` TLS *OutboundTLSOptions `json:"tls,omitempty"`
Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
Transport *V2RayTransportOptions `json:"transport,omitempty"` Transport *V2RayTransportOptions `json:"transport,omitempty"`
PacketEncoding *string `json:"packet_encoding,omitempty"` PacketEncoding *string `json:"packet_encoding,omitempty"`
} }

View File

@@ -11,6 +11,7 @@ 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/bufio"
"github.com/sagernet/sing/common/canceler" "github.com/sagernet/sing/common/canceler"
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"
@@ -101,29 +102,142 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
} }
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
var reader N.PacketReader = conn
var counters []N.CountFunc
var cachedPackets []*N.PacketBuffer
for {
reader, counters = N.UnwrapCountPacketReader(reader, counters)
if cachedReader, isCached := reader.(N.CachedPacketReader); isCached {
packet := cachedReader.ReadCachedPacket()
if packet != nil {
cachedPackets = append(cachedPackets, packet)
continue
}
}
if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created {
return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata)
}
break
}
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
fastClose, cancel := common.ContextWithCancelCause(ctx) fastClose, cancel := common.ContextWithCancelCause(ctx)
timeout := canceler.New(fastClose, cancel, C.DNSTimeout) timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
var group task.Group var group task.Group
group.Append0(func(ctx context.Context) error { group.Append0(func(ctx context.Context) error {
_buffer := buf.StackNewSize(dns.FixedPacketSize)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
for { for {
buffer.FullReset()
destination, err := conn.ReadPacket(buffer)
if err != nil {
cancel(err)
return err
}
var message mDNS.Msg var message mDNS.Msg
err = message.Unpack(buffer.Bytes()) var destination M.Socksaddr
if err != nil { var err error
cancel(err) if len(cachedPackets) > 0 {
return err packet := cachedPackets[0]
cachedPackets = cachedPackets[1:]
for _, counter := range counters {
counter(int64(packet.Buffer.Len()))
}
err = message.Unpack(packet.Buffer.Bytes())
packet.Buffer.Release()
if err != nil {
cancel(err)
return err
}
destination = packet.Destination
} else {
buffer := buf.NewPacket()
destination, err = conn.ReadPacket(buffer)
if err != nil {
buffer.Release()
cancel(err)
return err
}
for _, counter := range counters {
counter(int64(buffer.Len()))
}
err = message.Unpack(buffer.Bytes())
buffer.Release()
if err != nil {
cancel(err)
return err
}
timeout.Update()
}
metadataInQuery := metadata
go func() error {
response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
if err != nil {
cancel(err)
return err
}
timeout.Update()
responseBuffer := buf.NewPacket()
n, err := response.PackBuffer(responseBuffer.FreeBytes())
if err != nil {
cancel(err)
responseBuffer.Release()
return err
}
responseBuffer.Truncate(len(n))
err = conn.WritePacket(responseBuffer, destination)
if err != nil {
cancel(err)
}
return err
}()
}
})
group.Cleanup(func() {
conn.Close()
})
return group.Run(fastClose)
}
func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWaiter N.PacketReadWaiter, readCounters []N.CountFunc, cached []*N.PacketBuffer, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
fastClose, cancel := common.ContextWithCancelCause(ctx)
timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
var group task.Group
group.Append0(func(ctx context.Context) error {
var buffer *buf.Buffer
readWaiter.InitializeReadWaiter(func() *buf.Buffer {
buffer = buf.NewSize(dns.FixedPacketSize)
buffer.FullReset()
return buffer
})
defer readWaiter.InitializeReadWaiter(nil)
for {
var message mDNS.Msg
var destination M.Socksaddr
var err error
if len(cached) > 0 {
packet := cached[0]
cached = cached[1:]
for _, counter := range readCounters {
counter(int64(packet.Buffer.Len()))
}
err = message.Unpack(packet.Buffer.Bytes())
packet.Buffer.Release()
if err != nil {
cancel(err)
return err
}
destination = packet.Destination
} else {
destination, err = readWaiter.WaitReadPacket()
if err != nil {
buffer.Release()
cancel(err)
return err
}
for _, counter := range readCounters {
counter(int64(buffer.Len()))
}
err = message.Unpack(buffer.Bytes())
buffer.Release()
if err != nil {
cancel(err)
return err
}
timeout.Update()
} }
timeout.Update()
metadataInQuery := metadata metadataInQuery := metadata
go func() error { go func() error {
response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message) response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)

View File

@@ -11,8 +11,7 @@ import (
"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/sip003" "github.com/sagernet/sing-box/transport/sip003"
"github.com/sagernet/sing-shadowsocks" "github.com/sagernet/sing-shadowsocks2"
"github.com/sagernet/sing-shadowsocks/shadowimpl"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@@ -30,11 +29,13 @@ type Shadowsocks struct {
serverAddr M.Socksaddr serverAddr M.Socksaddr
plugin sip003.Plugin plugin sip003.Plugin
uotClient *uot.Client uotClient *uot.Client
multiplexDialer N.Dialer multiplexDialer *mux.Client
} }
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) { func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
method, err := shadowimpl.FetchMethod(options.Method, options.Password, router.TimeFunc()) method, err := shadowsocks.CreateMethod(ctx, options.Method, shadowsocks.MethodOptions{
Password: options.Password,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -58,7 +59,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
} }
uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions) uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions)
if !uotOptions.Enabled { if !uotOptions.Enabled {
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions)) outbound.multiplexDialer, err = mux.NewClientWithOptions((*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -127,8 +128,15 @@ func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn
return NewPacketConnection(ctx, h, conn, metadata) return NewPacketConnection(ctx, h, conn, metadata)
} }
func (h *Shadowsocks) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *Shadowsocks) Close() error { func (h *Shadowsocks) Close() error {
return common.Close(h.multiplexDialer) return common.Close(common.PtrOrNil(h.multiplexDialer))
} }
var _ N.Dialer = (*shadowsocksDialer)(nil) var _ N.Dialer = (*shadowsocksDialer)(nil)

View File

@@ -27,7 +27,7 @@ type Trojan struct {
dialer N.Dialer dialer N.Dialer
serverAddr M.Socksaddr serverAddr M.Socksaddr
key [56]byte key [56]byte
multiplexDialer N.Dialer multiplexDialer *mux.Client
tlsConfig tls.Config tlsConfig tls.Config
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
} }
@@ -58,7 +58,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
return nil, E.Cause(err, "create client transport: ", options.Transport.Type) return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
} }
} }
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*trojanDialer)(outbound), common.PtrValueOrDefault(options.Multiplex)) outbound.multiplexDialer, err = mux.NewClientWithOptions((*trojanDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -103,8 +103,15 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
return NewPacketConnection(ctx, h, conn, metadata) return NewPacketConnection(ctx, h, conn, metadata)
} }
func (h *Trojan) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *Trojan) Close() error { func (h *Trojan) Close() error {
return common.Close(h.multiplexDialer, h.transport) return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
} }
type trojanDialer Trojan type trojanDialer Trojan

View File

@@ -233,7 +233,7 @@ func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
outbounds = append(outbounds, detour) outbounds = append(outbounds, detour)
} }
} }
sort.Slice(outbounds, func(i, j int) bool { sort.SliceStable(outbounds, func(i, j int) bool {
oi := outbounds[i] oi := outbounds[i]
oj := outbounds[j] oj := outbounds[j]
hi := g.history.LoadURLTestHistory(RealTag(oi)) hi := g.history.LoadURLTestHistory(RealTag(oi))

View File

@@ -6,6 +6,7 @@ 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/mux"
"github.com/sagernet/sing-box/common/tls" "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"
@@ -24,13 +25,14 @@ var _ adapter.Outbound = (*VLESS)(nil)
type VLESS struct { type VLESS struct {
myOutboundAdapter myOutboundAdapter
dialer N.Dialer dialer N.Dialer
client *vless.Client client *vless.Client
serverAddr M.Socksaddr serverAddr M.Socksaddr
tlsConfig tls.Config multiplexDialer *mux.Client
transport adapter.V2RayClientTransport tlsConfig tls.Config
packetAddr bool transport adapter.V2RayClientTransport
xudp bool packetAddr bool
xudp bool
} }
func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) { func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) {
@@ -75,10 +77,65 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
if err != nil { if err != nil {
return nil, err return nil, err
} }
outbound.multiplexDialer, err = mux.NewClientWithOptions((*vlessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
if err != nil {
return nil, err
}
return outbound, nil return outbound, nil
} }
func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if h.multiplexDialer == nil {
switch N.NetworkName(network) {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound connection to ", destination)
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
return (*vlessDialer)(h).DialContext(ctx, network, destination)
} else {
switch N.NetworkName(network) {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
}
return h.multiplexDialer.DialContext(ctx, network, destination)
}
}
func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
if h.multiplexDialer == nil {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return (*vlessDialer)(h).ListenPacket(ctx, destination)
} else {
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
return h.multiplexDialer.ListenPacket(ctx, destination)
}
}
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *VLESS) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *VLESS) Close() error {
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
}
type vlessDialer VLESS
func (h *vlessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
metadata.Destination = destination metadata.Destination = destination
@@ -120,7 +177,7 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S
} }
} }
func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination) h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
ctx, metadata := adapter.AppendContext(ctx) ctx, metadata := adapter.AppendContext(ctx)
metadata.Outbound = h.tag metadata.Outbound = h.tag
@@ -154,15 +211,3 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
return h.client.DialEarlyPacketConn(conn, destination) return h.client.DialEarlyPacketConn(conn, destination)
} }
} }
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewConnection(ctx, h, conn, metadata)
}
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewPacketConnection(ctx, h, conn, metadata)
}
func (h *VLESS) Close() error {
return common.Close(h.transport)
}

View File

@@ -27,7 +27,7 @@ type VMess struct {
dialer N.Dialer dialer N.Dialer
client *vmess.Client client *vmess.Client
serverAddr M.Socksaddr serverAddr M.Socksaddr
multiplexDialer N.Dialer multiplexDialer *mux.Client
tlsConfig tls.Config tlsConfig tls.Config
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
packetAddr bool packetAddr bool
@@ -59,7 +59,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
return nil, E.Cause(err, "create client transport: ", options.Transport.Type) return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
} }
} }
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*vmessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex)) outbound.multiplexDialer, err = mux.NewClientWithOptions((*vmessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -97,8 +97,15 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
return outbound, nil return outbound, nil
} }
func (h *VMess) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *VMess) Close() error { func (h *VMess) Close() error {
return common.Close(h.multiplexDialer, h.transport) return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
} }
func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

View File

@@ -113,7 +113,12 @@ func NewRouter(
defaultMark: options.DefaultMark, defaultMark: options.DefaultMark,
platformInterface: platformInterface, platformInterface: platformInterface,
} }
router.dnsClient = dns.NewClient(ctx, dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger) router.dnsClient = dns.NewClient(dns.ClientOptions{
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
Logger: router.dnsLogger,
})
for i, ruleOptions := range options.Rules { for i, ruleOptions := range options.Rules {
routeRule, err := NewRule(router, router.logger, ruleOptions) routeRule, err := NewRule(router, router.logger, ruleOptions)
if err != nil { if err != nil {
@@ -274,7 +279,8 @@ func NewRouter(
router.networkMonitor = networkMonitor router.networkMonitor = networkMonitor
networkMonitor.RegisterCallback(router.interfaceFinder.update) networkMonitor.RegisterCallback(router.interfaceFinder.update)
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{ interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
OverrideAndroidVPN: options.OverrideAndroidVPN, OverrideAndroidVPN: options.OverrideAndroidVPN,
UnderNetworkExtension: platformInterface != nil && platformInterface.UnderNetworkExtension(),
}) })
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")
@@ -598,7 +604,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
switch metadata.Destination.Fqdn { switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn: case mux.Destination.Fqdn:
r.logger.InfoContext(ctx, "inbound multiplex connection") r.logger.InfoContext(ctx, "inbound multiplex connection")
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata) handler := adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r)
return mux.HandleConnection(ctx, handler, r.logger, conn, adapter.UpstreamMetadata(metadata))
case vmess.MuxDestination.Fqdn: case vmess.MuxDestination.Fqdn:
r.logger.InfoContext(ctx, "inbound legacy multiplex connection") r.logger.InfoContext(ctx, "inbound legacy multiplex connection")
return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r)) return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r))
@@ -896,7 +903,7 @@ func (r *Router) AutoDetectInterface() bool {
} }
func (r *Router) AutoDetectInterfaceFunc() control.Func { func (r *Router) AutoDetectInterfaceFunc() control.Func {
if r.platformInterface != nil { if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
return r.platformInterface.AutoDetectInterfaceControl() return r.platformInterface.AutoDetectInterfaceControl()
} else { } else {
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) { return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
@@ -987,9 +994,22 @@ func (r *Router) notifyNetworkUpdate(int) error {
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified())) r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
} }
if conntrack.Enabled { conntrack.Close()
conntrack.Close()
} for _, outbound := range r.outbounds {
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
if isListener {
err := listener.InterfaceUpdated()
if err != nil {
return err
}
}
}
return nil
}
func (r *Router) ResetNetwork() error {
conntrack.Close()
for _, outbound := range r.outbounds { for _, outbound := range r.outbounds {
listener, isListener := outbound.(adapter.InterfaceUpdateListener) listener, isListener := outbound.(adapter.InterfaceUpdateListener)

View File

@@ -73,23 +73,31 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
if len(message.Question) > 0 { if len(message.Question) > 0 {
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String())) r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
} }
ctx, metadata := adapter.AppendContext(ctx) var (
if len(message.Question) > 0 { response *mDNS.Msg
metadata.QueryType = message.Question[0].Qtype cached bool
switch metadata.QueryType { err error
case mDNS.TypeA: )
metadata.IPVersion = 4 response, cached = r.dnsClient.ExchangeCache(ctx, message)
case mDNS.TypeAAAA: if !cached {
metadata.IPVersion = 6 ctx, metadata := adapter.AppendContext(ctx)
if len(message.Question) > 0 {
metadata.QueryType = message.Question[0].Qtype
switch metadata.QueryType {
case mDNS.TypeA:
metadata.IPVersion = 4
case mDNS.TypeAAAA:
metadata.IPVersion = 6
}
metadata.Domain = fqdnToDomain(message.Question[0].Name)
}
ctx, transport, strategy := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
response, err = r.dnsClient.Exchange(ctx, transport, message, strategy)
if err != nil && len(message.Question) > 0 {
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
} }
metadata.Domain = fqdnToDomain(message.Question[0].Name)
}
ctx, transport, strategy := r.matchDNS(ctx)
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
defer cancel()
response, err := r.dnsClient.Exchange(ctx, transport, message, strategy)
if err != nil && len(message.Question) > 0 {
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
} }
if len(message.Question) > 0 && response != nil { if len(message.Question) > 0 && response != nil {
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer) LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)

View File

@@ -18,6 +18,7 @@ import (
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"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/service/filemanager"
) )
func (r *Router) GeoIPReader() *geoip.Reader { func (r *Router) GeoIPReader() *geoip.Reader {
@@ -51,7 +52,7 @@ func (r *Router) prepareGeoIPDatabase() error {
geoPath = foundPath geoPath = foundPath
} }
} }
geoPath = C.BasePath(geoPath) geoPath = filemanager.BasePath(r.ctx, geoPath)
if rw.FileExists(geoPath) { if rw.FileExists(geoPath) {
geoReader, codes, err := geoip.Open(geoPath) geoReader, codes, err := geoip.Open(geoPath)
if err == nil { if err == nil {
@@ -95,7 +96,7 @@ func (r *Router) prepareGeositeDatabase() error {
geoPath = foundPath geoPath = foundPath
} }
} }
geoPath = C.BasePath(geoPath) geoPath = filemanager.BasePath(r.ctx, geoPath)
if !rw.FileExists(geoPath) { if !rw.FileExists(geoPath) {
r.logger.Warn("geosite database not exists: ", geoPath) r.logger.Warn("geosite database not exists: ", geoPath)
var err error var err error
@@ -142,10 +143,10 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
} }
if parentDir := filepath.Dir(savePath); parentDir != "" { if parentDir := filepath.Dir(savePath); parentDir != "" {
os.MkdirAll(parentDir, 0o755) filemanager.MkdirAll(r.ctx, parentDir, 0o755)
} }
saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644) saveFile, err := filemanager.Create(r.ctx, savePath)
if err != nil { if err != nil {
return E.Cause(err, "open output file: ", downloadURL) return E.Cause(err, "open output file: ", downloadURL)
} }
@@ -190,10 +191,10 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
} }
if parentDir := filepath.Dir(savePath); parentDir != "" { if parentDir := filepath.Dir(savePath); parentDir != "" {
os.MkdirAll(parentDir, 0o755) filemanager.MkdirAll(r.ctx, parentDir, 0o755)
} }
saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644) saveFile, err := filemanager.Create(r.ctx, savePath)
if err != nil { if err != nil {
return E.Cause(err, "open output file: ", downloadURL) return E.Cause(err, "open output file: ", downloadURL)
} }

View File

@@ -9,9 +9,9 @@ replace github.com/sagernet/sing-box => ../
require ( require (
github.com/docker/docker v20.10.18+incompatible github.com/docker/docker v20.10.18+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.4.0+incompatible github.com/gofrs/uuid/v5 v5.0.0
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31 github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507
github.com/spyzhov/ajson v0.7.1 github.com/spyzhov/ajson v0.7.1
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
go.uber.org/goleak v1.2.0 go.uber.org/goleak v1.2.0
@@ -21,7 +21,7 @@ require (
require ( require (
berty.tech/go-libtor v1.0.385 // indirect berty.tech/go-libtor v1.0.385 // indirect
github.com/Dreamacro/clash v1.15.0 // indirect github.com/Dreamacro/clash v1.15.0 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
github.com/caddyserver/certmagic v0.17.2 // indirect github.com/caddyserver/certmagic v0.17.2 // indirect
@@ -36,7 +36,6 @@ require (
github.com/go-chi/cors v1.2.1 // indirect github.com/go-chi/cors v1.2.1 // indirect
github.com/go-chi/render v1.0.2 // indirect github.com/go-chi/render v1.0.2 // 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/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // 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
@@ -51,7 +50,7 @@ require (
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/mholt/acmez v1.1.0 // indirect github.com/mholt/acmez v1.1.0 // indirect
github.com/miekg/dns v1.1.53 // indirect github.com/miekg/dns v1.1.53 // indirect
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/ooni/go-libtor v1.1.7 // indirect github.com/ooni/go-libtor v1.1.7 // indirect
@@ -71,15 +70,16 @@ require (
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 // indirect github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc // indirect
github.com/sagernet/sing-shadowtls v0.1.0 // indirect github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 // indirect
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 // indirect github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b // indirect
github.com/sagernet/sing-vmess v0.1.3 // indirect github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b // indirect
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
@@ -93,15 +93,12 @@ require (
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.54.0 // indirect google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.4.0 // indirect gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 // indirect
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/blake3 v1.1.7 // indirect
) )
//replace github.com/sagernet/sing => ../../sing

View File

@@ -3,8 +3,8 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE= github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE= github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
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.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
@@ -43,8 +43,6 @@ 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.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -84,8 +82,8 @@ github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs= github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
@@ -128,18 +126,20 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
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.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31 h1:qgq8jeY/rbnY9NwYXByO//AP0ByIxnsKUxQx1tOB3W0= github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 h1:+dDVjW20IT+e8maKryaDeRY2+RFmTFdrQeIzqE2WOss=
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI= github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI= github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU= github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 h1:87jyxzTjq01VgEiUVSMNRKjCfsSfp/QwyUVT37eXY50=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q= github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88=
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog=
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b h1:9NsciSJGwzdkXwVvT2c2g+RvkTVkANeBLr2l+soJ7LM=
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE= github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b/go.mod h1:DD7Ce2Gt0GFc6I/1+Uw4D/aUlBsGqrQsC52CMK/V818=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
@@ -148,9 +148,8 @@ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfI
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A= github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
@@ -158,7 +157,6 @@ github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzy
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -218,13 +216,10 @@ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-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=
@@ -243,13 +238,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/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=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
@@ -272,8 +266,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
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=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 h1:zUQYeyyPLnSR6yMvLSOmLH37xDWCZ7BqlpE69fE5K3Q=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4= gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

View File

@@ -4,7 +4,6 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/sagernet/sing-box/common/mux"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022" "github.com/sagernet/sing-shadowsocks/shadowaead_2022"
@@ -12,24 +11,33 @@ import (
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
) )
var muxProtocols = []mux.Protocol{ var muxProtocols = []string{
mux.ProtocolYAMux, "smux",
mux.ProtocolSMux, "yamux",
"h2mux",
} }
func TestVMessSMux(t *testing.T) { func TestMux(t *testing.T) {
testVMessMux(t, mux.ProtocolSMux.String())
}
func TestShadowsocksMux(t *testing.T) {
for _, protocol := range muxProtocols { for _, protocol := range muxProtocols {
t.Run(protocol.String(), func(t *testing.T) { t.Run(protocol, func(t *testing.T) {
testShadowsocksMux(t, protocol.String()) options := option.MultiplexOptions{
Enabled: true,
Protocol: protocol,
}
t.Run("shadowsocks", func(t *testing.T) {
testShadowsocksMux(t, options)
})
t.Run("vmess", func(t *testing.T) {
testVMessMux(t, options)
})
t.Run("vless", func(t *testing.T) {
testVLESSMux(t, options)
})
}) })
} }
} }
func testShadowsocksMux(t *testing.T, protocol string) { func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
@@ -68,12 +76,9 @@ func testShadowsocksMux(t *testing.T, protocol string) {
Server: "127.0.0.1", Server: "127.0.0.1",
ServerPort: serverPort, ServerPort: serverPort,
}, },
Method: method, Method: method,
Password: password, Password: password,
MultiplexOptions: &option.MultiplexOptions{ MultiplexOptions: &options,
Enabled: true,
Protocol: protocol,
},
}, },
}, },
}, },
@@ -91,7 +96,7 @@ func testShadowsocksMux(t *testing.T, protocol string) {
testSuit(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func testVMessMux(t *testing.T, protocol string) { func testVMessMux(t *testing.T, options option.MultiplexOptions) {
user, _ := uuid.NewV4() user, _ := uuid.NewV4()
startInstance(t, option.Options{ startInstance(t, option.Options{
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
@@ -132,12 +137,9 @@ func testVMessMux(t *testing.T, protocol string) {
Server: "127.0.0.1", Server: "127.0.0.1",
ServerPort: serverPort, ServerPort: serverPort,
}, },
Security: "auto", Security: "auto",
UUID: user.String(), UUID: user.String(),
Multiplex: &option.MultiplexOptions{ Multiplex: &options,
Enabled: true,
Protocol: protocol,
},
}, },
}, },
}, },
@@ -154,3 +156,63 @@ func testVMessMux(t *testing.T, protocol string) {
}) })
testSuit(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func testVLESSMux(t *testing.T, options option.MultiplexOptions) {
user, _ := uuid.NewV4()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
UUID: user.String(),
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeVLESS,
Tag: "vless-out",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: user.String(),
Multiplex: &options,
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "vless-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}

View File

@@ -0,0 +1,53 @@
package main
import (
"net/netip"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks2/shadowstream"
F "github.com/sagernet/sing/common/format"
)
func TestShadowsocksLegacy(t *testing.T) {
testShadowsocksLegacy(t, shadowstream.MethodList[0])
}
func testShadowsocksLegacy(t *testing.T, method string) {
startDockerContainer(t, DockerOptions{
Image: ImageShadowsocksLegacy,
Ports: []uint16{serverPort},
Env: []string{
"SS_MODULE=ss-server",
F.ToString("SS_CONFIG=-s 0.0.0.0 -u -p 10000 -m ", method, " -k FzcLbKs2dY9mhL"),
},
})
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeShadowsocks,
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
Method: method,
Password: "FzcLbKs2dY9mhL",
},
},
},
})
testSuitSimple(t, clientPort, testPort)
}

View File

@@ -40,6 +40,10 @@ func TestShadowsocks(t *testing.T) {
} }
} }
func TestShadowsocksNone(t *testing.T) {
testShadowsocksSelf(t, "none", "")
}
func TestShadowsocks2022(t *testing.T) { func TestShadowsocks2022(t *testing.T) {
for _, method16 := range []string{ for _, method16 := range []string{
"2022-blake3-aes-128-gcm", "2022-blake3-aes-128-gcm",

View File

@@ -19,7 +19,7 @@ func TestUDPNatClose(t *testing.T) {
defer cancel() defer cancel()
connCtx, connCancel := common.ContextWithCancelCause(context.Background()) connCtx, connCancel := common.ContextWithCancelCause(context.Background())
defer connCancel(net.ErrClosed) defer connCancel(net.ErrClosed)
service := udpnat.New[int](ctx, 1, &testUDPNatCloseHandler{connCancel}) service := udpnat.New[int](1, &testUDPNatCloseHandler{connCancel})
service.NewPacket(ctx, 0, buf.As([]byte("Hello")), M.Metadata{}, func(natConn N.PacketConn) N.PacketWriter { service.NewPacket(ctx, 0, buf.As([]byte("Hello")), M.Metadata{}, func(natConn N.PacketConn) N.PacketWriter {
return &testPacketWriter{} return &testPacketWriter{}
}) })

View File

@@ -0,0 +1,37 @@
package fakeip
import (
"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"
)
func (c *NATPacketConn) CreatePacketReadWaiter() (N.PacketReadWaiter, bool) {
waiter, created := bufio.CreatePacketReadWaiter(c.PacketConn)
if !created {
return nil, false
}
return &waitNATPacketConn{c, waiter}, true
}
type waitNATPacketConn struct {
*NATPacketConn
waiter N.PacketReadWaiter
}
func (c *waitNATPacketConn) InitializeReadWaiter(newBuffer func() *buf.Buffer) {
c.waiter.InitializeReadWaiter(newBuffer)
}
func (c *waitNATPacketConn) WaitReadPacket() (destination M.Socksaddr, err error) {
destination, err = c.waiter.WaitReadPacket()
if socksaddrWithoutPort(destination) == c.origin {
destination = M.Socksaddr{
Addr: c.destination.Addr,
Fqdn: c.destination.Fqdn,
Port: destination.Port,
}
}
return
}

View File

@@ -45,6 +45,7 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
if hostOpt, loaded := pluginOpts.Get("host"); loaded { if hostOpt, loaded := pluginOpts.Get("host"); loaded {
host = hostOpt host = hostOpt
tlsOptions.ServerName = hostOpt
} }
if pathOpt, loaded := pluginOpts.Get("path"); loaded { if pathOpt, loaded := pluginOpts.Get("path"); loaded {
path = pathOpt path = pathOpt

View File

@@ -12,6 +12,7 @@ import (
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2rayhttp" "github.com/sagernet/sing-box/transport/v2rayhttp"
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"
@@ -33,9 +34,16 @@ type Client struct {
transport *http2.Transport transport *http2.Transport
options option.V2RayGRPCOptions options option.V2RayGRPCOptions
url *url.URL url *url.URL
host string
} }
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport { func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
var host string
if tlsConfig != nil && tlsConfig.ServerName() != "" {
host = M.ParseSocksaddrHostPort(tlsConfig.ServerName(), serverAddr.Port).String()
} else {
host = serverAddr.String()
}
client := &Client{ client := &Client{
ctx: ctx, ctx: ctx,
dialer: dialer, dialer: dialer,
@@ -52,6 +60,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
Path: "/" + options.ServiceName + "/Tun", Path: "/" + options.ServiceName + "/Tun",
RawPath: "/" + url.PathEscape(options.ServiceName) + "/Tun", RawPath: "/" + url.PathEscape(options.ServiceName) + "/Tun",
}, },
host: host,
} }
if tlsConfig == nil { if tlsConfig == nil {
@@ -81,15 +90,19 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
Body: pipeInReader, Body: pipeInReader,
URL: c.url, URL: c.url,
Header: defaultClientHeader, Header: defaultClientHeader,
Host: c.host,
} }
request = request.WithContext(ctx) request = request.WithContext(ctx)
conn := newLateGunConn(pipeInWriter) conn := newLateGunConn(pipeInWriter)
go func() { go func() {
response, err := c.transport.RoundTrip(request) response, err := c.transport.RoundTrip(request)
if err == nil { if err != nil {
conn.setup(response.Body, nil)
} else {
conn.setup(nil, err) conn.setup(nil, err)
} else if response.StatusCode != 200 {
response.Body.Close()
conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
} else {
conn.setup(response.Body, nil)
} }
}() }()
return conn, nil return conn, nil

View File

@@ -139,15 +139,16 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
default: default:
request.Host = c.host[rand.Intn(hostLen)] request.Host = c.host[rand.Intn(hostLen)]
} }
conn := newLateHTTPConn(pipeInWriter) conn := NewLateHTTPConn(pipeInWriter)
go func() { go func() {
response, err := c.transport.RoundTrip(request) response, err := c.transport.RoundTrip(request)
if err != nil { if err != nil {
conn.setup(nil, err) conn.Setup(nil, err)
} else if response.StatusCode != 200 { } else if response.StatusCode != 200 {
conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status)) response.Body.Close()
conn.Setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
} else { } else {
conn.setup(response.Body, nil) conn.Setup(response.Body, nil)
} }
}() }()
return conn, nil return conn, nil

View File

@@ -140,14 +140,14 @@ func NewHTTPConn(reader io.Reader, writer io.Writer) HTTP2Conn {
} }
} }
func newLateHTTPConn(writer io.Writer) *HTTP2Conn { func NewLateHTTPConn(writer io.Writer) *HTTP2Conn {
return &HTTP2Conn{ return &HTTP2Conn{
create: make(chan struct{}), create: make(chan struct{}),
writer: writer, writer: writer,
} }
} }
func (c *HTTP2Conn) setup(reader io.Reader, err error) { func (c *HTTP2Conn) Setup(reader io.Reader, err error) {
c.reader = reader c.reader = reader
c.err = err c.err = err
close(c.create) close(c.create)
@@ -199,13 +199,13 @@ func (c *HTTP2Conn) NeedAdditionalReadDeadline() bool {
type ServerHTTPConn struct { type ServerHTTPConn struct {
HTTP2Conn HTTP2Conn
flusher http.Flusher Flusher http.Flusher
} }
func (c *ServerHTTPConn) Write(b []byte) (n int, err error) { func (c *ServerHTTPConn) Write(b []byte) (n int, err error) {
n, err = c.writer.Write(b) n, err = c.writer.Write(b)
if err == nil { if err == nil {
c.flusher.Flush() c.Flusher.Flush()
} }
return return
} }
@@ -246,6 +246,11 @@ func (w *HTTP2ConnWrapper) CloseWrapper() {
w.closed = true w.closed = true
} }
func (w *HTTP2ConnWrapper) Close() error {
w.CloseWrapper()
return w.ExtendedConn.Close()
}
func (w *HTTP2ConnWrapper) Upstream() any { func (w *HTTP2ConnWrapper) Upstream() any {
return w.ExtendedConn return w.ExtendedConn
} }

View File

@@ -42,11 +42,19 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
if err != nil { if err != nil {
return nil, err return nil, err
} }
return tls.ClientHandshake(ctx, conn, tlsConfig) tlsConn, err := tls.ClientHandshake(ctx, conn, tlsConfig)
if err != nil {
return nil, err
}
return &deadConn{tlsConn}, nil
} }
} else { } else {
wsDialer.NetDialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { wsDialer.NetDialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
if err != nil {
return nil, err
}
return &deadConn{conn}, nil
} }
} }
var uri url.URL var uri url.URL

View File

@@ -0,0 +1,22 @@
package v2raywebsocket
import (
"net"
"time"
)
type deadConn struct {
net.Conn
}
func (c *deadConn) SetDeadline(t time.Time) error {
return nil
}
func (c *deadConn) SetReadDeadline(t time.Time) error {
return nil
}
func (c *deadConn) SetWriteDeadline(t time.Time) error {
return nil
}

View File

@@ -130,7 +130,7 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
var addonsLen int var addonsLen int
if request.Flow != "" { if request.Flow != "" {
addonsLen += 1 // protobuf header addonsLen += 1 // protobuf header
addonsLen += UvarintLen(uint64(len(request.Flow))) addonsLen += rw.UVariantLen(uint64(len(request.Flow)))
addonsLen += len(request.Flow) addonsLen += len(request.Flow)
requestLen += addonsLen requestLen += addonsLen
} }
@@ -150,8 +150,8 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
) )
if addonsLen > 0 { if addonsLen > 0 {
common.Must(buffer.WriteByte(10)) common.Must(buffer.WriteByte(10))
binary.PutUvarint(buffer.Extend(UvarintLen(uint64(len(request.Flow)))), uint64(len(request.Flow))) binary.PutUvarint(buffer.Extend(rw.UVariantLen(uint64(len(request.Flow)))), uint64(len(request.Flow)))
common.Must(common.Error(buffer.Write([]byte(request.Flow)))) common.Must(common.Error(buffer.WriteString(request.Flow)))
} }
common.Must( common.Must(
buffer.WriteByte(request.Command), buffer.WriteByte(request.Command),
@@ -174,7 +174,7 @@ func EncodeRequest(request Request, buffer *buf.Buffer) {
var addonsLen int var addonsLen int
if request.Flow != "" { if request.Flow != "" {
addonsLen += 1 // protobuf header addonsLen += 1 // protobuf header
addonsLen += UvarintLen(uint64(len(request.Flow))) addonsLen += rw.UVariantLen(uint64(len(request.Flow)))
addonsLen += len(request.Flow) addonsLen += len(request.Flow)
requestLen += addonsLen requestLen += addonsLen
} }
@@ -189,8 +189,8 @@ func EncodeRequest(request Request, buffer *buf.Buffer) {
) )
if addonsLen > 0 { if addonsLen > 0 {
common.Must(buffer.WriteByte(10)) common.Must(buffer.WriteByte(10))
binary.PutUvarint(buffer.Extend(UvarintLen(uint64(len(request.Flow)))), uint64(len(request.Flow))) binary.PutUvarint(buffer.Extend(rw.UVariantLen(uint64(len(request.Flow)))), uint64(len(request.Flow)))
common.Must(common.Error(buffer.Write([]byte(request.Flow)))) common.Must(common.Error(buffer.WriteString(request.Flow)))
} }
common.Must( common.Must(
buffer.WriteByte(request.Command), buffer.WriteByte(request.Command),
@@ -210,7 +210,7 @@ func RequestLen(request Request) int {
var addonsLen int var addonsLen int
if request.Flow != "" { if request.Flow != "" {
addonsLen += 1 // protobuf header addonsLen += 1 // protobuf header
addonsLen += UvarintLen(uint64(len(request.Flow))) addonsLen += rw.UVariantLen(uint64(len(request.Flow)))
addonsLen += len(request.Flow) addonsLen += len(request.Flow)
requestLen += addonsLen requestLen += addonsLen
} }
@@ -229,7 +229,7 @@ func WritePacketRequest(writer io.Writer, request Request, payload []byte) error
var addonsLen int var addonsLen int
/*if request.Flow != "" { /*if request.Flow != "" {
addonsLen += 1 // protobuf header addonsLen += 1 // protobuf header
addonsLen += UvarintLen(uint64(len(request.Flow))) addonsLen += rw.UVariantLen(uint64(len(request.Flow)))
addonsLen += len(request.Flow) addonsLen += len(request.Flow)
requestLen += addonsLen requestLen += addonsLen
}*/ }*/
@@ -251,8 +251,8 @@ func WritePacketRequest(writer io.Writer, request Request, payload []byte) error
if addonsLen > 0 { if addonsLen > 0 {
common.Must(buffer.WriteByte(10)) common.Must(buffer.WriteByte(10))
binary.PutUvarint(buffer.Extend(UvarintLen(uint64(len(request.Flow)))), uint64(len(request.Flow))) binary.PutUvarint(buffer.Extend(rw.UVariantLen(uint64(len(request.Flow)))), uint64(len(request.Flow)))
common.Must(common.Error(buffer.Write([]byte(request.Flow)))) common.Must(common.Error(buffer.WriteString(request.Flow)))
} }
common.Must( common.Must(
@@ -290,8 +290,3 @@ func ReadResponse(reader io.Reader) error {
} }
return nil return nil
} }
func UvarintLen(value uint64) int {
var buffer [binary.MaxVarintLen64]byte
return binary.PutUvarint(buffer[:], value)
}

View File

@@ -101,7 +101,7 @@ func (c *ClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint1
return []conn.ReceiveFunc{c.receive}, 0, nil return []conn.ReceiveFunc{c.receive}, 0, nil
} }
func (c *ClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) { func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint) (count int, err error) {
udpConn, err := c.connect() udpConn, err := c.connect()
if err != nil { if err != nil {
select { select {
@@ -113,22 +113,26 @@ func (c *ClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
err = nil err = nil
return return
} }
n, addr, err := udpConn.ReadFrom(b) n, addr, err := udpConn.ReadFrom(packets[0])
if err != nil { if err != nil {
udpConn.Close() udpConn.Close()
select { select {
case <-c.done: case <-c.done:
default: default:
c.errorHandler.NewError(context.Background(), E.Cause(err, "read packet")) c.errorHandler.NewError(context.Background(), E.Cause(err, "read packet"))
err = nil
} }
return return
} }
sizes[0] = n
if n > 3 { if n > 3 {
b := packets[0]
b[1] = 0 b[1] = 0
b[2] = 0 b[2] = 0
b[3] = 0 b[3] = 0
} }
ep = Endpoint(M.SocksaddrFromNet(addr)) eps[0] = Endpoint(M.SocksaddrFromNet(addr))
count = 1
return return
} }
@@ -155,32 +159,39 @@ func (c *ClientBind) SetMark(mark uint32) error {
return nil return nil
} }
func (c *ClientBind) Send(b []byte, ep conn.Endpoint) error { func (c *ClientBind) Send(bufs [][]byte, ep conn.Endpoint) error {
udpConn, err := c.connect() udpConn, err := c.connect()
if err != nil { if err != nil {
return err return err
} }
destination := M.Socksaddr(ep.(Endpoint)) destination := M.Socksaddr(ep.(Endpoint))
if len(b) > 3 { for _, b := range bufs {
reserved, loaded := c.reservedForEndpoint[destination] if len(b) > 3 {
if !loaded { reserved, loaded := c.reservedForEndpoint[destination]
reserved = c.reserved if !loaded {
reserved = c.reserved
}
b[1] = reserved[0]
b[2] = reserved[1]
b[3] = reserved[2]
}
_, err = udpConn.WriteTo(b, destination)
if err != nil {
udpConn.Close()
return err
} }
b[1] = reserved[0]
b[2] = reserved[1]
b[3] = reserved[2]
} }
_, err = udpConn.WriteTo(b, destination) return nil
if err != nil {
udpConn.Close()
}
return err
} }
func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) { func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return Endpoint(M.ParseSocksaddr(s)), nil return Endpoint(M.ParseSocksaddr(s)), nil
} }
func (c *ClientBind) BatchSize() int {
return 1
}
type wireConn struct { type wireConn struct {
net.PacketConn net.PacketConn
access sync.Mutex access sync.Mutex

View File

@@ -26,23 +26,24 @@ func NewNATDevice(upstream Device, ipRewrite bool) NatDevice {
return wrapper return wrapper
} }
func (d *natDeviceWrapper) Read(p []byte, offset int) (int, error) { func (d *natDeviceWrapper) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
select { select {
case packet := <-d.outbound: case packet := <-d.outbound:
defer packet.Release() defer packet.Release()
return copy(p[offset:], packet.Bytes()), nil sizes[0] = copy(bufs[0][offset:], packet.Bytes())
return 1, nil
default: default:
} }
return d.Device.Read(p, offset) return d.Device.Read(bufs, sizes, offset)
} }
func (d *natDeviceWrapper) Write(p []byte, offset int) (int, error) { func (d *natDeviceWrapper) Write(bufs [][]byte, offset int) (count int, err error) {
packet := p[offset:] packet := bufs[0][offset:]
handled, err := d.mapping.WritePacket(packet) handled, err := d.mapping.WritePacket(packet)
if handled { if handled {
return len(packet), err return 1, err
} }
return d.Device.Write(p, offset) return d.Device.Write(bufs, offset)
} }
func (d *natDeviceWrapper) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination { func (d *natDeviceWrapper) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination {

View File

@@ -9,7 +9,7 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/stack"
) )
func (d *natDestinationWrapper) WritePacketBuffer(buffer *stack.PacketBuffer) error { func (d *natDestinationWrapper) WritePacketBuffer(buffer stack.PacketBufferPtr) error {
defer buffer.DecRef() defer buffer.DecRef()
if d.device.writer != nil { if d.device.writer != nil {
d.device.writer.RewritePacketBuffer(buffer) d.device.writer.RewritePacketBuffer(buffer)

View File

@@ -35,7 +35,7 @@ type StackDevice struct {
stack *stack.Stack stack *stack.Stack
mtu uint32 mtu uint32
events chan wgTun.Event events chan wgTun.Event
outbound chan *stack.PacketBuffer outbound chan stack.PacketBufferPtr
packetOutbound chan *buf.Buffer packetOutbound chan *buf.Buffer
done chan struct{} done chan struct{}
dispatcher stack.NetworkDispatcher dispatcher stack.NetworkDispatcher
@@ -55,7 +55,7 @@ func NewStackDevice(localAddresses []netip.Prefix, mtu uint32, ipRewrite bool) (
stack: ipStack, stack: ipStack,
mtu: mtu, mtu: mtu,
events: make(chan wgTun.Event, 1), events: make(chan wgTun.Event, 1),
outbound: make(chan *stack.PacketBuffer, 256), outbound: make(chan stack.PacketBufferPtr, 256),
packetOutbound: make(chan *buf.Buffer, 256), packetOutbound: make(chan *buf.Buffer, 256),
done: make(chan struct{}), done: make(chan struct{}),
mapping: tun.NewNatMapping(ipRewrite), mapping: tun.NewNatMapping(ipRewrite),
@@ -119,7 +119,7 @@ func (w *StackDevice) DialContext(ctx context.Context, network string, destinati
} }
switch N.NetworkName(network) { switch N.NetworkName(network) {
case N.NetworkTCP: case N.NetworkTCP:
tcpConn, err := gonet.DialTCPWithBind(ctx, w.stack, bind, addr, networkProtocol) tcpConn, err := DialTCPWithBind(ctx, w.stack, bind, addr, networkProtocol)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -171,49 +171,60 @@ func (w *StackDevice) File() *os.File {
return nil return nil
} }
func (w *StackDevice) Read(p []byte, offset int) (n int, err error) { func (w *StackDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
select { select {
case packetBuffer, ok := <-w.outbound: case packetBuffer, ok := <-w.outbound:
if !ok { if !ok {
return 0, os.ErrClosed return 0, os.ErrClosed
} }
defer packetBuffer.DecRef() defer packetBuffer.DecRef()
p := bufs[0]
p = p[offset:] p = p[offset:]
n := 0
for _, slice := range packetBuffer.AsSlices() { for _, slice := range packetBuffer.AsSlices() {
n += copy(p[n:], slice) n += copy(p[n:], slice)
} }
sizes[0] = n
count = 1
return return
case packet := <-w.packetOutbound: case packet := <-w.packetOutbound:
defer packet.Release() defer packet.Release()
n = copy(p[offset:], packet.Bytes()) sizes[0] = copy(bufs[0][offset:], packet.Bytes())
count = 1
return return
case <-w.done: case <-w.done:
return 0, os.ErrClosed return 0, os.ErrClosed
} }
} }
func (w *StackDevice) Write(p []byte, offset int) (n int, err error) { func (w *StackDevice) Write(bufs [][]byte, offset int) (count int, err error) {
p = p[offset:] for _, b := range bufs {
if len(p) == 0 { b = b[offset:]
return if len(b) == 0 {
continue
}
handled, err := w.mapping.WritePacket(b)
if handled {
count++
if err != nil {
return count, err
}
continue
}
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(b) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(b),
})
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
packetBuffer.DecRef()
count++
} }
handled, err := w.mapping.WritePacket(p)
if handled {
return len(p), err
}
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 return
} }
@@ -229,7 +240,7 @@ func (w *StackDevice) Name() (string, error) {
return "sing-box", nil return "sing-box", nil
} }
func (w *StackDevice) Events() chan wgTun.Event { func (w *StackDevice) Events() <-chan wgTun.Event {
return w.events return w.events
} }
@@ -248,6 +259,10 @@ func (w *StackDevice) Close() error {
return nil return nil
} }
func (w *StackDevice) BatchSize() int {
return 1
}
func (w *StackDevice) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination { func (w *StackDevice) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination {
w.mapping.CreateSession(session, conn) w.mapping.CreateSession(session, conn)
return &stackNatDestination{ return &stackNatDestination{
@@ -269,7 +284,7 @@ func (d *stackNatDestination) WritePacket(buffer *buf.Buffer) error {
return nil return nil
} }
func (d *stackNatDestination) WritePacketBuffer(buffer *stack.PacketBuffer) error { func (d *stackNatDestination) WritePacketBuffer(buffer stack.PacketBufferPtr) error {
if d.device.writer != nil { if d.device.writer != nil {
d.device.writer.RewritePacketBuffer(buffer) d.device.writer.RewritePacketBuffer(buffer)
} }
@@ -321,7 +336,7 @@ func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone return header.ARPHardwareNone
} }
func (ep *wireEndpoint) AddHeader(buffer *stack.PacketBuffer) { func (ep *wireEndpoint) AddHeader(buffer stack.PacketBufferPtr) {
} }
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) { func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {

View File

@@ -27,14 +27,6 @@ type SystemDevice struct {
addr6 netip.Addr addr6 netip.Addr
} }
/*func (w *SystemDevice) NewEndpoint() (stack.LinkEndpoint, error) {
gTun, isGTun := w.device.(tun.GVisorTun)
if !isGTun {
return nil, tun.ErrGVisorUnsupported
}
return gTun.NewEndpoint()
}*/
func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32) (*SystemDevice, error) { func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32) (*SystemDevice, error) {
var inet4Addresses []netip.Prefix var inet4Addresses []netip.Prefix
var inet6Addresses []netip.Prefix var inet6Addresses []netip.Prefix
@@ -103,12 +95,23 @@ func (w *SystemDevice) File() *os.File {
return nil return nil
} }
func (w *SystemDevice) Read(p []byte, offset int) (int, error) { func (w *SystemDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
return w.device.Read(p[offset-tun.PacketOffset:]) sizes[0], err = w.device.Read(bufs[0][offset-tun.PacketOffset:])
if err == nil {
count = 1
}
return
} }
func (w *SystemDevice) Write(p []byte, offset int) (int, error) { func (w *SystemDevice) Write(bufs [][]byte, offset int) (count int, err error) {
return w.device.Write(p[offset:]) for _, b := range bufs {
_, err = w.device.Write(b[offset:])
if err != nil {
return
}
count++
}
return
} }
func (w *SystemDevice) Flush() error { func (w *SystemDevice) Flush() error {
@@ -123,10 +126,14 @@ func (w *SystemDevice) Name() (string, error) {
return w.name, nil return w.name, nil
} }
func (w *SystemDevice) Events() chan wgTun.Event { func (w *SystemDevice) Events() <-chan wgTun.Event {
return w.events return w.events
} }
func (w *SystemDevice) Close() error { func (w *SystemDevice) Close() error {
return w.device.Close() return w.device.Close()
} }
func (w *SystemDevice) BatchSize() int {
return 1
}

View File

@@ -0,0 +1,78 @@
//go:build with_gvisor
package wireguard
import (
"context"
"errors"
"fmt"
"net"
"time"
M "github.com/sagernet/sing/common/metadata"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter"
)
func DialTCPWithBind(ctx context.Context, s *stack.Stack, localAddr, remoteAddr tcpip.FullAddress, network tcpip.NetworkProtocolNumber) (*gonet.TCPConn, error) {
// Create TCP endpoint, then connect.
var wq waiter.Queue
ep, err := s.NewEndpoint(tcp.ProtocolNumber, network, &wq)
if err != nil {
return nil, errors.New(err.String())
}
// Create wait queue entry that notifies a channel.
//
// We do this unconditionally as Connect will always return an error.
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.WritableEvents)
wq.EventRegister(&waitEntry)
defer wq.EventUnregister(&waitEntry)
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// Bind before connect if requested.
if localAddr != (tcpip.FullAddress{}) {
if err = ep.Bind(localAddr); err != nil {
return nil, fmt.Errorf("ep.Bind(%+v) = %s", localAddr, err)
}
}
err = ep.Connect(remoteAddr)
if _, ok := err.(*tcpip.ErrConnectStarted); ok {
select {
case <-ctx.Done():
ep.Close()
return nil, ctx.Err()
case <-notifyCh:
}
err = ep.LastError()
}
if err != nil {
ep.Close()
return nil, &net.OpError{
Op: "connect",
Net: "tcp",
Addr: M.SocksaddrFrom(M.AddrFromIP(net.IP(remoteAddr.Addr)), remoteAddr.Port).TCPAddr(),
Err: errors.New(err.String()),
}
}
// sing-box added: set keepalive
ep.SocketOptions().SetKeepAlive(true)
keepAliveIdle := tcpip.KeepaliveIdleOption(15 * time.Second)
ep.SetSockOpt(&keepAliveIdle)
keepAliveInterval := tcpip.KeepaliveIntervalOption(15 * time.Second)
ep.SetSockOpt(&keepAliveInterval)
return gonet.NewTCPConn(&wq, ep), nil
}

View File

@@ -1,95 +0,0 @@
package wireguard
import (
"io"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/wireguard-go/conn"
)
var _ conn.Bind = (*ServerBind)(nil)
type ServerBind struct {
inbound chan serverPacket
done chan struct{}
writeBack N.PacketWriter
}
func NewServerBind(writeBack N.PacketWriter) *ServerBind {
return &ServerBind{
inbound: make(chan serverPacket, 256),
done: make(chan struct{}),
writeBack: writeBack,
}
}
func (s *ServerBind) Abort() error {
select {
case <-s.done:
return io.ErrClosedPipe
default:
close(s.done)
}
return nil
}
type serverPacket struct {
buffer *buf.Buffer
source M.Socksaddr
}
func (s *ServerBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
fns = []conn.ReceiveFunc{s.receive}
return
}
func (s *ServerBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
select {
case packet := <-s.inbound:
defer packet.buffer.Release()
n = copy(b, packet.buffer.Bytes())
ep = Endpoint(packet.source)
return
case <-s.done:
err = io.ErrClosedPipe
return
}
}
func (s *ServerBind) WriteIsThreadUnsafe() {
}
func (s *ServerBind) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
select {
case s.inbound <- serverPacket{
buffer: buffer,
source: destination,
}:
return nil
case <-s.done:
return io.ErrClosedPipe
}
}
func (s *ServerBind) Close() error {
return nil
}
func (s *ServerBind) SetMark(mark uint32) error {
return nil
}
func (s *ServerBind) Send(b []byte, ep conn.Endpoint) error {
return s.writeBack.WritePacket(buf.As(b), M.Socksaddr(ep.(Endpoint)))
}
func (s *ServerBind) ParseEndpoint(addr string) (conn.Endpoint, error) {
destination := M.ParseSocksaddr(addr)
if !destination.IsValid() || destination.Port == 0 {
return nil, E.New("invalid endpoint: ", addr)
}
return Endpoint(destination), nil
}