sing-box/experimental/libbox/command_client.go

134 lines
2.9 KiB
Go
Raw Normal View History

2023-03-01 02:37:47 +00:00
package libbox
import (
"encoding/binary"
"net"
"os"
2023-03-01 02:37:47 +00:00
"path/filepath"
2023-09-03 13:06:21 +00:00
"time"
2023-03-01 02:37:47 +00:00
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
)
type CommandClient struct {
2023-07-29 00:37:10 +00:00
handler CommandClientHandler
conn net.Conn
options CommandClientOptions
2023-03-01 02:37:47 +00:00
}
type CommandClientOptions struct {
Command int32
StatusInterval int64
}
type CommandClientHandler interface {
Connected()
Disconnected(message string)
2023-10-02 14:27:48 +00:00
ClearLog()
2023-03-01 02:37:47 +00:00
WriteLog(message string)
WriteStatus(message *StatusMessage)
2023-07-02 08:45:30 +00:00
WriteGroups(message OutboundGroupIterator)
InitializeClashMode(modeList StringIterator, currentMode string)
UpdateClashMode(newMode string)
2024-06-11 13:16:33 +00:00
WriteConnections(message *Connections)
2023-07-02 08:45:30 +00:00
}
2023-07-29 00:37:10 +00:00
func NewStandaloneCommandClient() *CommandClient {
return new(CommandClient)
2023-03-01 02:37:47 +00:00
}
2023-08-16 08:48:23 +00:00
func NewCommandClient(handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
2023-03-01 02:37:47 +00:00
return &CommandClient{
2023-07-29 00:37:10 +00:00
handler: handler,
options: common.PtrValueOrDefault(options),
2023-03-01 02:37:47 +00:00
}
}
2023-07-02 08:45:30 +00:00
func (c *CommandClient) directConnect() (net.Conn, error) {
2023-07-29 00:37:10 +00:00
if !sTVOS {
return net.DialUnix("unix", nil, &net.UnixAddr{
Name: filepath.Join(sBasePath, "command.sock"),
Net: "unix",
})
} else {
return net.Dial("tcp", "127.0.0.1:8964")
}
2023-03-03 11:26:54 +00:00
}
2023-09-03 13:06:21 +00:00
func (c *CommandClient) directConnectWithRetry() (net.Conn, error) {
var (
conn net.Conn
err error
)
for i := 0; i < 10; i++ {
conn, err = c.directConnect()
if err == nil {
return conn, nil
}
time.Sleep(time.Duration(100+i*50) * time.Millisecond)
}
return nil, err
}
2023-03-03 11:26:54 +00:00
func (c *CommandClient) Connect() error {
2023-04-04 20:38:56 +00:00
common.Close(c.conn)
2023-09-03 13:06:21 +00:00
conn, err := c.directConnectWithRetry()
2023-03-01 02:37:47 +00:00
if err != nil {
return err
}
c.conn = conn
err = binary.Write(conn, binary.BigEndian, uint8(c.options.Command))
if err != nil {
return err
}
switch c.options.Command {
case CommandLog:
2023-03-02 08:40:28 +00:00
c.handler.Connected()
2023-03-01 02:37:47 +00:00
go c.handleLogConn(conn)
case CommandStatus:
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
if err != nil {
return E.Cause(err, "write interval")
}
2023-03-02 08:40:28 +00:00
c.handler.Connected()
2023-03-01 02:37:47 +00:00
go c.handleStatusConn(conn)
2023-07-02 08:45:30 +00:00
case CommandGroup:
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
if err != nil {
return E.Cause(err, "write interval")
}
c.handler.Connected()
go c.handleGroupConn(conn)
case CommandClashMode:
var (
modeList []string
currentMode string
)
modeList, currentMode, err = readClashModeList(conn)
if err != nil {
return err
}
c.handler.Connected()
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
if len(modeList) == 0 {
conn.Close()
c.handler.Disconnected(os.ErrInvalid.Error())
return nil
}
go c.handleModeConn(conn)
2024-06-11 13:16:33 +00:00
case CommandConnections:
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
if err != nil {
return E.Cause(err, "write interval")
}
c.handler.Connected()
go c.handleConnectionsConn(conn)
2023-03-01 02:37:47 +00:00
}
return nil
}
func (c *CommandClient) Disconnect() error {
return common.Close(c.conn)
}