123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- package libbox
- import (
- "bufio"
- "net"
- "slices"
- "strings"
- "time"
- "github.com/sagernet/sing-box/experimental/clashapi"
- "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
- "github.com/sagernet/sing/common/binary"
- E "github.com/sagernet/sing/common/exceptions"
- M "github.com/sagernet/sing/common/metadata"
- "github.com/sagernet/sing/common/varbin"
- "github.com/gofrs/uuid/v5"
- )
- func (c *CommandClient) handleConnectionsConn(conn net.Conn) {
- defer conn.Close()
- reader := bufio.NewReader(conn)
- var (
- rawConnections []Connection
- connections Connections
- )
- for {
- rawConnections = nil
- err := varbin.Read(reader, binary.BigEndian, &rawConnections)
- if err != nil {
- c.handler.Disconnected(err.Error())
- return
- }
- connections.input = rawConnections
- c.handler.WriteConnections(&connections)
- }
- }
- func (s *CommandServer) handleConnectionsConn(conn net.Conn) error {
- var interval int64
- err := binary.Read(conn, binary.BigEndian, &interval)
- if err != nil {
- return E.Cause(err, "read interval")
- }
- ticker := time.NewTicker(time.Duration(interval))
- defer ticker.Stop()
- ctx := connKeepAlive(conn)
- var trafficManager *trafficontrol.Manager
- for {
- service := s.service
- if service != nil {
- trafficManager = service.clashServer.(*clashapi.Server).TrafficManager()
- break
- }
- select {
- case <-ctx.Done():
- return ctx.Err()
- case <-ticker.C:
- }
- }
- var (
- connections = make(map[uuid.UUID]*Connection)
- outConnections []Connection
- )
- writer := bufio.NewWriter(conn)
- for {
- outConnections = outConnections[:0]
- for _, connection := range trafficManager.Connections() {
- outConnections = append(outConnections, newConnection(connections, connection, false))
- }
- for _, connection := range trafficManager.ClosedConnections() {
- outConnections = append(outConnections, newConnection(connections, connection, true))
- }
- err = varbin.Write(writer, binary.BigEndian, outConnections)
- if err != nil {
- return err
- }
- err = writer.Flush()
- if err != nil {
- return err
- }
- select {
- case <-ctx.Done():
- return ctx.Err()
- case <-ticker.C:
- }
- }
- }
- const (
- ConnectionStateAll = iota
- ConnectionStateActive
- ConnectionStateClosed
- )
- type Connections struct {
- input []Connection
- filtered []Connection
- }
- func (c *Connections) FilterState(state int32) {
- c.filtered = c.filtered[:0]
- switch state {
- case ConnectionStateAll:
- c.filtered = append(c.filtered, c.input...)
- case ConnectionStateActive:
- for _, connection := range c.input {
- if connection.ClosedAt == 0 {
- c.filtered = append(c.filtered, connection)
- }
- }
- case ConnectionStateClosed:
- for _, connection := range c.input {
- if connection.ClosedAt != 0 {
- c.filtered = append(c.filtered, connection)
- }
- }
- }
- }
- func (c *Connections) SortByDate() {
- slices.SortStableFunc(c.filtered, func(x, y Connection) int {
- if x.CreatedAt < y.CreatedAt {
- return 1
- } else if x.CreatedAt > y.CreatedAt {
- return -1
- } else {
- return strings.Compare(y.ID, x.ID)
- }
- })
- }
- func (c *Connections) SortByTraffic() {
- slices.SortStableFunc(c.filtered, func(x, y Connection) int {
- xTraffic := x.Uplink + x.Downlink
- yTraffic := y.Uplink + y.Downlink
- if xTraffic < yTraffic {
- return 1
- } else if xTraffic > yTraffic {
- return -1
- } else {
- return strings.Compare(y.ID, x.ID)
- }
- })
- }
- func (c *Connections) SortByTrafficTotal() {
- slices.SortStableFunc(c.filtered, func(x, y Connection) int {
- xTraffic := x.UplinkTotal + x.DownlinkTotal
- yTraffic := y.UplinkTotal + y.DownlinkTotal
- if xTraffic < yTraffic {
- return 1
- } else if xTraffic > yTraffic {
- return -1
- } else {
- return strings.Compare(y.ID, x.ID)
- }
- })
- }
- func (c *Connections) Iterator() ConnectionIterator {
- return newPtrIterator(c.filtered)
- }
- type Connection struct {
- ID string
- Inbound string
- InboundType string
- IPVersion int32
- Network string
- Source string
- Destination string
- Domain string
- Protocol string
- User string
- FromOutbound string
- CreatedAt int64
- ClosedAt int64
- Uplink int64
- Downlink int64
- UplinkTotal int64
- DownlinkTotal int64
- Rule string
- Outbound string
- OutboundType string
- ChainList []string
- }
- func (c *Connection) Chain() StringIterator {
- return newIterator(c.ChainList)
- }
- func (c *Connection) DisplayDestination() string {
- destination := M.ParseSocksaddr(c.Destination)
- if destination.IsIP() && c.Domain != "" {
- destination = M.Socksaddr{
- Fqdn: c.Domain,
- Port: destination.Port,
- }
- return destination.String()
- }
- return c.Destination
- }
- type ConnectionIterator interface {
- Next() *Connection
- HasNext() bool
- }
- func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol.TrackerMetadata, isClosed bool) Connection {
- if oldConnection, loaded := connections[metadata.ID]; loaded {
- if isClosed {
- if oldConnection.ClosedAt == 0 {
- oldConnection.Uplink = 0
- oldConnection.Downlink = 0
- oldConnection.ClosedAt = metadata.ClosedAt.UnixMilli()
- }
- return *oldConnection
- }
- lastUplink := oldConnection.UplinkTotal
- lastDownlink := oldConnection.DownlinkTotal
- uplinkTotal := metadata.Upload.Load()
- downlinkTotal := metadata.Download.Load()
- oldConnection.Uplink = uplinkTotal - lastUplink
- oldConnection.Downlink = downlinkTotal - lastDownlink
- oldConnection.UplinkTotal = uplinkTotal
- oldConnection.DownlinkTotal = downlinkTotal
- return *oldConnection
- }
- var rule string
- if metadata.Rule != nil {
- rule = metadata.Rule.String()
- }
- uplinkTotal := metadata.Upload.Load()
- downlinkTotal := metadata.Download.Load()
- uplink := uplinkTotal
- downlink := downlinkTotal
- var closedAt int64
- if !metadata.ClosedAt.IsZero() {
- closedAt = metadata.ClosedAt.UnixMilli()
- uplink = 0
- downlink = 0
- }
- connection := Connection{
- ID: metadata.ID.String(),
- Inbound: metadata.Metadata.Inbound,
- InboundType: metadata.Metadata.InboundType,
- IPVersion: int32(metadata.Metadata.IPVersion),
- Network: metadata.Metadata.Network,
- Source: metadata.Metadata.Source.String(),
- Destination: metadata.Metadata.Destination.String(),
- Domain: metadata.Metadata.Domain,
- Protocol: metadata.Metadata.Protocol,
- User: metadata.Metadata.User,
- FromOutbound: metadata.Metadata.Outbound,
- CreatedAt: metadata.CreatedAt.UnixMilli(),
- ClosedAt: closedAt,
- Uplink: uplink,
- Downlink: downlink,
- UplinkTotal: uplinkTotal,
- DownlinkTotal: downlinkTotal,
- Rule: rule,
- Outbound: metadata.Outbound,
- OutboundType: metadata.OutboundType,
- ChainList: metadata.Chain,
- }
- connections[metadata.ID] = &connection
- return connection
- }
|