2023-03-01 02:37:47 +00:00
|
|
|
package libbox
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"net"
|
2023-08-24 13:52:38 +00:00
|
|
|
"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)
|
2024-06-18 09:49:06 +00:00
|
|
|
ClearLogs()
|
|
|
|
WriteLogs(messageList StringIterator)
|
2023-03-01 02:37:47 +00:00
|
|
|
WriteStatus(message *StatusMessage)
|
2023-07-02 08:45:30 +00:00
|
|
|
WriteGroups(message OutboundGroupIterator)
|
2023-08-24 13:52:38 +00:00
|
|
|
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:
|
2024-06-18 09:49:06 +00:00
|
|
|
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.handleLogConn(conn)
|
|
|
|
case CommandStatus:
|
|
|
|
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
|
|
|
if err != nil {
|
2024-11-05 12:03:39 +00:00
|
|
|
return E.Cause(err, "write interval")
|
2023-03-01 02:37:47 +00:00
|
|
|
}
|
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)
|
2023-08-24 13:52:38 +00:00
|
|
|
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)
|
|
|
|
}
|