Introduce UDP Tracker Protocol sniffer

Signed-off-by: iosmanthus <myosmanthustree@gmail.com>
This commit is contained in:
iosmanthus 2024-05-23 16:33:16 +08:00
parent f97687e85f
commit 99601b49dd
No known key found for this signature in database
GPG Key ID: DEE5BAABFE092169
4 changed files with 65 additions and 0 deletions

View File

@ -11,6 +11,18 @@ import (
"github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/constant"
) )
const (
trackerConnectFlag = iota
trackerAnnounceFlag
trackerScrapeFlag
trackerProtocolID = 0x41727101980
trackerConnectMinSize = 16
trackerAnnounceMinSize = 20
trackerScrapeMinSize = 8
)
// BitTorrent detects if the stream is a BitTorrent connection. // BitTorrent detects if the stream is a BitTorrent connection.
// For the BitTorrent protocol specification, see https://www.bittorrent.org/beps/bep_0003.html // For the BitTorrent protocol specification, see https://www.bittorrent.org/beps/bep_0003.html
func BitTorrent(_ context.Context, reader io.Reader) (*adapter.InboundContext, error) { func BitTorrent(_ context.Context, reader io.Reader) (*adapter.InboundContext, error) {
@ -78,3 +90,24 @@ func UTP(_ context.Context, packet []byte) (*adapter.InboundContext, error) {
Protocol: constant.ProtocolUTP, Protocol: constant.ProtocolUTP,
}, nil }, nil
} }
// UDPTracker detects if the packet is a UDP Tracker Protocol packet.
// For the UDP Tracker Protocol specification, see https://www.bittorrent.org/beps/bep_0015.html
func UDPTracker(_ context.Context, packet []byte) (*adapter.InboundContext, error) {
switch {
case len(packet) >= trackerConnectMinSize &&
binary.BigEndian.Uint64(packet[:8]) == trackerProtocolID &&
binary.BigEndian.Uint32(packet[8:12]) == trackerConnectFlag:
fallthrough
case len(packet) >= trackerAnnounceMinSize &&
binary.BigEndian.Uint32(packet[8:12]) == trackerAnnounceFlag:
fallthrough
case len(packet) >= trackerScrapeMinSize &&
binary.BigEndian.Uint32(packet[8:12]) == trackerScrapeFlag:
return &adapter.InboundContext{
Protocol: constant.ProtocolUDPTracker,
}, nil
default:
return nil, os.ErrInvalid
}
}

View File

@ -49,3 +49,33 @@ func TestSniffUTP(t *testing.T) {
require.Equal(t, constant.ProtocolUTP, metadata.Protocol) require.Equal(t, constant.ProtocolUTP, metadata.Protocol)
} }
} }
func TestSniffUDPTracker(t *testing.T) {
t.Parallel()
connectPackets := []string{
// connect packets
"00000417271019800000000078e90560",
"00000417271019800000000022c5d64d",
"000004172710198000000000b3863541",
// announce packets
"3d7592ead4b8c9e300000001b871a3820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002092f616e6e6f756e6365",
"3d7592ead4b8c9e30000000188deed1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002092f616e6e6f756e6365",
"3d7592ead4b8c9e300000001ceb948ad0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a3362cdb7020ff920e5aa642c3d4066950dd1f01f4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000002092f616e6e6f756e6365",
// scrape packets
"3d7592ead4b8c9e300000002d2f4bba5a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
"3d7592ead4b8c9e300000002441243292aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
"3d7592ead4b8c9e300000002b2aa461b1ad1fa9661cf3fe45fb2504ad52ec6c67758e294",
}
for _, pkt := range connectPackets {
pkt, err := hex.DecodeString(pkt)
require.NoError(t, err)
metadata, err := sniff.UDPTracker(context.TODO(), pkt)
require.NoError(t, err)
require.Equal(t, constant.ProtocolUDPTracker, metadata.Protocol)
}
}

View File

@ -8,4 +8,5 @@ const (
ProtocolSTUN = "stun" ProtocolSTUN = "stun"
ProtocolBitTorrent = "bittorrent" ProtocolBitTorrent = "bittorrent"
ProtocolUTP = "utp" ProtocolUTP = "utp"
ProtocolUDPTracker = "udp-tracker"
) )

View File

@ -924,6 +924,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
sniff.QUICClientHello, sniff.QUICClientHello,
sniff.STUNMessage, sniff.STUNMessage,
sniff.UTP, sniff.UTP,
sniff.UDPTracker,
) )
if sniffMetadata != nil { if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol metadata.Protocol = sniffMetadata.Protocol