command_client.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package libbox
  2. import (
  3. "encoding/binary"
  4. "net"
  5. "os"
  6. "path/filepath"
  7. "github.com/sagernet/sing/common"
  8. E "github.com/sagernet/sing/common/exceptions"
  9. )
  10. type CommandClient struct {
  11. handler CommandClientHandler
  12. conn net.Conn
  13. options CommandClientOptions
  14. }
  15. type CommandClientOptions struct {
  16. Command int32
  17. StatusInterval int64
  18. }
  19. type CommandClientHandler interface {
  20. Connected()
  21. Disconnected(message string)
  22. WriteLog(message string)
  23. WriteStatus(message *StatusMessage)
  24. WriteGroups(message OutboundGroupIterator)
  25. InitializeClashMode(modeList StringIterator, currentMode string)
  26. UpdateClashMode(newMode string)
  27. }
  28. func NewStandaloneCommandClient() *CommandClient {
  29. return new(CommandClient)
  30. }
  31. func NewCommandClient(handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
  32. return &CommandClient{
  33. handler: handler,
  34. options: common.PtrValueOrDefault(options),
  35. }
  36. }
  37. func (c *CommandClient) directConnect() (net.Conn, error) {
  38. if !sTVOS {
  39. return net.DialUnix("unix", nil, &net.UnixAddr{
  40. Name: filepath.Join(sBasePath, "command.sock"),
  41. Net: "unix",
  42. })
  43. } else {
  44. return net.Dial("tcp", "127.0.0.1:8964")
  45. }
  46. }
  47. func (c *CommandClient) Connect() error {
  48. common.Close(c.conn)
  49. conn, err := c.directConnect()
  50. if err != nil {
  51. return err
  52. }
  53. c.conn = conn
  54. err = binary.Write(conn, binary.BigEndian, uint8(c.options.Command))
  55. if err != nil {
  56. return err
  57. }
  58. switch c.options.Command {
  59. case CommandLog:
  60. c.handler.Connected()
  61. go c.handleLogConn(conn)
  62. case CommandStatus:
  63. err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
  64. if err != nil {
  65. return E.Cause(err, "write interval")
  66. }
  67. c.handler.Connected()
  68. go c.handleStatusConn(conn)
  69. case CommandGroup:
  70. err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
  71. if err != nil {
  72. return E.Cause(err, "write interval")
  73. }
  74. c.handler.Connected()
  75. go c.handleGroupConn(conn)
  76. case CommandClashMode:
  77. var (
  78. modeList []string
  79. currentMode string
  80. )
  81. modeList, currentMode, err = readClashModeList(conn)
  82. if err != nil {
  83. return err
  84. }
  85. c.handler.Connected()
  86. c.handler.InitializeClashMode(newIterator(modeList), currentMode)
  87. if len(modeList) == 0 {
  88. conn.Close()
  89. c.handler.Disconnected(os.ErrInvalid.Error())
  90. return nil
  91. }
  92. go c.handleModeConn(conn)
  93. }
  94. return nil
  95. }
  96. func (c *CommandClient) Disconnect() error {
  97. return common.Close(c.conn)
  98. }