diff --git a/src/main/java/com/mykola2312/retracker/bencode/BTree.java b/src/main/java/com/mykola2312/retracker/bencode/BTree.java index 18de2e6..3c24728 100644 --- a/src/main/java/com/mykola2312/retracker/bencode/BTree.java +++ b/src/main/java/com/mykola2312/retracker/bencode/BTree.java @@ -4,8 +4,8 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import com.mykola2312.retracker.bencode.error.BDecodeError; +import com.mykola2312.retracker.bencode.error.BDecodeMalformed; import com.mykola2312.retracker.bencode.error.BDecodeParseError; -import com.mykola2312.retracker.bencode.error.BDecodeUnknown; public class BTree { private BValue root = null; @@ -40,22 +40,31 @@ public class BTree { public BValue decode() throws BDecodeError { BType type; + if (data.length < 2) { + /* no bencode data can be less than 2 bytes: for integer must be 'i0e' atleast, + * strings must be at least '0:' and lists and dicts can be empty + */ + throw new BDecodeMalformed(data, offset, "data is shorter than 2 bytes"); + } // consume and determine type switch (data[offset]) { case BE_INTEGER: type = BType.INTEGER; break; case BE_LIST: type = BType.LIST; break; case BE_DICT: type = BType.DICT; break; - default: throw new BDecodeUnknown(data, offset, data[offset]); + default: type = BType.STRING; } offset++; if (type.equals(BType.INTEGER)) { // advance until we hit end marker int end = offset; - while (data[end] != BDecoder.BE_END) { + while (end < data.length && data[end] != BDecoder.BE_END) { end++; } + if (end == data.length) { + throw new BDecodeMalformed(data, offset, "no integer terminator"); + } // convert bytes to string and string to integer byte[] bytes = Arrays.copyOfRange(data, offset, end); return new BInteger(parseLong(bytes)); diff --git a/src/main/java/com/mykola2312/retracker/bencode/error/BDecodeMalformed.java b/src/main/java/com/mykola2312/retracker/bencode/error/BDecodeMalformed.java new file mode 100644 index 0000000..d839097 --- /dev/null +++ b/src/main/java/com/mykola2312/retracker/bencode/error/BDecodeMalformed.java @@ -0,0 +1,13 @@ +package com.mykola2312.retracker.bencode.error; + +/* this exception should be thrown in cases buffer overrun, + * missing terminators and other nasty stuff. but it also + * could signal that there is parsing logic error + */ +public class BDecodeMalformed extends BDecodeError { + private static final long serialVersionUID = -8134440302174728903L; + + public BDecodeMalformed(byte[] data, int offset, String reason) { + super(data, offset, reason); + } +} diff --git a/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java b/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java index 2f8ead0..7c7abf8 100644 --- a/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java +++ b/src/test/java/com/mykola2312/retracker/bencode/BTreeTest.java @@ -3,10 +3,12 @@ package com.mykola2312.retracker.bencode; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 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 org.junit.jupiter.api.Test; import com.mykola2312.retracker.bencode.error.BDecodeError; +import com.mykola2312.retracker.bencode.error.BDecodeMalformed; public class BTreeTest { @Test @@ -24,4 +26,14 @@ public class BTreeTest { System.out.println("testParseInt: " + root.toString()); }); } + + @Test + public void testParseIntOverrun() throws BDecodeError { + final byte[] data = "i696969".getBytes(); + + BTree tree = new BTree(); + assertThrows(BDecodeMalformed.class, () -> { + tree.decode(data); + }); + } }