command_server.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package libbox
  2. import (
  3. "encoding/binary"
  4. "net"
  5. "os"
  6. "path/filepath"
  7. "sync"
  8. "github.com/sagernet/sing-box/common/urltest"
  9. "github.com/sagernet/sing-box/experimental/clashapi"
  10. "github.com/sagernet/sing-box/log"
  11. "github.com/sagernet/sing/common"
  12. "github.com/sagernet/sing/common/debug"
  13. E "github.com/sagernet/sing/common/exceptions"
  14. "github.com/sagernet/sing/common/observable"
  15. "github.com/sagernet/sing/common/x/list"
  16. "github.com/sagernet/sing/service"
  17. )
  18. type CommandServer struct {
  19. listener net.Listener
  20. handler CommandServerHandler
  21. access sync.Mutex
  22. savedLines list.List[string]
  23. maxLines int
  24. subscriber *observable.Subscriber[string]
  25. observer *observable.Observer[string]
  26. service *BoxService
  27. // These channels only work with a single client. if multi-client support is needed, replace with Subscriber/Observer
  28. urlTestUpdate chan struct{}
  29. modeUpdate chan struct{}
  30. logReset chan struct{}
  31. closedConnections []Connection
  32. }
  33. type CommandServerHandler interface {
  34. ServiceReload() error
  35. PostServiceClose()
  36. GetSystemProxyStatus() *SystemProxyStatus
  37. SetSystemProxyEnabled(isEnabled bool) error
  38. }
  39. func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServer {
  40. server := &CommandServer{
  41. handler: handler,
  42. maxLines: int(maxLines),
  43. subscriber: observable.NewSubscriber[string](128),
  44. urlTestUpdate: make(chan struct{}, 1),
  45. modeUpdate: make(chan struct{}, 1),
  46. logReset: make(chan struct{}, 1),
  47. }
  48. server.observer = observable.NewObserver[string](server.subscriber, 64)
  49. return server
  50. }
  51. func (s *CommandServer) SetService(newService *BoxService) {
  52. if newService != nil {
  53. service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
  54. newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
  55. }
  56. s.service = newService
  57. s.notifyURLTestUpdate()
  58. }
  59. func (s *CommandServer) notifyURLTestUpdate() {
  60. select {
  61. case s.urlTestUpdate <- struct{}{}:
  62. default:
  63. }
  64. }
  65. func (s *CommandServer) Start() error {
  66. if !sTVOS {
  67. return s.listenUNIX()
  68. } else {
  69. return s.listenTCP()
  70. }
  71. }
  72. func (s *CommandServer) listenUNIX() error {
  73. sockPath := filepath.Join(sBasePath, "command.sock")
  74. os.Remove(sockPath)
  75. listener, err := net.ListenUnix("unix", &net.UnixAddr{
  76. Name: sockPath,
  77. Net: "unix",
  78. })
  79. if err != nil {
  80. return E.Cause(err, "listen ", sockPath)
  81. }
  82. err = os.Chown(sockPath, sUserID, sGroupID)
  83. if err != nil {
  84. listener.Close()
  85. os.Remove(sockPath)
  86. return E.Cause(err, "chown")
  87. }
  88. s.listener = listener
  89. go s.loopConnection(listener)
  90. return nil
  91. }
  92. func (s *CommandServer) listenTCP() error {
  93. listener, err := net.Listen("tcp", "127.0.0.1:8964")
  94. if err != nil {
  95. return E.Cause(err, "listen")
  96. }
  97. s.listener = listener
  98. go s.loopConnection(listener)
  99. return nil
  100. }
  101. func (s *CommandServer) Close() error {
  102. return common.Close(
  103. s.listener,
  104. s.observer,
  105. )
  106. }
  107. func (s *CommandServer) loopConnection(listener net.Listener) {
  108. for {
  109. conn, err := listener.Accept()
  110. if err != nil {
  111. return
  112. }
  113. go func() {
  114. hErr := s.handleConnection(conn)
  115. if hErr != nil && !E.IsClosed(err) {
  116. if debug.Enabled {
  117. log.Warn("log-server: process connection: ", hErr)
  118. }
  119. }
  120. }()
  121. }
  122. }
  123. func (s *CommandServer) handleConnection(conn net.Conn) error {
  124. defer conn.Close()
  125. var command uint8
  126. err := binary.Read(conn, binary.BigEndian, &command)
  127. if err != nil {
  128. return E.Cause(err, "read command")
  129. }
  130. switch int32(command) {
  131. case CommandLog:
  132. return s.handleLogConn(conn)
  133. case CommandStatus:
  134. return s.handleStatusConn(conn)
  135. case CommandServiceReload:
  136. return s.handleServiceReload(conn)
  137. case CommandServiceClose:
  138. return s.handleServiceClose(conn)
  139. case CommandCloseConnections:
  140. return s.handleCloseConnections(conn)
  141. case CommandGroup:
  142. return s.handleGroupConn(conn)
  143. case CommandSelectOutbound:
  144. return s.handleSelectOutbound(conn)
  145. case CommandURLTest:
  146. return s.handleURLTest(conn)
  147. case CommandGroupExpand:
  148. return s.handleSetGroupExpand(conn)
  149. case CommandClashMode:
  150. return s.handleModeConn(conn)
  151. case CommandSetClashMode:
  152. return s.handleSetClashMode(conn)
  153. case CommandGetSystemProxyStatus:
  154. return s.handleGetSystemProxyStatus(conn)
  155. case CommandSetSystemProxyEnabled:
  156. return s.handleSetSystemProxyEnabled(conn)
  157. case CommandConnections:
  158. return s.handleConnectionsConn(conn)
  159. case CommandCloseConnection:
  160. return s.handleCloseConnection(conn)
  161. case CommandGetDeprecatedNotes:
  162. return s.handleGetDeprecatedNotes(conn)
  163. default:
  164. return E.New("unknown command: ", command)
  165. }
  166. }