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 }