From e3c5be0a33e66b124ff261479cffa9f95ce53dc0 Mon Sep 17 00:00:00 2001 From: Martin Ashby Date: Sat, 11 Nov 2023 22:17:31 +0000 Subject: Make request to tracker, get and parse response to find peers list --- src/main.zig | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/metainfo.zig | 11 ++++++- 2 files changed, 104 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/main.zig b/src/main.zig index 14ab3c3..9966a55 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,10 +1,101 @@ const std = @import("std"); +const MetaInfo = @import("metainfo.zig"); +const bencode = @import("bencode.zig"); pub fn main() !void { - std.log.info("Hello, World", .{}); + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const a = gpa.allocator(); + + // open & parse the torrent file + const f = try std.fs.cwd().openFile("src/sample.torrent", .{}); + defer f.close(); + var fr = f.reader(); + var mib = try bencode.bdecode(a, fr); + defer mib.deinit(a); + var mi = try MetaInfo.parse(a, mib); + defer mi.deinit(a); + + // call the tracker... + var c = std.http.Client{ + .allocator = a, + }; + defer c.deinit(); + + var q = std.StringHashMap([]const u8).init(a); + defer q.deinit(); + var info_hash = try mi.info.hash(a); + var buf_left = [_]u8{0} ** 1024; + try q.put("info_hash", &info_hash); + try q.put("peer_id", "00112233445566778899"); + try q.put("port", "6881"); + try q.put("uploaded", "0"); + try q.put("downloaded", "0"); + try q.put("left", try std.fmt.bufPrint(&buf_left, "{}", .{mi.info.files[0].length})); + try q.put("compact", "1"); + var qs = try toqs(a, q); + defer a.free(qs); + var url = try std.fmt.allocPrint(a, "{s}?{s}", .{ mi.announce, qs }); + defer a.free(url); + var res = try c.fetch(a, .{ .location = .{ .url = url } }); + defer res.deinit(); + if (res.status == .ok) { + var trb = try bencode.bdecodeBuf(a, res.body.?); + defer trb.deinit(a); + var tr = try TrackerResp.parse(a, trb); + defer tr.deinit(a); + var w = std.io.getStdOut().writer(); + for (tr.peers) |peer| { + try std.fmt.format(w, "peer: {}\n", .{peer}); + } + } +} + +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(); } +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); + } +}; + test { - _ = @import("bencode.zig"); - _ = @import("metainfo.zig"); + _ = bencode; + _ = MetaInfo; } diff --git a/src/metainfo.zig b/src/metainfo.zig index 29cc655..cd00438 100644 --- a/src/metainfo.zig +++ b/src/metainfo.zig @@ -63,9 +63,13 @@ pub const Info = struct { const pri = pr.asInt(u1) catch return error.Malformatted; priv = pri == 1; } + // Validation.... + const ps = pp.asString() catch return error.Malformatted; + if (ps.len % 20 != 0) return error.Malformatted; + return .{ .piece_length = pl.asInt(u64) catch return error.Malformatted, - .pieces = pp.asString() catch return error.Malformatted, + .pieces = ps, .files = try files.toOwnedSlice(), .private = priv, }; @@ -112,6 +116,11 @@ pub const Info = struct { try b.bencode(w); return sha1.finalResult(); } + pub fn pieceHash(self: Info, ix: usize) ?[]const u8 { + const start = 20 * ix; + if (start >= self.pieces.len) return null; + return self.pieces[start .. start + 20]; + } }; info: Info, -- cgit v1.2.3-ZIG