123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- package libbox
- import (
- "encoding/binary"
- "net"
- "os"
- "path/filepath"
- "time"
- "github.com/sagernet/sing/common"
- E "github.com/sagernet/sing/common/exceptions"
- )
- type CommandClient struct {
- handler CommandClientHandler
- conn net.Conn
- options CommandClientOptions
- }
- type CommandClientOptions struct {
- Command int32
- StatusInterval int64
- }
- type CommandClientHandler interface {
- Connected()
- Disconnected(message string)
- ClearLogs()
- WriteLogs(messageList StringIterator)
- WriteStatus(message *StatusMessage)
- WriteGroups(message OutboundGroupIterator)
- InitializeClashMode(modeList StringIterator, currentMode string)
- UpdateClashMode(newMode string)
- WriteConnections(message *Connections)
- }
- func NewStandaloneCommandClient() *CommandClient {
- return new(CommandClient)
- }
- func NewCommandClient(handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
- return &CommandClient{
- handler: handler,
- options: common.PtrValueOrDefault(options),
- }
- }
- func (c *CommandClient) directConnect() (net.Conn, error) {
- 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")
- }
- }
- 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
- }
- func (c *CommandClient) Connect() error {
- common.Close(c.conn)
- conn, err := c.directConnectWithRetry()
- 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:
- err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
- if err != nil {
- return E.Cause(err, "write interval")
- }
- c.handler.Connected()
- go c.handleLogConn(conn)
- case CommandStatus:
- err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
- if err != nil {
- return E.Cause(err, "write interval")
- }
- c.handler.Connected()
- go c.handleStatusConn(conn)
- 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
- }
- if sFixAndroidStack {
- go func() {
- c.handler.Connected()
- c.handler.InitializeClashMode(newIterator(modeList), currentMode)
- if len(modeList) == 0 {
- conn.Close()
- c.handler.Disconnected(os.ErrInvalid.Error())
- }
- }()
- } else {
- c.handler.Connected()
- c.handler.InitializeClashMode(newIterator(modeList), currentMode)
- if len(modeList) == 0 {
- conn.Close()
- c.handler.Disconnected(os.ErrInvalid.Error())
- }
- }
- if len(modeList) == 0 {
- return nil
- }
- go c.handleModeConn(conn)
- 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)
- }
- return nil
- }
- func (c *CommandClient) Disconnect() error {
- return common.Close(c.conn)
- }
|