44 lines
1.8 KiB
Python
44 lines
1.8 KiB
Python
#!/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()
|