From ef86c78ba473d6fa9505690f1cc9eb2437806ac2 Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Wed, 16 Oct 2024 02:29:30 +0300 Subject: [PATCH] bencode decoding is complete and fully tested on real torrent files --- .../mykola2312/retracker/bencode/BTree.java | 2 + .../retracker/bencode/BTreeTest.java | 47 ++++++++++++++++++ test/test.torrent | Bin 0 -> 8622 bytes 3 files changed, 49 insertions(+) create mode 100644 test/test.torrent diff --git a/src/main/java/com/mykola2312/retracker/bencode/BTree.java b/src/main/java/com/mykola2312/retracker/bencode/BTree.java index 71937bf..edc8d0f 100644 --- a/src/main/java/com/mykola2312/retracker/bencode/BTree.java +++ b/src/main/java/com/mykola2312/retracker/bencode/BTree.java @@ -98,6 +98,8 @@ public class BTree { // advance past terminator offset++; return dict; + } else if (type == BDecoder.BE_END) { + throw new BDecodeMalformed(data, offset, "encountered terminator where it shouldn't be"); } else { // string // since string does not have leading type byte, move back offset offset--; diff --git a/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java b/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java index 721493e..910d05b 100644 --- a/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java +++ b/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java @@ -5,10 +5,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + import org.junit.jupiter.api.Test; import com.mykola2312.retracker.bencode.error.BDecodeError; import com.mykola2312.retracker.bencode.error.BDecodeMalformed; +import com.mykola2312.retracker.bencode.error.BError; public class BTreeTest { @Test @@ -116,4 +121,46 @@ public class BTreeTest { assertEquals(new BInteger(69), root.get(BType.INTEGER, "second")); }); } + + @Test + public void testNestedDict() throws BError { + final byte[] data = "d5:firstd6:secondd5:thirdi1337eeee".getBytes(); + + BTree tree = new BTree(); + assertDoesNotThrow(() -> { + tree.decode(data); + + BInteger value = tree + .asDict() + .get(BType.DICT, "first") + .get(BType.DICT, "second") + .get(BType.INTEGER, "third"); + assertNotNull(value); + assertEquals(new BInteger(1337), value); + }); + } + + @Test + public void testTorrentFile() throws BError, IOException { + final byte[] data = Files.readAllBytes(Path.of("test", "test.torrent")); + + BTree torrent = new BTree(); + assertDoesNotThrow(() -> { + torrent.decode(data); + + BDict root = torrent.asDict(); + assertEquals(new BString("http://example.com/announce"), root.get(BType.STRING, "announce")); + assertEquals(new BString("Test Comment"), root.get(BType.STRING, "comment")); + assertEquals(new BString("qBittorrent v4.6.5"), root.get(BType.STRING, "created by")); + assertEquals(new BInteger(1729033917), root.get(BType.INTEGER, "creation date")); + + BInteger pieceLength = root + .get(BType.DICT, "info") + .get(BType.DICT, "file tree") + .get(BType.DICT, "random-data.bin") + .get(BType.DICT, "") + .get(BType.INTEGER, "length"); + assertEquals(new BInteger(16777216), pieceLength); + }); + } } diff --git a/test/test.torrent b/test/test.torrent new file mode 100644 index 0000000000000000000000000000000000000000..a3e53f4ad4a45f6b0724f106e59e59a9f8021ddf GIT binary patch literal 8622 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