74 lines
2.7 KiB
Python
74 lines
2.7 KiB
Python
"""Inspect a torrent or demonstrate unchanged infohash and damaged piece detection."""
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
from metainfo import decode, encode, inspect, make_metainfo, verify
|
|
|
|
|
|
def describe(raw):
|
|
meta, infohash = inspect(raw)
|
|
info = meta[b"info"]
|
|
print(json.dumps({
|
|
"announce": meta[b"announce"].decode(),
|
|
"name": info[b"name"].decode(),
|
|
"length": info[b"length"],
|
|
"piece length": info[b"piece length"],
|
|
"pieces (SHA-1)": [info[b"pieces"][i:i + 20].hex()
|
|
for i in range(0, len(info[b"pieces"]), 20)],
|
|
"infohash (raw bencoded info)": infohash,
|
|
}, indent=2))
|
|
|
|
|
|
def demo(directory):
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
data = bytes(range(256)) * 1024
|
|
original = encode(make_metainfo("lesson.bin", data))
|
|
meta, original_info = decode(original)
|
|
changed = dict(meta)
|
|
changed[b"announce"] = b"http://127.0.0.1:6970/announce"
|
|
changed[b"comment"] = b"A different comment"
|
|
modified = encode(changed)
|
|
assert decode(modified)[1] == original_info
|
|
damaged = bytearray(data)
|
|
damaged[65536 + 17] ^= 1
|
|
for name, content in [("lesson.bin", data), ("lesson.torrent", original),
|
|
("changed.torrent", modified), ("damaged.bin", damaged)]:
|
|
(directory / name).write_bytes(content)
|
|
describe(original)
|
|
print("Original infohash:", inspect(original)[1])
|
|
print("Changed infohash:", inspect(modified)[1])
|
|
assert inspect(original)[1] == inspect(modified)[1]
|
|
good, _ = verify(original, data)
|
|
bad, indices = verify(original, damaged)
|
|
assert good and not bad and indices == [1]
|
|
print("PASS: changing announce/comment preserved infohash")
|
|
print("PASS: original file verified; damaged file rejected, piece index:", indices)
|
|
print("Files:", directory)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
subs = parser.add_subparsers(dest="command", required=True)
|
|
p = subs.add_parser("demo")
|
|
p.add_argument("--directory", type=Path, default=Path(".local/metadata"))
|
|
p = subs.add_parser("inspect")
|
|
p.add_argument("torrent", type=Path)
|
|
p = subs.add_parser("verify")
|
|
p.add_argument("torrent", type=Path)
|
|
p.add_argument("file", type=Path)
|
|
args = parser.parse_args()
|
|
if args.command == "demo":
|
|
demo(args.directory)
|
|
elif args.command == "inspect":
|
|
describe(args.torrent.read_bytes())
|
|
else:
|
|
ok, bad = verify(args.torrent.read_bytes(), args.file.read_bytes())
|
|
print("OK" if ok else "FAILED", "bad pieces:", bad)
|
|
return 0 if ok else 1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|