github.com/metacubex/mihomo@v1.18.5/tunnel/statistic/tracker.go (about)

     1  package statistic
     2  
     3  import (
     4  	"io"
     5  	"net"
     6  	"net/netip"
     7  	"time"
     8  
     9  	"github.com/metacubex/mihomo/common/atomic"
    10  	"github.com/metacubex/mihomo/common/buf"
    11  	N "github.com/metacubex/mihomo/common/net"
    12  	"github.com/metacubex/mihomo/common/utils"
    13  	C "github.com/metacubex/mihomo/constant"
    14  
    15  	"github.com/gofrs/uuid/v5"
    16  )
    17  
    18  type Tracker interface {
    19  	ID() string
    20  	Close() error
    21  	Info() *TrackerInfo
    22  	C.Connection
    23  }
    24  
    25  type TrackerInfo struct {
    26  	UUID          uuid.UUID    `json:"id"`
    27  	Metadata      *C.Metadata  `json:"metadata"`
    28  	UploadTotal   atomic.Int64 `json:"upload"`
    29  	DownloadTotal atomic.Int64 `json:"download"`
    30  	Start         time.Time    `json:"start"`
    31  	Chain         C.Chain      `json:"chains"`
    32  	Rule          string       `json:"rule"`
    33  	RulePayload   string       `json:"rulePayload"`
    34  }
    35  
    36  type tcpTracker struct {
    37  	C.Conn `json:"-"`
    38  	*TrackerInfo
    39  	manager *Manager
    40  
    41  	pushToManager bool `json:"-"`
    42  }
    43  
    44  func (tt *tcpTracker) ID() string {
    45  	return tt.UUID.String()
    46  }
    47  
    48  func (tt *tcpTracker) Info() *TrackerInfo {
    49  	return tt.TrackerInfo
    50  }
    51  
    52  func (tt *tcpTracker) Read(b []byte) (int, error) {
    53  	n, err := tt.Conn.Read(b)
    54  	download := int64(n)
    55  	if tt.pushToManager {
    56  		tt.manager.PushDownloaded(download)
    57  	}
    58  	tt.DownloadTotal.Add(download)
    59  	return n, err
    60  }
    61  
    62  func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) {
    63  	err = tt.Conn.ReadBuffer(buffer)
    64  	download := int64(buffer.Len())
    65  	if tt.pushToManager {
    66  		tt.manager.PushDownloaded(download)
    67  	}
    68  	tt.DownloadTotal.Add(download)
    69  	return
    70  }
    71  
    72  func (tt *tcpTracker) UnwrapReader() (io.Reader, []N.CountFunc) {
    73  	return tt.Conn, []N.CountFunc{func(download int64) {
    74  		if tt.pushToManager {
    75  			tt.manager.PushDownloaded(download)
    76  		}
    77  		tt.DownloadTotal.Add(download)
    78  	}}
    79  }
    80  
    81  func (tt *tcpTracker) Write(b []byte) (int, error) {
    82  	n, err := tt.Conn.Write(b)
    83  	upload := int64(n)
    84  	if tt.pushToManager {
    85  		tt.manager.PushUploaded(upload)
    86  	}
    87  	tt.UploadTotal.Add(upload)
    88  	return n, err
    89  }
    90  
    91  func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) {
    92  	upload := int64(buffer.Len())
    93  	err = tt.Conn.WriteBuffer(buffer)
    94  	if tt.pushToManager {
    95  		tt.manager.PushUploaded(upload)
    96  	}
    97  	tt.UploadTotal.Add(upload)
    98  	return
    99  }
   100  
   101  func (tt *tcpTracker) UnwrapWriter() (io.Writer, []N.CountFunc) {
   102  	return tt.Conn, []N.CountFunc{func(upload int64) {
   103  		if tt.pushToManager {
   104  			tt.manager.PushUploaded(upload)
   105  		}
   106  		tt.UploadTotal.Add(upload)
   107  	}}
   108  }
   109  
   110  func (tt *tcpTracker) Close() error {
   111  	tt.manager.Leave(tt)
   112  	return tt.Conn.Close()
   113  }
   114  
   115  func (tt *tcpTracker) Upstream() any {
   116  	return tt.Conn
   117  }
   118  
   119  func parseRemoteDestination(addr net.Addr, conn C.Connection) string {
   120  	if addr == nil && conn != nil {
   121  		return conn.RemoteDestination()
   122  	}
   123  	if addrPort, err := netip.ParseAddrPort(addr.String()); err == nil && addrPort.Addr().IsValid() {
   124  		return addrPort.Addr().String()
   125  	} else {
   126  		if conn != nil {
   127  			return conn.RemoteDestination()
   128  		} else {
   129  			return ""
   130  		}
   131  	}
   132  }
   133  
   134  func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *tcpTracker {
   135  	if conn != nil {
   136  		metadata.RemoteDst = parseRemoteDestination(conn.RemoteAddr(), conn)
   137  	}
   138  
   139  	t := &tcpTracker{
   140  		Conn:    conn,
   141  		manager: manager,
   142  		TrackerInfo: &TrackerInfo{
   143  			UUID:          utils.NewUUIDV4(),
   144  			Start:         time.Now(),
   145  			Metadata:      metadata,
   146  			Chain:         conn.Chains(),
   147  			Rule:          "",
   148  			UploadTotal:   atomic.NewInt64(uploadTotal),
   149  			DownloadTotal: atomic.NewInt64(downloadTotal),
   150  		},
   151  		pushToManager: pushToManager,
   152  	}
   153  
   154  	if pushToManager {
   155  		if uploadTotal > 0 {
   156  			manager.PushUploaded(uploadTotal)
   157  		}
   158  		if downloadTotal > 0 {
   159  			manager.PushDownloaded(downloadTotal)
   160  		}
   161  	}
   162  
   163  	if rule != nil {
   164  		t.TrackerInfo.Rule = rule.RuleType().String()
   165  		t.TrackerInfo.RulePayload = rule.Payload()
   166  	}
   167  
   168  	manager.Join(t)
   169  	return t
   170  }
   171  
   172  type udpTracker struct {
   173  	C.PacketConn `json:"-"`
   174  	*TrackerInfo
   175  	manager *Manager
   176  
   177  	pushToManager bool `json:"-"`
   178  }
   179  
   180  func (ut *udpTracker) ID() string {
   181  	return ut.UUID.String()
   182  }
   183  
   184  func (ut *udpTracker) Info() *TrackerInfo {
   185  	return ut.TrackerInfo
   186  }
   187  
   188  func (ut *udpTracker) ReadFrom(b []byte) (int, net.Addr, error) {
   189  	n, addr, err := ut.PacketConn.ReadFrom(b)
   190  	download := int64(n)
   191  	if ut.pushToManager {
   192  		ut.manager.PushDownloaded(download)
   193  	}
   194  	ut.DownloadTotal.Add(download)
   195  	return n, addr, err
   196  }
   197  
   198  func (ut *udpTracker) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
   199  	data, put, addr, err = ut.PacketConn.WaitReadFrom()
   200  	download := int64(len(data))
   201  	if ut.pushToManager {
   202  		ut.manager.PushDownloaded(download)
   203  	}
   204  	ut.DownloadTotal.Add(download)
   205  	return
   206  }
   207  
   208  func (ut *udpTracker) WriteTo(b []byte, addr net.Addr) (int, error) {
   209  	n, err := ut.PacketConn.WriteTo(b, addr)
   210  	upload := int64(n)
   211  	if ut.pushToManager {
   212  		ut.manager.PushUploaded(upload)
   213  	}
   214  	ut.UploadTotal.Add(upload)
   215  	return n, err
   216  }
   217  
   218  func (ut *udpTracker) Close() error {
   219  	ut.manager.Leave(ut)
   220  	return ut.PacketConn.Close()
   221  }
   222  
   223  func (ut *udpTracker) Upstream() any {
   224  	return ut.PacketConn
   225  }
   226  
   227  func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *udpTracker {
   228  	metadata.RemoteDst = parseRemoteDestination(nil, conn)
   229  
   230  	ut := &udpTracker{
   231  		PacketConn: conn,
   232  		manager:    manager,
   233  		TrackerInfo: &TrackerInfo{
   234  			UUID:          utils.NewUUIDV4(),
   235  			Start:         time.Now(),
   236  			Metadata:      metadata,
   237  			Chain:         conn.Chains(),
   238  			Rule:          "",
   239  			UploadTotal:   atomic.NewInt64(uploadTotal),
   240  			DownloadTotal: atomic.NewInt64(downloadTotal),
   241  		},
   242  		pushToManager: pushToManager,
   243  	}
   244  
   245  	if pushToManager {
   246  		if uploadTotal > 0 {
   247  			manager.PushUploaded(uploadTotal)
   248  		}
   249  		if downloadTotal > 0 {
   250  			manager.PushDownloaded(downloadTotal)
   251  		}
   252  	}
   253  
   254  	if rule != nil {
   255  		ut.TrackerInfo.Rule = rule.RuleType().String()
   256  		ut.TrackerInfo.RulePayload = rule.Payload()
   257  	}
   258  
   259  	manager.Join(ut)
   260  	return ut
   261  }