Add reload platform command

This commit is contained in:
世界 2023-03-03 19:26:54 +08:00
parent 2366835121
commit b9b2b77814
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
13 changed files with 286 additions and 19 deletions

View file

@ -86,10 +86,10 @@ func buildiOS() {
if !debugEnabled { if !debugEnabled {
args = append( args = append(
args, "-trimpath", "-ldflags=-s -w -buildid=", args, "-trimpath", "-ldflags=-s -w -buildid=",
"-tags", "with_gvisor,with_utls,with_clash_api", "-tags", "with_gvisor,with_utls,with_clash_api,with_conntrack",
) )
} else { } else {
args = append(args, "-tags", "with_gvisor,with_utls,with_clash_api,debug") args = append(args, "-tags", "with_gvisor,with_utls,with_clash_api,with_conntrack,debug")
} }
args = append(args, "./experimental/libbox") args = append(args, "./experimental/libbox")

View file

@ -0,0 +1,51 @@
package conntrack
import (
"net"
"runtime/debug"
"github.com/sagernet/sing/common/x/list"
)
type Conn struct {
net.Conn
element *list.Element[*ConnEntry]
}
func NewConn(conn net.Conn) *Conn {
entry := &ConnEntry{
Conn: conn,
Stack: debug.Stack(),
}
connAccess.Lock()
element := openConnection.PushBack(entry)
connAccess.Unlock()
return &Conn{
Conn: conn,
element: element,
}
}
func (c *Conn) Close() error {
if c.element.Value != nil {
connAccess.Lock()
if c.element.Value != nil {
openConnection.Remove(c.element)
c.element.Value = nil
}
connAccess.Unlock()
}
return c.Conn.Close()
}
func (c *Conn) Upstream() any {
return c.Conn
}
func (c *Conn) ReaderReplaceable() bool {
return true
}
func (c *Conn) WriterReplaceable() bool {
return true
}

View file

@ -0,0 +1,51 @@
package conntrack
import (
"net"
"runtime/debug"
"github.com/sagernet/sing/common/x/list"
)
type PacketConn struct {
net.PacketConn
element *list.Element[*ConnEntry]
}
func NewPacketConn(conn net.PacketConn) *PacketConn {
entry := &ConnEntry{
Conn: conn,
Stack: debug.Stack(),
}
connAccess.Lock()
element := openConnection.PushBack(entry)
connAccess.Unlock()
return &PacketConn{
PacketConn: conn,
element: element,
}
}
func (c *PacketConn) Close() error {
if c.element.Value != nil {
connAccess.Lock()
if c.element.Value != nil {
openConnection.Remove(c.element)
c.element.Value = nil
}
connAccess.Unlock()
}
return c.PacketConn.Close()
}
func (c *PacketConn) Upstream() any {
return c.PacketConn
}
func (c *PacketConn) ReaderReplaceable() bool {
return true
}
func (c *PacketConn) WriterReplaceable() bool {
return true
}

View file

@ -0,0 +1,43 @@
package conntrack
import (
"io"
"sync"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/x/list"
)
var (
connAccess sync.RWMutex
openConnection list.List[*ConnEntry]
)
type ConnEntry struct {
Conn io.Closer
Stack []byte
}
func Count() int {
return openConnection.Len()
}
func List() []*ConnEntry {
connAccess.RLock()
defer connAccess.RUnlock()
connList := make([]*ConnEntry, 0, openConnection.Len())
for element := openConnection.Front(); element != nil; element = element.Next() {
connList = append(connList, element.Value)
}
return connList
}
func Close() {
connAccess.Lock()
defer connAccess.Unlock()
for element := openConnection.Front(); element != nil; element = element.Next() {
common.Close(element.Value.Conn)
element.Value = nil
}
openConnection = list.List[*ConnEntry]{}
}

View file

@ -0,0 +1,5 @@
//go:build !with_conntrack
package conntrack
const Enabled = false

View file

@ -0,0 +1,5 @@
//go:build with_conntrack
package conntrack
const Enabled = true

View file

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer/conntrack"
"github.com/sagernet/sing-box/common/warning" "github.com/sagernet/sing-box/common/warning"
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"
@ -159,16 +160,30 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
} }
} }
if !address.IsIPv6() { if !address.IsIPv6() {
return DialSlowContext(&d.dialer4, ctx, network, address) return trackConn(DialSlowContext(&d.dialer4, ctx, network, address))
} else { } else {
return DialSlowContext(&d.dialer6, ctx, network, address) return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
} }
} }
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
if !destination.IsIPv6() { if !destination.IsIPv6() {
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4) return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
} else { } else {
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6) return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
} }
} }
func trackConn(conn net.Conn, err error) (net.Conn, error) {
if !conntrack.Enabled || err != nil {
return conn, err
}
return conntrack.NewConn(conn), nil
}
func trackPacketConn(conn net.PacketConn, err error) (net.PacketConn, error) {
if !conntrack.Enabled || err != nil {
return conn, err
}
return conntrack.NewPacketConn(conn), nil
}

View file

@ -5,4 +5,6 @@ package libbox
const ( const (
CommandLog int32 = iota CommandLog int32 = iota
CommandStatus CommandStatus
CommandServiceReload
CommandCloseConnections
) )

View file

@ -12,7 +12,7 @@ import (
) )
type CommandClient struct { type CommandClient struct {
sockPath string sharedDirectory string
handler CommandClientHandler handler CommandClientHandler
conn net.Conn conn net.Conn
options CommandClientOptions options CommandClientOptions
@ -32,17 +32,21 @@ type CommandClientHandler interface {
func NewCommandClient(sharedDirectory string, handler CommandClientHandler, options *CommandClientOptions) *CommandClient { func NewCommandClient(sharedDirectory string, handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
return &CommandClient{ return &CommandClient{
sockPath: filepath.Join(sharedDirectory, "command.sock"), sharedDirectory: sharedDirectory,
handler: handler, handler: handler,
options: common.PtrValueOrDefault(options), options: common.PtrValueOrDefault(options),
} }
} }
func (c *CommandClient) Connect() error { func clientConnect(sharedDirectory string) (net.Conn, error) {
conn, err := net.DialUnix("unix", nil, &net.UnixAddr{ return net.DialUnix("unix", nil, &net.UnixAddr{
Name: c.sockPath, Name: filepath.Join(sharedDirectory, "command.sock"),
Net: "unix", Net: "unix",
}) })
}
func (c *CommandClient) Connect() error {
conn, err := clientConnect(c.sharedDirectory)
if err != nil { if err != nil {
return err return err
} }

View file

@ -0,0 +1,30 @@
//go:build darwin
package libbox
import (
"encoding/binary"
"net"
runtimeDebug "runtime/debug"
"time"
"github.com/sagernet/sing-box/common/dialer/conntrack"
)
func ClientCloseConnections(sharedDirectory string) error {
conn, err := clientConnect(sharedDirectory)
if err != nil {
return err
}
defer conn.Close()
return binary.Write(conn, binary.BigEndian, uint8(CommandCloseConnections))
}
func (s *CommandServer) handleCloseConnections(conn net.Conn) error {
conntrack.Close()
go func() {
time.Sleep(time.Second)
runtimeDebug.FreeOSMemory()
}()
return nil
}

View file

@ -0,0 +1,48 @@
//go:build darwin
package libbox
import (
"encoding/binary"
"net"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw"
)
func ClientServiceReload(sharedDirectory string) error {
conn, err := clientConnect(sharedDirectory)
if err != nil {
return err
}
defer conn.Close()
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceReload))
if err != nil {
return err
}
var hasError bool
err = binary.Read(conn, binary.BigEndian, &hasError)
if err != nil {
return err
}
if hasError {
errorMessage, err := rw.ReadVString(conn)
if err != nil {
return err
}
return E.New(errorMessage)
}
return nil
}
func (s *CommandServer) handleServiceReload(conn net.Conn) error {
rErr := s.handler.ServiceReload()
err := binary.Write(conn, binary.BigEndian, rErr != nil)
if err != nil {
return err
}
if rErr != nil {
return rw.WriteVString(conn, rErr.Error())
}
return nil
}

View file

@ -18,6 +18,7 @@ import (
type CommandServer struct { type CommandServer struct {
sockPath string sockPath string
listener net.Listener listener net.Listener
handler CommandServerHandler
access sync.Mutex access sync.Mutex
savedLines *list.List[string] savedLines *list.List[string]
@ -25,9 +26,14 @@ type CommandServer struct {
observer *observable.Observer[string] observer *observable.Observer[string]
} }
func NewCommandServer(sharedDirectory string) *CommandServer { type CommandServerHandler interface {
ServiceReload() error
}
func NewCommandServer(sharedDirectory string, handler CommandServerHandler) *CommandServer {
server := &CommandServer{ server := &CommandServer{
sockPath: filepath.Join(sharedDirectory, "command.sock"), sockPath: filepath.Join(sharedDirectory, "command.sock"),
handler: handler,
savedLines: new(list.List[string]), savedLines: new(list.List[string]),
subscriber: observable.NewSubscriber[string](128), subscriber: observable.NewSubscriber[string](128),
} }
@ -79,6 +85,10 @@ func (s *CommandServer) handleConnection(conn net.Conn) error {
return s.handleLogConn(conn) return s.handleLogConn(conn)
case CommandStatus: case CommandStatus:
return s.handleStatusConn(conn) return s.handleStatusConn(conn)
case CommandServiceReload:
return s.handleServiceReload(conn)
case CommandCloseConnections:
return s.handleCloseConnections(conn)
default: default:
return E.New("unknown command: ", command) return E.New("unknown command: ", command)
} }

View file

@ -8,12 +8,14 @@ import (
"runtime" "runtime"
"time" "time"
"github.com/sagernet/sing-box/common/dialer/conntrack"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
) )
type StatusMessage struct { type StatusMessage struct {
Memory int64 Memory int64
Goroutines int32 Goroutines int32
Connections int32
} }
func readStatus() StatusMessage { func readStatus() StatusMessage {
@ -22,6 +24,7 @@ func readStatus() StatusMessage {
var message StatusMessage var message StatusMessage
message.Memory = int64(memStats.Sys - memStats.HeapReleased) message.Memory = int64(memStats.Sys - memStats.HeapReleased)
message.Goroutines = int32(runtime.NumGoroutine()) message.Goroutines = int32(runtime.NumGoroutine())
message.Connections = int32(conntrack.Count())
return message return message
} }