tracker.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package trafficontrol
  2. import (
  3. "encoding/json"
  4. "net"
  5. "net/netip"
  6. "time"
  7. "github.com/sagernet/sing-box/adapter"
  8. "github.com/sagernet/sing/common"
  9. "github.com/sagernet/sing/common/atomic"
  10. "github.com/sagernet/sing/common/bufio"
  11. N "github.com/sagernet/sing/common/network"
  12. "github.com/gofrs/uuid/v5"
  13. )
  14. type Metadata struct {
  15. NetWork string `json:"network"`
  16. Type string `json:"type"`
  17. SrcIP netip.Addr `json:"sourceIP"`
  18. DstIP netip.Addr `json:"destinationIP"`
  19. SrcPort string `json:"sourcePort"`
  20. DstPort string `json:"destinationPort"`
  21. Host string `json:"host"`
  22. DNSMode string `json:"dnsMode"`
  23. ProcessPath string `json:"processPath"`
  24. }
  25. type tracker interface {
  26. ID() string
  27. Close() error
  28. Leave()
  29. }
  30. type trackerInfo struct {
  31. UUID uuid.UUID `json:"id"`
  32. Metadata Metadata `json:"metadata"`
  33. UploadTotal *atomic.Int64 `json:"upload"`
  34. DownloadTotal *atomic.Int64 `json:"download"`
  35. Start time.Time `json:"start"`
  36. Chain []string `json:"chains"`
  37. Rule string `json:"rule"`
  38. RulePayload string `json:"rulePayload"`
  39. }
  40. func (t trackerInfo) MarshalJSON() ([]byte, error) {
  41. return json.Marshal(map[string]any{
  42. "id": t.UUID.String(),
  43. "metadata": t.Metadata,
  44. "upload": t.UploadTotal.Load(),
  45. "download": t.DownloadTotal.Load(),
  46. "start": t.Start,
  47. "chains": t.Chain,
  48. "rule": t.Rule,
  49. "rulePayload": t.RulePayload,
  50. })
  51. }
  52. type tcpTracker struct {
  53. N.ExtendedConn `json:"-"`
  54. *trackerInfo
  55. manager *Manager
  56. }
  57. func (tt *tcpTracker) ID() string {
  58. return tt.UUID.String()
  59. }
  60. func (tt *tcpTracker) Close() error {
  61. tt.manager.Leave(tt)
  62. return tt.ExtendedConn.Close()
  63. }
  64. func (tt *tcpTracker) Leave() {
  65. tt.manager.Leave(tt)
  66. }
  67. func (tt *tcpTracker) Upstream() any {
  68. return tt.ExtendedConn
  69. }
  70. func (tt *tcpTracker) ReaderReplaceable() bool {
  71. return true
  72. }
  73. func (tt *tcpTracker) WriterReplaceable() bool {
  74. return true
  75. }
  76. func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *tcpTracker {
  77. uuid, _ := uuid.NewV4()
  78. var chain []string
  79. var next string
  80. if rule == nil {
  81. if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil {
  82. next = defaultOutbound.Tag()
  83. }
  84. } else {
  85. next = rule.Outbound()
  86. }
  87. for {
  88. chain = append(chain, next)
  89. detour, loaded := router.Outbound(next)
  90. if !loaded {
  91. break
  92. }
  93. group, isGroup := detour.(adapter.OutboundGroup)
  94. if !isGroup {
  95. break
  96. }
  97. next = group.Now()
  98. }
  99. upload := new(atomic.Int64)
  100. download := new(atomic.Int64)
  101. t := &tcpTracker{
  102. ExtendedConn: bufio.NewCounterConn(conn, []N.CountFunc{func(n int64) {
  103. upload.Add(n)
  104. manager.PushUploaded(n)
  105. }}, []N.CountFunc{func(n int64) {
  106. download.Add(n)
  107. manager.PushDownloaded(n)
  108. }}),
  109. manager: manager,
  110. trackerInfo: &trackerInfo{
  111. UUID: uuid,
  112. Start: time.Now(),
  113. Metadata: metadata,
  114. Chain: common.Reverse(chain),
  115. Rule: "",
  116. UploadTotal: upload,
  117. DownloadTotal: download,
  118. },
  119. }
  120. if rule != nil {
  121. t.trackerInfo.Rule = rule.String() + " => " + rule.Outbound()
  122. } else {
  123. t.trackerInfo.Rule = "final"
  124. }
  125. manager.Join(t)
  126. return t
  127. }
  128. type udpTracker struct {
  129. N.PacketConn `json:"-"`
  130. *trackerInfo
  131. manager *Manager
  132. }
  133. func (ut *udpTracker) ID() string {
  134. return ut.UUID.String()
  135. }
  136. func (ut *udpTracker) Close() error {
  137. ut.manager.Leave(ut)
  138. return ut.PacketConn.Close()
  139. }
  140. func (ut *udpTracker) Leave() {
  141. ut.manager.Leave(ut)
  142. }
  143. func (ut *udpTracker) Upstream() any {
  144. return ut.PacketConn
  145. }
  146. func (ut *udpTracker) ReaderReplaceable() bool {
  147. return true
  148. }
  149. func (ut *udpTracker) WriterReplaceable() bool {
  150. return true
  151. }
  152. func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *udpTracker {
  153. uuid, _ := uuid.NewV4()
  154. var chain []string
  155. var next string
  156. if rule == nil {
  157. if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil {
  158. next = defaultOutbound.Tag()
  159. }
  160. } else {
  161. next = rule.Outbound()
  162. }
  163. for {
  164. chain = append(chain, next)
  165. detour, loaded := router.Outbound(next)
  166. if !loaded {
  167. break
  168. }
  169. group, isGroup := detour.(adapter.OutboundGroup)
  170. if !isGroup {
  171. break
  172. }
  173. next = group.Now()
  174. }
  175. upload := new(atomic.Int64)
  176. download := new(atomic.Int64)
  177. ut := &udpTracker{
  178. PacketConn: bufio.NewCounterPacketConn(conn, []N.CountFunc{func(n int64) {
  179. upload.Add(n)
  180. manager.PushUploaded(n)
  181. }}, []N.CountFunc{func(n int64) {
  182. download.Add(n)
  183. manager.PushDownloaded(n)
  184. }}),
  185. manager: manager,
  186. trackerInfo: &trackerInfo{
  187. UUID: uuid,
  188. Start: time.Now(),
  189. Metadata: metadata,
  190. Chain: common.Reverse(chain),
  191. Rule: "",
  192. UploadTotal: upload,
  193. DownloadTotal: download,
  194. },
  195. }
  196. if rule != nil {
  197. ut.trackerInfo.Rule = rule.String() + " => " + rule.Outbound()
  198. } else {
  199. ut.trackerInfo.Rule = "final"
  200. }
  201. manager.Join(ut)
  202. return ut
  203. }