| 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
 
- }
 
 
  |