sing-box/experimental/clashapi/trafficontrol/tracker.go

219 lines
4.4 KiB
Go
Raw Permalink Normal View History

2022-07-22 01:29:13 +00:00
package trafficontrol
2022-07-19 14:16:49 +00:00
import (
"net"
"net/netip"
"time"
"github.com/sagernet/sing-box/adapter"
2022-09-26 07:31:02 +00:00
"github.com/sagernet/sing-box/experimental/trackerconn"
2022-07-22 05:51:08 +00:00
"github.com/sagernet/sing/common"
2022-07-19 14:16:49 +00:00
N "github.com/sagernet/sing/common/network"
"github.com/gofrs/uuid"
"go.uber.org/atomic"
)
type Metadata struct {
NetWork string `json:"network"`
Type string `json:"type"`
SrcIP netip.Addr `json:"sourceIP"`
DstIP netip.Addr `json:"destinationIP"`
SrcPort string `json:"sourcePort"`
DstPort string `json:"destinationPort"`
Host string `json:"host"`
DNSMode string `json:"dnsMode"`
ProcessPath string `json:"processPath"`
}
type tracker interface {
ID() string
Close() error
2022-07-25 22:56:13 +00:00
Leave()
2022-07-19 14:16:49 +00:00
}
type trackerInfo struct {
UUID uuid.UUID `json:"id"`
Metadata Metadata `json:"metadata"`
UploadTotal *atomic.Int64 `json:"upload"`
DownloadTotal *atomic.Int64 `json:"download"`
Start time.Time `json:"start"`
Chain []string `json:"chains"`
Rule string `json:"rule"`
RulePayload string `json:"rulePayload"`
}
type tcpTracker struct {
2022-09-26 07:31:02 +00:00
N.ExtendedConn `json:"-"`
2022-07-19 14:16:49 +00:00
*trackerInfo
manager *Manager
}
func (tt *tcpTracker) ID() string {
return tt.UUID.String()
}
func (tt *tcpTracker) Close() error {
tt.manager.Leave(tt)
2022-09-26 07:31:02 +00:00
return tt.ExtendedConn.Close()
2022-07-19 14:16:49 +00:00
}
2022-07-25 22:56:13 +00:00
func (tt *tcpTracker) Leave() {
tt.manager.Leave(tt)
}
2022-08-08 12:56:53 +00:00
func (tt *tcpTracker) Upstream() any {
2022-09-26 07:31:02 +00:00
return tt.ExtendedConn
}
func (tt *tcpTracker) ReaderReplaceable() bool {
return true
}
func (tt *tcpTracker) WriterReplaceable() bool {
return true
2022-08-08 12:56:53 +00:00
}
2022-11-24 04:37:29 +00:00
func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *tcpTracker {
2022-07-19 14:16:49 +00:00
uuid, _ := uuid.NewV4()
2022-07-22 05:51:08 +00:00
var chain []string
var next string
if rule == nil {
2022-07-29 16:29:22 +00:00
next = router.DefaultOutbound(N.NetworkTCP).Tag()
2022-07-22 05:51:08 +00:00
} else {
next = rule.Outbound()
}
for {
chain = append(chain, next)
detour, loaded := router.Outbound(next)
if !loaded {
break
}
group, isGroup := detour.(adapter.OutboundGroup)
if !isGroup {
break
}
next = group.Now()
}
2022-09-26 07:31:02 +00:00
upload := atomic.NewInt64(0)
download := atomic.NewInt64(0)
2022-07-19 14:16:49 +00:00
t := &tcpTracker{
2022-10-01 01:56:09 +00:00
ExtendedConn: trackerconn.NewHook(conn, func(n int64) {
upload.Add(n)
manager.PushUploaded(n)
}, func(n int64) {
download.Add(n)
manager.PushDownloaded(n)
2022-11-24 04:37:29 +00:00
}),
2022-10-01 01:56:09 +00:00
manager: manager,
2022-07-19 14:16:49 +00:00
trackerInfo: &trackerInfo{
UUID: uuid,
Start: time.Now(),
Metadata: metadata,
2022-07-22 05:51:08 +00:00
Chain: common.Reverse(chain),
2022-07-19 14:16:49 +00:00
Rule: "",
2022-09-26 07:31:02 +00:00
UploadTotal: upload,
DownloadTotal: download,
2022-07-19 14:16:49 +00:00
},
}
if rule != nil {
2022-07-22 05:51:08 +00:00
t.trackerInfo.Rule = rule.String() + " => " + rule.Outbound()
} else {
t.trackerInfo.Rule = "final"
2022-07-19 14:16:49 +00:00
}
manager.Join(t)
return t
}
type udpTracker struct {
N.PacketConn `json:"-"`
*trackerInfo
manager *Manager
}
func (ut *udpTracker) ID() string {
return ut.UUID.String()
}
func (ut *udpTracker) Close() error {
ut.manager.Leave(ut)
return ut.PacketConn.Close()
}
2022-07-25 22:56:13 +00:00
func (ut *udpTracker) Leave() {
ut.manager.Leave(ut)
}
2022-08-08 12:56:53 +00:00
func (ut *udpTracker) Upstream() any {
return ut.PacketConn
}
2022-09-26 07:31:02 +00:00
func (ut *udpTracker) ReaderReplaceable() bool {
return true
}
func (ut *udpTracker) WriterReplaceable() bool {
return true
}
2022-07-22 05:51:08 +00:00
func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, router adapter.Router, rule adapter.Rule) *udpTracker {
2022-07-19 14:16:49 +00:00
uuid, _ := uuid.NewV4()
2022-07-22 05:51:08 +00:00
var chain []string
var next string
if rule == nil {
2022-07-29 16:29:22 +00:00
next = router.DefaultOutbound(N.NetworkUDP).Tag()
2022-07-22 05:51:08 +00:00
} else {
next = rule.Outbound()
}
for {
chain = append(chain, next)
detour, loaded := router.Outbound(next)
if !loaded {
break
}
group, isGroup := detour.(adapter.OutboundGroup)
if !isGroup {
break
}
next = group.Now()
}
2022-09-26 07:31:02 +00:00
upload := atomic.NewInt64(0)
download := atomic.NewInt64(0)
2022-07-19 14:16:49 +00:00
ut := &udpTracker{
2022-10-01 01:56:09 +00:00
PacketConn: trackerconn.NewHookPacket(conn, func(n int64) {
upload.Add(n)
manager.PushUploaded(n)
}, func(n int64) {
download.Add(n)
manager.PushDownloaded(n)
}),
manager: manager,
2022-07-19 14:16:49 +00:00
trackerInfo: &trackerInfo{
UUID: uuid,
Start: time.Now(),
Metadata: metadata,
2022-07-22 05:51:08 +00:00
Chain: common.Reverse(chain),
2022-07-19 14:16:49 +00:00
Rule: "",
2022-09-26 07:31:02 +00:00
UploadTotal: upload,
DownloadTotal: download,
2022-07-19 14:16:49 +00:00
},
}
if rule != nil {
2022-07-22 05:51:08 +00:00
ut.trackerInfo.Rule = rule.String() + " => " + rule.Outbound()
} else {
ut.trackerInfo.Rule = "final"
2022-07-19 14:16:49 +00:00
}
manager.Join(ut)
return ut
}