#!/usr/bin/env python3 """tracker.py [port] — a minimal HTTP BitTorrent tracker for the night's R-1261 rig (stdlib only). It records every peer that announces (by source address and the announced port) and answers the others in compact form.""" import http.server, socket, struct, sys, time, urllib.parse PEERS = {} # info_hash -> {(ip, port): last_seen} def benc(d): out = b"d" for k in sorted(d): v = d[k] out += b"%d:%s" % (len(k), k.encode()) out += (b"i%de" % v) if isinstance(v, int) else (b"%d:%s" % (len(v), v)) return out + b"e" class H(http.server.BaseHTTPRequestHandler): def do_GET(self): u = urllib.parse.urlsplit(self.path) q = urllib.parse.parse_qs(u.query, keep_blank_values=True, encoding="latin-1") ih = q.get("info_hash", [""])[0].encode("latin-1") port = int(q.get("port", ["0"])[0] or 0) ip = self.client_address[0] now = time.time() peers = PEERS.setdefault(ih, {}) if q.get("event", [""])[0] == "stopped": peers.pop((ip, port), None) else: peers[(ip, port)] = now others = [p for p, t in peers.items() if p != (ip, port) and now - t < 600] compact = b"".join(socket.inet_aton(a) + struct.pack(">H", pt) for a, pt in others) body = benc({"interval": 30, "peers": compact}) self.send_response(200) self.send_header("Content-Type", "text/plain") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) sys.stdout.write("%s announce %s:%d -> %d peer(s)\n" % (time.strftime("%H:%M:%S"), ip, port, len(others))) sys.stdout.flush() def log_message(self, *a): pass http.server.ThreadingHTTPServer(("0.0.0.0", int(sys.argv[1]) if len(sys.argv) > 1 else 6969), H).serve_forever()