gapmind/services/dht/tracker.go
2026-09-11 23:59:05 -04:00

80 lines
1.9 KiB
Go

package main
import (
"bytes"
"encoding/binary"
"encoding/hex"
"net"
"time"
)
type TrackerMeta struct {
Seeders int
Leechers int
Completed int
}
func udpScrape(host string, infoHashes [][]byte) map[string]TrackerMeta {
out := map[string]TrackerMeta{}
if len(infoHashes) == 0 {
return out
}
addr, err := net.ResolveUDPAddr("udp", host)
if err != nil {
return out
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
return out
}
defer conn.Close()
_ = conn.SetDeadline(time.Now().Add(6 * time.Second))
transID := uint32(time.Now().UnixNano() & 0x7fffffff)
var req bytes.Buffer
_ = binary.Write(&req, binary.BigEndian, uint64(0x41727101980))
_ = binary.Write(&req, binary.BigEndian, uint32(0))
_ = binary.Write(&req, binary.BigEndian, transID)
if _, err := conn.Write(req.Bytes()); err != nil {
return out
}
resp := make([]byte, 16)
n, err := conn.Read(resp)
if err != nil || n < 16 || binary.BigEndian.Uint32(resp[4:8]) != transID {
return out
}
connID := resp[8:16]
if len(infoHashes) > 70 {
infoHashes = infoHashes[:70]
}
var sc bytes.Buffer
sc.Write(connID)
_ = binary.Write(&sc, binary.BigEndian, uint32(2))
_ = binary.Write(&sc, binary.BigEndian, transID+1)
for _, h := range infoHashes {
if len(h) == 20 {
sc.Write(h)
}
}
if _, err := conn.Write(sc.Bytes()); err != nil {
return out
}
buf := make([]byte, 8+12*74)
_ = conn.SetDeadline(time.Now().Add(6 * time.Second))
n, err = conn.Read(buf)
if err != nil || n < 8 {
return out
}
off := 8
for i, h := range infoHashes {
if off+12 > n {
break
}
_ = i
seeders := int(binary.BigEndian.Uint32(buf[off : off+4]))
done := int(binary.BigEndian.Uint32(buf[off+4 : off+8]))
leech := int(binary.BigEndian.Uint32(buf[off+8 : off+12]))
out[hex.EncodeToString(h)] = TrackerMeta{Seeders: seeders, Leechers: leech, Completed: done}
off += 12
}
return out
}