const std = @import("std"); const bencode = @import("bencode.zig"); // https://wiki.theory.org/BitTorrentSpecification#Tracker_Response pub const TrackerResp = struct { // interval: u64, peers: []std.net.Address, pub fn parse(a: std.mem.Allocator, b: bencode.BValue) !TrackerResp { var ipl = std.ArrayList(std.net.Address).init(a); defer ipl.deinit(); var d = b.asDict() catch return error.Malformatted; var pb = d.get("peers") orelse return error.Malformatted; var ps = pb.asString() catch return error.Malformatted; if ((ps.len % 6) != 0) return error.Malformatted; for (0..ps.len / 6) |ix| { const start = ix * 6; const port = std.mem.readInt(u16, ps[start + 4 .. start + 6][0..2], .big); var ip = [_]u8{0} ** 4; @memcpy(&ip, ps[start .. start + 4]); try ipl.append(std.net.Address.initIp4(ip, port)); } return .{ .peers = try ipl.toOwnedSlice(), }; } pub fn deinit(self: *TrackerResp, a: std.mem.Allocator) void { a.free(self.peers); } }; pub fn trackerRequestUrl(a: std.mem.Allocator, info_hash: [20]u8, peer_id: [20]u8, left: usize, announce: []const u8) ![]const u8 { var q = std.StringHashMap([]const u8).init(a); defer q.deinit(); try q.put("info_hash", &info_hash); try q.put("peer_id", &peer_id); try q.put("port", "6881"); try q.put("uploaded", "0"); try q.put("downloaded", "0"); var buf_left = [_]u8{0} ** 1024; try q.put("left", try std.fmt.bufPrint(&buf_left, "{}", .{left})); try q.put("compact", "1"); var qs = try toqs(a, q); defer a.free(qs); return try std.fmt.allocPrint(a, "{s}?{s}", .{ announce, qs }); } fn toqs(a: std.mem.Allocator, hm: std.StringHashMap([]const u8)) ![]const u8 { var al = std.ArrayList(u8).init(a); defer al.deinit(); var w = al.writer(); var it = hm.iterator(); var first = true; while (it.next()) |entry| { if (!first) try w.writeByte('&'); try std.Uri.writeEscapedQuery(w, entry.key_ptr.*); try w.writeByte('='); try std.Uri.writeEscapedQuery(w, entry.value_ptr.*); first = false; } return try al.toOwnedSlice(); }