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 }