command_connections.go 6.7 KB

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