tracker.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package trafficontrol
  2. import (
  3. "net"
  4. "sync/atomic"
  5. "time"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/sagernet/sing/common"
  8. "github.com/sagernet/sing/common/bufio"
  9. F "github.com/sagernet/sing/common/format"
  10. "github.com/sagernet/sing/common/json"
  11. N "github.com/sagernet/sing/common/network"
  12. "github.com/gofrs/uuid/v5"
  13. )
  14. type TrackerMetadata struct {
  15. ID uuid.UUID
  16. Metadata adapter.InboundContext
  17. CreatedAt time.Time
  18. ClosedAt time.Time
  19. Upload *atomic.Int64
  20. Download *atomic.Int64
  21. Chain []string
  22. Rule adapter.Rule
  23. Outbound string
  24. OutboundType string
  25. }
  26. func (t TrackerMetadata) MarshalJSON() ([]byte, error) {
  27. var inbound string
  28. if t.Metadata.Inbound != "" {
  29. inbound = t.Metadata.InboundType + "/" + t.Metadata.Inbound
  30. } else {
  31. inbound = t.Metadata.InboundType
  32. }
  33. var domain string
  34. if t.Metadata.Domain != "" {
  35. domain = t.Metadata.Domain
  36. } else {
  37. domain = t.Metadata.Destination.Fqdn
  38. }
  39. var processPath string
  40. if t.Metadata.ProcessInfo != nil {
  41. if t.Metadata.ProcessInfo.ProcessPath != "" {
  42. processPath = t.Metadata.ProcessInfo.ProcessPath
  43. } else if t.Metadata.ProcessInfo.AndroidPackageName != "" {
  44. processPath = t.Metadata.ProcessInfo.AndroidPackageName
  45. }
  46. if processPath == "" {
  47. if t.Metadata.ProcessInfo.UserId != -1 {
  48. processPath = F.ToString(t.Metadata.ProcessInfo.UserId)
  49. }
  50. } else if t.Metadata.ProcessInfo.UserName != "" {
  51. processPath = F.ToString(processPath, " (", t.Metadata.ProcessInfo.UserName, ")")
  52. } else if t.Metadata.ProcessInfo.UserId != -1 {
  53. processPath = F.ToString(processPath, " (", t.Metadata.ProcessInfo.UserId, ")")
  54. }
  55. }
  56. var rule string
  57. if t.Rule != nil {
  58. rule = F.ToString(t.Rule, " => ", t.Rule.Action())
  59. } else {
  60. rule = "final"
  61. }
  62. return json.Marshal(map[string]any{
  63. "id": t.ID,
  64. "metadata": map[string]any{
  65. "network": t.Metadata.Network,
  66. "type": inbound,
  67. "sourceIP": t.Metadata.Source.Addr,
  68. "destinationIP": t.Metadata.Destination.Addr,
  69. "sourcePort": F.ToString(t.Metadata.Source.Port),
  70. "destinationPort": F.ToString(t.Metadata.Destination.Port),
  71. "host": domain,
  72. "dnsMode": "normal",
  73. "processPath": processPath,
  74. },
  75. "upload": t.Upload.Load(),
  76. "download": t.Download.Load(),
  77. "start": t.CreatedAt,
  78. "chains": t.Chain,
  79. "rule": rule,
  80. "rulePayload": "",
  81. })
  82. }
  83. type Tracker interface {
  84. Metadata() TrackerMetadata
  85. Close() error
  86. }
  87. type TCPConn struct {
  88. N.ExtendedConn
  89. metadata TrackerMetadata
  90. manager *Manager
  91. }
  92. func (tt *TCPConn) Metadata() TrackerMetadata {
  93. return tt.metadata
  94. }
  95. func (tt *TCPConn) Close() error {
  96. tt.manager.Leave(tt)
  97. return tt.ExtendedConn.Close()
  98. }
  99. func (tt *TCPConn) Upstream() any {
  100. return tt.ExtendedConn
  101. }
  102. func (tt *TCPConn) ReaderReplaceable() bool {
  103. return true
  104. }
  105. func (tt *TCPConn) WriterReplaceable() bool {
  106. return true
  107. }
  108. func NewTCPTracker(conn net.Conn, manager *Manager, metadata adapter.InboundContext, outboundManager adapter.OutboundManager, matchRule adapter.Rule, matchOutbound adapter.Outbound) *TCPConn {
  109. id, _ := uuid.NewV4()
  110. var (
  111. chain []string
  112. next string
  113. outbound string
  114. outboundType string
  115. )
  116. if matchOutbound != nil {
  117. next = matchOutbound.Tag()
  118. } else {
  119. next = outboundManager.Default().Tag()
  120. }
  121. for {
  122. detour, loaded := outboundManager.Outbound(next)
  123. if !loaded {
  124. break
  125. }
  126. chain = append(chain, next)
  127. outbound = detour.Tag()
  128. outboundType = detour.Type()
  129. group, isGroup := detour.(adapter.OutboundGroup)
  130. if !isGroup {
  131. break
  132. }
  133. next = group.Now()
  134. }
  135. upload := new(atomic.Int64)
  136. download := new(atomic.Int64)
  137. tracker := &TCPConn{
  138. ExtendedConn: bufio.NewCounterConn(conn, []N.CountFunc{func(n int64) {
  139. upload.Add(n)
  140. manager.PushUploaded(n)
  141. }}, []N.CountFunc{func(n int64) {
  142. download.Add(n)
  143. manager.PushDownloaded(n)
  144. }}),
  145. metadata: TrackerMetadata{
  146. ID: id,
  147. Metadata: metadata,
  148. CreatedAt: time.Now(),
  149. Upload: upload,
  150. Download: download,
  151. Chain: common.Reverse(chain),
  152. Rule: matchRule,
  153. Outbound: outbound,
  154. OutboundType: outboundType,
  155. },
  156. manager: manager,
  157. }
  158. manager.Join(tracker)
  159. return tracker
  160. }
  161. type UDPConn struct {
  162. N.PacketConn `json:"-"`
  163. metadata TrackerMetadata
  164. manager *Manager
  165. }
  166. func (ut *UDPConn) Metadata() TrackerMetadata {
  167. return ut.metadata
  168. }
  169. func (ut *UDPConn) Close() error {
  170. ut.manager.Leave(ut)
  171. return ut.PacketConn.Close()
  172. }
  173. func (ut *UDPConn) Upstream() any {
  174. return ut.PacketConn
  175. }
  176. func (ut *UDPConn) ReaderReplaceable() bool {
  177. return true
  178. }
  179. func (ut *UDPConn) WriterReplaceable() bool {
  180. return true
  181. }
  182. func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata adapter.InboundContext, outboundManager adapter.OutboundManager, matchRule adapter.Rule, matchOutbound adapter.Outbound) *UDPConn {
  183. id, _ := uuid.NewV4()
  184. var (
  185. chain []string
  186. next string
  187. outbound string
  188. outboundType string
  189. )
  190. if matchOutbound != nil {
  191. next = matchOutbound.Tag()
  192. } else {
  193. next = outboundManager.Default().Tag()
  194. }
  195. for {
  196. detour, loaded := outboundManager.Outbound(next)
  197. if !loaded {
  198. break
  199. }
  200. chain = append(chain, next)
  201. outbound = detour.Tag()
  202. outboundType = detour.Type()
  203. group, isGroup := detour.(adapter.OutboundGroup)
  204. if !isGroup {
  205. break
  206. }
  207. next = group.Now()
  208. }
  209. upload := new(atomic.Int64)
  210. download := new(atomic.Int64)
  211. trackerConn := &UDPConn{
  212. PacketConn: bufio.NewCounterPacketConn(conn, []N.CountFunc{func(n int64) {
  213. upload.Add(n)
  214. manager.PushUploaded(n)
  215. }}, []N.CountFunc{func(n int64) {
  216. download.Add(n)
  217. manager.PushDownloaded(n)
  218. }}),
  219. metadata: TrackerMetadata{
  220. ID: id,
  221. Metadata: metadata,
  222. CreatedAt: time.Now(),
  223. Upload: upload,
  224. Download: download,
  225. Chain: common.Reverse(chain),
  226. Rule: matchRule,
  227. Outbound: outbound,
  228. OutboundType: outboundType,
  229. },
  230. manager: manager,
  231. }
  232. manager.Join(trackerConn)
  233. return trackerConn
  234. }