command_connections.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package libbox
  2. import (
  3. "bufio"
  4. "net"
  5. "slices"
  6. "strings"
  7. "time"
  8. "github.com/sagernet/sing-box/experimental/clashapi"
  9. "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
  10. "github.com/sagernet/sing/common/binary"
  11. E "github.com/sagernet/sing/common/exceptions"
  12. M "github.com/sagernet/sing/common/metadata"
  13. "github.com/sagernet/sing/common/varbin"
  14. "github.com/gofrs/uuid/v5"
  15. )
  16. func (c *CommandClient) handleConnectionsConn(conn net.Conn) {
  17. defer conn.Close()
  18. reader := bufio.NewReader(conn)
  19. var (
  20. rawConnections []Connection
  21. connections Connections
  22. )
  23. for {
  24. rawConnections = nil
  25. err := varbin.Read(reader, binary.BigEndian, &rawConnections)
  26. if err != nil {
  27. c.handler.Disconnected(err.Error())
  28. return
  29. }
  30. connections.input = rawConnections
  31. c.handler.WriteConnections(&connections)
  32. }
  33. }
  34. func (s *CommandServer) handleConnectionsConn(conn net.Conn) error {
  35. var interval int64
  36. err := binary.Read(conn, binary.BigEndian, &interval)
  37. if err != nil {
  38. return E.Cause(err, "read interval")
  39. }
  40. ticker := time.NewTicker(time.Duration(interval))
  41. defer ticker.Stop()
  42. ctx := connKeepAlive(conn)
  43. var trafficManager *trafficontrol.Manager
  44. for {
  45. service := s.service
  46. if service != nil {
  47. clashServer := service.instance.Router().ClashServer()
  48. if clashServer == nil {
  49. return E.New("Clash API disabled")
  50. }
  51. trafficManager = clashServer.(*clashapi.Server).TrafficManager()
  52. break
  53. }
  54. select {
  55. case <-ctx.Done():
  56. return ctx.Err()
  57. case <-ticker.C:
  58. }
  59. }
  60. var (
  61. connections = make(map[uuid.UUID]*Connection)
  62. outConnections []Connection
  63. )
  64. writer := bufio.NewWriter(conn)
  65. for {
  66. outConnections = outConnections[:0]
  67. for _, connection := range trafficManager.Connections() {
  68. outConnections = append(outConnections, newConnection(connections, connection, false))
  69. }
  70. for _, connection := range trafficManager.ClosedConnections() {
  71. outConnections = append(outConnections, newConnection(connections, connection, true))
  72. }
  73. err = varbin.Write(writer, binary.BigEndian, outConnections)
  74. if err != nil {
  75. return err
  76. }
  77. err = writer.Flush()
  78. if err != nil {
  79. return err
  80. }
  81. select {
  82. case <-ctx.Done():
  83. return ctx.Err()
  84. case <-ticker.C:
  85. }
  86. }
  87. }
  88. const (
  89. ConnectionStateAll = iota
  90. ConnectionStateActive
  91. ConnectionStateClosed
  92. )
  93. type Connections struct {
  94. input []Connection
  95. filtered []Connection
  96. }
  97. func (c *Connections) FilterState(state int32) {
  98. c.filtered = c.filtered[:0]
  99. switch state {
  100. case ConnectionStateAll:
  101. c.filtered = append(c.filtered, c.input...)
  102. case ConnectionStateActive:
  103. for _, connection := range c.input {
  104. if connection.ClosedAt == 0 {
  105. c.filtered = append(c.filtered, connection)
  106. }
  107. }
  108. case ConnectionStateClosed:
  109. for _, connection := range c.input {
  110. if connection.ClosedAt != 0 {
  111. c.filtered = append(c.filtered, connection)
  112. }
  113. }
  114. }
  115. }
  116. func (c *Connections) SortByDate() {
  117. slices.SortStableFunc(c.filtered, func(x, y Connection) int {
  118. if x.CreatedAt < y.CreatedAt {
  119. return 1
  120. } else if x.CreatedAt > y.CreatedAt {
  121. return -1
  122. } else {
  123. return strings.Compare(y.ID, x.ID)
  124. }
  125. })
  126. }
  127. func (c *Connections) SortByTraffic() {
  128. slices.SortStableFunc(c.filtered, func(x, y Connection) int {
  129. xTraffic := x.Uplink + x.Downlink
  130. yTraffic := y.Uplink + y.Downlink
  131. if xTraffic < yTraffic {
  132. return 1
  133. } else if xTraffic > yTraffic {
  134. return -1
  135. } else {
  136. return strings.Compare(y.ID, x.ID)
  137. }
  138. })
  139. }
  140. func (c *Connections) SortByTrafficTotal() {
  141. slices.SortStableFunc(c.filtered, func(x, y Connection) int {
  142. xTraffic := x.UplinkTotal + x.DownlinkTotal
  143. yTraffic := y.UplinkTotal + y.DownlinkTotal
  144. if xTraffic < yTraffic {
  145. return 1
  146. } else if xTraffic > yTraffic {
  147. return -1
  148. } else {
  149. return strings.Compare(y.ID, x.ID)
  150. }
  151. })
  152. }
  153. func (c *Connections) Iterator() ConnectionIterator {
  154. return newPtrIterator(c.filtered)
  155. }
  156. type Connection struct {
  157. ID string
  158. Inbound string
  159. InboundType string
  160. IPVersion int32
  161. Network string
  162. Source string
  163. Destination string
  164. Domain string
  165. Protocol string
  166. User string
  167. FromOutbound string
  168. CreatedAt int64
  169. ClosedAt int64
  170. Uplink int64
  171. Downlink int64
  172. UplinkTotal int64
  173. DownlinkTotal int64
  174. Rule string
  175. Outbound string
  176. OutboundType string
  177. ChainList []string
  178. }
  179. func (c *Connection) Chain() StringIterator {
  180. return newIterator(c.ChainList)
  181. }
  182. func (c *Connection) DisplayDestination() string {
  183. destination := M.ParseSocksaddr(c.Destination)
  184. if destination.IsIP() && c.Domain != "" {
  185. destination = M.Socksaddr{
  186. Fqdn: c.Domain,
  187. Port: destination.Port,
  188. }
  189. return destination.String()
  190. }
  191. return c.Destination
  192. }
  193. type ConnectionIterator interface {
  194. Next() *Connection
  195. HasNext() bool
  196. }
  197. func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol.TrackerMetadata, isClosed bool) Connection {
  198. if oldConnection, loaded := connections[metadata.ID]; loaded {
  199. if isClosed {
  200. if oldConnection.ClosedAt == 0 {
  201. oldConnection.Uplink = 0
  202. oldConnection.Downlink = 0
  203. oldConnection.ClosedAt = metadata.ClosedAt.UnixMilli()
  204. }
  205. return *oldConnection
  206. }
  207. lastUplink := oldConnection.UplinkTotal
  208. lastDownlink := oldConnection.DownlinkTotal
  209. uplinkTotal := metadata.Upload.Load()
  210. downlinkTotal := metadata.Download.Load()
  211. oldConnection.Uplink = uplinkTotal - lastUplink
  212. oldConnection.Downlink = downlinkTotal - lastDownlink
  213. oldConnection.UplinkTotal = uplinkTotal
  214. oldConnection.DownlinkTotal = downlinkTotal
  215. return *oldConnection
  216. }
  217. var rule string
  218. if metadata.Rule != nil {
  219. rule = metadata.Rule.String()
  220. }
  221. uplinkTotal := metadata.Upload.Load()
  222. downlinkTotal := metadata.Download.Load()
  223. uplink := uplinkTotal
  224. downlink := downlinkTotal
  225. var closedAt int64
  226. if !metadata.ClosedAt.IsZero() {
  227. closedAt = metadata.ClosedAt.UnixMilli()
  228. uplink = 0
  229. downlink = 0
  230. }
  231. connection := Connection{
  232. ID: metadata.ID.String(),
  233. Inbound: metadata.Metadata.Inbound,
  234. InboundType: metadata.Metadata.InboundType,
  235. IPVersion: int32(metadata.Metadata.IPVersion),
  236. Network: metadata.Metadata.Network,
  237. Source: metadata.Metadata.Source.String(),
  238. Destination: metadata.Metadata.Destination.String(),
  239. Domain: metadata.Metadata.Domain,
  240. Protocol: metadata.Metadata.Protocol,
  241. User: metadata.Metadata.User,
  242. FromOutbound: metadata.Metadata.Outbound,
  243. CreatedAt: metadata.CreatedAt.UnixMilli(),
  244. ClosedAt: closedAt,
  245. Uplink: uplink,
  246. Downlink: downlink,
  247. UplinkTotal: uplinkTotal,
  248. DownlinkTotal: downlinkTotal,
  249. Rule: rule,
  250. Outbound: metadata.Outbound,
  251. OutboundType: metadata.OutboundType,
  252. ChainList: metadata.Chain,
  253. }
  254. connections[metadata.ID] = &connection
  255. return connection
  256. }