command_connections.go 6.6 KB

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