/* Copyright (c) 2015, Arvid Norberg All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.hpp" #include "libtorrent/bdecode.hpp" #include "libtorrent/entry.hpp" using namespace lt; // test integer TORRENT_TEST(integer) { char b[] = "i12453e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); span section = e.data_section(); TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0); TEST_EQUAL(section.size(), sizeof(b) - 1); TEST_EQUAL(e.type(), bdecode_node::int_t); TEST_EQUAL(e.int_value(), 12453); } TORRENT_TEST(construct_string) { entry e(std::string("abc123")); TEST_EQUAL(e.string(), "abc123"); } TORRENT_TEST(construct_string_literal) { entry e("abc123"); TEST_EQUAL(e.string(), "abc123"); } TORRENT_TEST(construct_string_view) { entry e(string_view("abc123")); TEST_EQUAL(e.string(), "abc123"); } TORRENT_TEST(construct_integer) { entry e(4); TEST_EQUAL(e.integer(), 4); } // test string TORRENT_TEST(string) { char b[] = "26:abcdefghijklmnopqrstuvwxyz"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); span section = e.data_section(); TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0); TEST_EQUAL(section.size(), sizeof(b) - 1); TEST_EQUAL(e.type(), bdecode_node::string_t); TEST_EQUAL(e.string_value(), std::string("abcdefghijklmnopqrstuvwxyz")); TEST_EQUAL(e.string_length(), 26); } // test string-prefix TORRENT_TEST(string_prefix1) { // test edge-case of a string that's nearly too long std::string test; test.resize(1000000 + 8); memcpy(&test[0], "1000000:", 8); // test is a valid bencoded string, that's quite long bdecode_node e; error_code ec; int ret = bdecode(test.c_str(), test.c_str() + test.size(), e, ec); TEST_EQUAL(ret, 0); std::printf("%d bytes string\n", e.string_length()); span section = e.data_section(); TEST_CHECK(std::memcmp(test.c_str(), section.data(), section.size()) == 0); TEST_EQUAL(section.size(), test.size()); TEST_EQUAL(e.type(), bdecode_node::string_t); TEST_EQUAL(e.string_length(), 1000000); TEST_EQUAL(e.string_ptr(), test.c_str() + 8); } // test list TORRENT_TEST(list) { char b[] = "li12453e3:aaae"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); span section = e.data_section(); TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0); TEST_EQUAL(section.size(), sizeof(b) - 1); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_size(), 2); TEST_EQUAL(e.list_at(0).type(), bdecode_node::int_t); TEST_EQUAL(e.list_at(1).type(), bdecode_node::string_t); TEST_EQUAL(e.list_at(0).int_value(), 12453); TEST_EQUAL(e.list_at(1).string_value(), std::string("aaa")); TEST_EQUAL(e.list_at(1).string_length(), 3); section = e.list_at(1).data_section(); TEST_CHECK(std::memcmp("3:aaa", section.data(), section.size()) == 0); TEST_EQUAL(section.size(), 5); } // test dict TORRENT_TEST(dict) { char b[] = "d1:ai12453e1:b3:aaa1:c3:bbb1:X10:0123456789e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); span section = e.data_section(); TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0); TEST_EQUAL(section.size(), sizeof(b) - 1); TEST_EQUAL(e.type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_size(), 4); TEST_EQUAL(e.dict_find("a").type(), bdecode_node::int_t); TEST_EQUAL(e.dict_find("a").int_value(), 12453); TEST_EQUAL(e.dict_find("b").type(), bdecode_node::string_t); TEST_EQUAL(e.dict_find("b").string_value(), std::string("aaa")); TEST_EQUAL(e.dict_find("b").string_length(), 3); TEST_EQUAL(e.dict_find("c").type(), bdecode_node::string_t); TEST_EQUAL(e.dict_find("c").string_value(), std::string("bbb")); TEST_EQUAL(e.dict_find("c").string_length(), 3); TEST_EQUAL(e.dict_find_string_value("X"), "0123456789"); char error_string[200]; TEST_CHECK(e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key")); } // test dictionary with a key without a value TORRENT_TEST(dict_key_novalue) { char b[] = "d1:ai1e1:be"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 10); TEST_EQUAL(ec, error_code(bdecode_errors::expected_value)); std::printf("%s\n", print_entry(e).c_str()); } // test dictionary with a key that's not a string TORRENT_TEST(dict_nonstring_key) { char b[] = "di5e1:ae"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); std::printf("%s\n", print_entry(e).c_str()); } // dictionary key with \0 TORRENT_TEST(dict_null_key) { char b[] = "d3:a\0bi1ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); TEST_CHECK(e.dict_size() == 1); bdecode_node d = e.dict_find(std::string("a\0b", 3)); TEST_EQUAL(d.type(), bdecode_node::int_t); TEST_EQUAL(d.int_value(), 1); } // soft error reported for dictionary with unordered keys TORRENT_TEST(dict_unordered_keys) { char error_string[200]; { char b[] = "d2:abi1e2:aai2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); TEST_CHECK(e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key")); } { char b[] = "d2:bai1e2:aai2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); TEST_CHECK(e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key")); } { char b[] = "d2:aai1e1:ai2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); TEST_CHECK(e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key")); } { char b[] = "d1:ai1e2:aai2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); TEST_CHECK(!e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key")); } { char b[] = "d2:aai1e1:bi2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); TEST_CHECK(!e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key")); } } TORRENT_TEST(dict_duplicate_key) { char b[] = "d2:aai1e2:aai2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); char error_string[200]; TEST_CHECK(e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("duplicate dictionary key")); } // premature e TORRENT_TEST(premature_e) { char b[] = "e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test strings with negative length-prefix TORRENT_TEST(negative_length_prefix) { char b[] = "-10:foobar"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 0); TEST_EQUAL(ec, error_code(bdecode_errors::expected_value)); std::printf("%s\n", print_entry(e).c_str()); } // test strings with overflow length-prefix TORRENT_TEST(overflow_length_prefix) { char b[] = "18446744073709551615:foobar"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 19); TEST_EQUAL(ec, error_code(bdecode_errors::overflow)); std::printf("%s\n", print_entry(e).c_str()); } // test strings with almost overflow (more than 8 digits) TORRENT_TEST(close_overflow_length_prefix) { char b[] = "99999999:foobar"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 8); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test strings with overflow (more than 8 digits) TORRENT_TEST(overflow_length_prefix2) { char b[] = "199999999:foobar"; bdecode_node e; error_code ec; int pos; // pretend that we have a large buffer like that int ret = bdecode(b, b + 999999999, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 0); TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded)); std::printf("%s\n", print_entry(e).c_str()); } TORRENT_TEST(leading_zero_length_prefix) { { char b[] = "06:foobar"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b) - 1, e, ec, &pos); TEST_EQUAL(ret, 0); char error_string[200]; TEST_CHECK(e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("leading zero in string length")); std::printf("%s\n", print_entry(e).c_str()); } { char b[] = "0:"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b) - 1, e, ec, &pos); TEST_EQUAL(ret, 0); char error_string[200]; TEST_CHECK(!e.has_soft_error(error_string)); std::printf("%s\n", print_entry(e).c_str()); } } // test integer without any digits TORRENT_TEST(nodigit_int) { char b[] = "ie"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); std::printf("%s\n", print_entry(e).c_str()); } // test integer with just a minus TORRENT_TEST(minus_int) { char b[] = "i-e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 2); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); std::printf("%s\n", print_entry(e).c_str()); } // test integer with a minus inserted in it TORRENT_TEST(interior_minus_int) { char b[] = "i35412-5633e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 6); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); std::printf("%s\n", print_entry(e).c_str()); } // test integers that don't fit in 64 bits TORRENT_TEST(int_overflow) { char b[] = "i18446744073709551615e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); // the lazy aspect makes this overflow when asking for // the value. turning it to zero. TEST_EQUAL(e.int_value(), 0); } // test integers with more than 20 digits (overflow on parsing) TORRENT_TEST(int_overflow2) { char b[] = "i184467440737095516154e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 22); TEST_EQUAL(ec, error_code(bdecode_errors::overflow)); std::printf("%s\n", print_entry(e).c_str()); } // test truncated negative integer TORRENT_TEST(int_truncated) { char b[] = "i-"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 2); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } TORRENT_TEST(int_leading_zero) { { char b[] = "i01e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); char error_string[200]; TEST_CHECK(e.has_soft_error(error_string)); TEST_EQUAL(std::string(error_string), std::string("leading zero in integer")); std::printf("%s\n", print_entry(e).c_str()); } { char b[] = "i0e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b) - 1, e, ec); TEST_EQUAL(ret, 0); char error_string[200]; TEST_CHECK(!e.has_soft_error(error_string)); std::printf("%s\n", print_entry(e).c_str()); } } // bdecode_error TORRENT_TEST(bdecode_error) { error_code ec(bdecode_errors::overflow); TEST_EQUAL(ec.message(), "integer overflow"); TEST_EQUAL(ec.category().name(), std::string("bdecode error")); ec.assign(5434, bdecode_category()); TEST_EQUAL(ec.message(), "Unknown error"); } // test integers that just exactly fit in 64 bits TORRENT_TEST(64bit_int) { char b[] = "i9223372036854775807e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_CHECK(e.int_value() == 9223372036854775807LL); } // test integers that just exactly fit in 64 bits TORRENT_TEST(64bit_int_negative) { char b[] = "i-9223372036854775807e"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_CHECK(e.int_value() == -9223372036854775807LL); } // test integers that have invalid digits TORRENT_TEST(int_invalid_digit) { char b[] = "i92337203t854775807e"; bdecode_node e; error_code ec; int pos = 0; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 9); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); std::printf("%s\n", print_entry(e).c_str()); } // test invalid encoding TORRENT_TEST(invalid_encoding) { unsigned char buf[] = { 0x64, 0x31, 0x3a, 0x61, 0x64, 0x32, 0x3a, 0x69 , 0x64, 0x32, 0x30, 0x3a, 0x2a, 0x21, 0x19, 0x89 , 0x9f, 0xcd, 0x5f, 0xc9, 0xbc, 0x80, 0xc1, 0x76 , 0xfe, 0xe0, 0xc6, 0x84, 0x2d, 0xf6, 0xfc, 0xb8 , 0x39, 0x3a, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68 , 0x61, 0xae, 0x68, 0x32, 0x30, 0x3a, 0x14, 0x78 , 0xd5, 0xb0, 0xdc, 0xf6, 0x82, 0x42, 0x32, 0xa0 , 0xd6, 0x88, 0xeb, 0x48, 0x57, 0x01, 0x89, 0x40 , 0x4e, 0xbc, 0x65, 0x31, 0x3a, 0x71, 0x39, 0x3a , 0x67, 0x65, 0x74, 0x5f, 0x70, 0x65, 0x65, 0x72 , 0x78, 0xff, 0x3a, 0x74, 0x38, 0x3a, 0xaa, 0xd4 , 0xa1, 0x88, 0x7a, 0x8d, 0xc3, 0xd6, 0x31, 0x3a , 0x79, 0x31, 0xae, 0x71, 0x65, 0}; std::printf("%s\n", buf); bdecode_node e; error_code ec; int ret = bdecode((char*)buf, (char*)buf + sizeof(buf), e, ec); TEST_CHECK(ret == -1); } // test the depth limit TORRENT_TEST(depth_limit) { char b[2048]; for (int i = 0; i < 1024; ++i) b[i]= 'l'; for (int i = 1024; i < 2048; ++i) b[i]= 'e'; // 1024 levels nested lists bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b), e, ec, nullptr, 100); TEST_CHECK(ret != 0); TEST_EQUAL(ec, error_code(bdecode_errors::depth_exceeded)); } // test the item limit TORRENT_TEST(item_limit) { char b[10240]; b[0] = 'l'; int i = 1; for (i = 1; i < 10239; i += 2) memcpy(&b[i], "0:", 2); b[i] = 'e'; bdecode_node e; error_code ec; int ret = bdecode(b, b + i + 1, e, ec, nullptr, 1000, 1000); TEST_CHECK(ret != 0); TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded)); } // test unexpected EOF TORRENT_TEST(unepected_eof) { char b[] = "l2:.."; // expected terminating 'e' bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF in string length TORRENT_TEST(unepected_eof2) { char b[] = "l2:..0"; // expected ':' delimiter instead of EOF bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 6); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test expected string TORRENT_TEST(expected_string) { char b[] = "di2ei0ee"; // expected string (dict keys must be strings) bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); std::printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing dict key TORRENT_TEST(unexpected_eof_dict_key) { char b[] = "d1000:..e"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing dict key TORRENT_TEST(unexpected_eof_dict_key2) { char b[] = "d1000:"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test expected string while parsing dict key TORRENT_TEST(expected_string_dict_key2) { char b[] = "df00:"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit)); std::printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing int TORRENT_TEST(unexpected_eof_int) { char b[] = "i"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test unexpected EOF while parsing int TORRENT_TEST(unexpected_eof_int2) { char b[] = "i10"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 3); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test expected colon TORRENT_TEST(expected_colon_dict) { char b[] = "d1000"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 5); TEST_EQUAL(ec, error_code(bdecode_errors::expected_colon)); std::printf("%s\n", print_entry(e).c_str()); } // test empty string TORRENT_TEST(empty_string) { char b[] = ""; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec, nullptr); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } // test partial string TORRENT_TEST(partial_string) { char b[] = "100:.."; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 3); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof)); std::printf("%s\n", print_entry(e).c_str()); } TORRENT_TEST(list_ints) { std::string buf; buf += "l"; for (int i = 0; i < 1000; ++i) { char tmp[20]; std::snprintf(tmp, sizeof(tmp), "i%de", i); buf += tmp; } buf += "e"; bdecode_node e; error_code ec; int ret = bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_size(), 1000); for (int i = 0; i < 1000; ++i) { TEST_EQUAL(e.list_int_value_at(i), i); } } TORRENT_TEST(dict_ints) { std::string buf; buf += "d"; for (int i = 0; i < 1000; ++i) { char tmp[30]; std::snprintf(tmp, sizeof(tmp), "4:%04di%de", i, i); buf += tmp; } buf += "e"; std::printf("%s\n", buf.c_str()); bdecode_node e; error_code ec; int ret = bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_size(), 1000); for (int i = 0; i < 1000; ++i) { char tmp[30]; std::snprintf(tmp, sizeof(tmp), "%04d", i); TEST_EQUAL(e.dict_find_int_value(tmp), i); } } // test dict_at TORRENT_TEST(dict_at) { char b[] = "d3:fooi1e3:bari2ee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_size(), 2); TEST_EQUAL(e.dict_at(0).first, "foo"); TEST_EQUAL(e.dict_at(0).second.type(), bdecode_node::int_t); TEST_EQUAL(e.dict_at(0).second.int_value(), 1); TEST_EQUAL(e.dict_at(1).first, "bar"); TEST_EQUAL(e.dict_at(1).second.type(), bdecode_node::int_t); TEST_EQUAL(e.dict_at(1).second.int_value(), 2); } // test string_ptr TORRENT_TEST(string_ptr) { char b[] = "l3:fooe"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_size(), 1); TEST_EQUAL(e.list_at(0).type(), bdecode_node::string_t); TEST_EQUAL(e.list_at(0).string_ptr(), b + 3); TEST_EQUAL(e.list_at(0).string_length(), 3); } // test exceeding buffer size limit TORRENT_TEST(exceed_buf_limit) { char b[] = "l3:fooe"; bdecode_node e; error_code ec; int ret = bdecode(b, b + 0x3fffffff, e, ec); TEST_EQUAL(ret, -1); TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded)); std::printf("%s\n", print_entry(e).c_str()); } // test parse_int TORRENT_TEST(parse_int) { char b[] = "1234567890e"; std::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); TEST_EQUAL(val, 1234567890); TEST_EQUAL(e, b + sizeof(b) - 2); } // test invalid digit TORRENT_TEST(invalid_digit) { char b[] = "0o"; std::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); TEST_EQUAL(ec, bdecode_errors::expected_digit); TEST_EQUAL(e, b + 1); } // test parse_int overflow TORRENT_TEST(parse_int_overflow) { char b[] = "9223372036854775808:"; std::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); TEST_EQUAL(ec, bdecode_errors::overflow); TEST_EQUAL(e, b + 18); } TORRENT_TEST(parse_length_overflow) { char const* b[] = { "d1:a1919191010:11111", "d2143289344:a4:aaaae", "d214328934114:a4:aaaae", "d9205357638345293824:a4:aaaae", "d1:a9205357638345293824:11111", }; for (int i = 0; i < int(sizeof(b)/sizeof(b[0])); ++i) { error_code ec; bdecode_node e; int ret = bdecode(b[i], b[i] + strlen(b[i]), e, ec); TEST_EQUAL(ret, -1); TEST_CHECK(ec == error_code(bdecode_errors::unexpected_eof)); } } TORRENT_TEST(expected_colon_string) { char b[] = "928"; std::int64_t val = 0; bdecode_errors::error_code_enum ec; char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); TEST_EQUAL(ec, bdecode_errors::expected_colon); TEST_EQUAL(e, b + 3); } // test dict_find_* functions TORRENT_TEST(dict_find_funs) { // a: int // b: string // c: list // d: dict char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::dict_t); // dict_find_int* TEST_EQUAL(e.dict_find_int_value("a"), 1); TEST_EQUAL(e.dict_find_int("a").type(), bdecode_node::int_t); TEST_EQUAL(e.dict_find_int_value("b", -10), -10); TEST_EQUAL(e.dict_find_int_value("x", -10), -10); TEST_EQUAL(e.dict_find_int("b").type(), bdecode_node::none_t); TEST_EQUAL(e.dict_find_int("x").type(), bdecode_node::none_t); // dict_find_string* TEST_EQUAL(e.dict_find_string_value("b"), "foo"); TEST_EQUAL(e.dict_find_string("b").type(), bdecode_node::string_t); TEST_EQUAL(e.dict_find_string_value("c", "blah"), "blah"); TEST_EQUAL(e.dict_find_string_value("x", "blah"), "blah"); TEST_EQUAL(e.dict_find_string("c").type(), bdecode_node::none_t); TEST_EQUAL(e.dict_find_string("x").type(), bdecode_node::none_t); // dict_find_list TEST_CHECK(e.dict_find_list("c")); TEST_EQUAL(e.dict_find_list("c").list_size(), 2); TEST_EQUAL(e.dict_find_list("c").list_int_value_at(0), 1); TEST_EQUAL(e.dict_find_list("c").list_int_value_at(1), 2); TEST_CHECK(!e.dict_find_list("d")); // dict_find_dict TEST_CHECK(e.dict_find_dict("d")); TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("x"), 1); TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("y", -10), -10); TEST_CHECK(!e.dict_find_dict("c")); // variants taking std::string TEST_EQUAL(e.dict_find_dict(std::string("d")).dict_find_int_value("x"), 1); TEST_CHECK(!e.dict_find_dict(std::string("c"))); TEST_CHECK(!e.dict_find_dict(std::string("x"))); TEST_EQUAL(e.dict_size(), 4); TEST_EQUAL(e.dict_size(), 4); // dict_at TEST_EQUAL(e.dict_at(0).first, "a"); TEST_EQUAL(e.dict_at(0).second.int_value(), 1); TEST_EQUAL(e.dict_at(1).first, "b"); TEST_EQUAL(e.dict_at(1).second.string_value(), "foo"); TEST_EQUAL(e.dict_at(2).first, "c"); TEST_EQUAL(e.dict_at(2).second.type(), bdecode_node::list_t); TEST_EQUAL(e.dict_at(3).first, "d"); TEST_EQUAL(e.dict_at(3).second.type(), bdecode_node::dict_t); } // test list_*_at functions TORRENT_TEST(list_at_funs) { // int // string // list // dict char b[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_int_value_at(0), 1); // make sure default values work TEST_EQUAL(e.list_int_value_at(1, -10), -10); TEST_EQUAL(e.list_string_value_at(1), "foo"); // make sure default values work TEST_EQUAL(e.list_string_value_at(2, "blah"), "blah"); TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t); TEST_EQUAL(e.list_at(2).list_size(), 2); TEST_EQUAL(e.list_at(2).list_int_value_at(0), 1); TEST_EQUAL(e.list_at(2).list_int_value_at(1), 2); TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t); TEST_EQUAL(e.list_at(3).dict_size(), 1); TEST_EQUAL(e.list_at(3).dict_find_int_value("x"), 1); TEST_EQUAL(e.list_at(3).dict_find_int_value("y", -10), -10); TEST_EQUAL(e.list_size(), 4); TEST_EQUAL(e.list_size(), 4); } // test list_at in reverse order TORRENT_TEST(list_at_reverse) { // int // string // list // dict char b[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t); TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t); TEST_EQUAL(e.list_string_value_at(1), "foo"); TEST_EQUAL(e.list_int_value_at(0), 1); TEST_EQUAL(e.list_size(), 4); TEST_EQUAL(e.list_size(), 4); } // test dict_find_* functions TORRENT_TEST(dict_find_funs2) { // a: int // b: string // c: list // d: dict char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(e.type(), bdecode_node::dict_t); // try finding the last item in a dict (to skip all the other ones) TEST_EQUAL(e.dict_find("d").type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_find(std::string("d")).type(), bdecode_node::dict_t); } // print_entry TORRENT_TEST(print_entry) { char b[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "[ 1, 'foo', [ 1, 2 ], { 'x': 1 } ]"); } TORRENT_TEST(print_entry2) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int ret = bdecode(b, b + sizeof(b)-1, e, ec); TEST_EQUAL(ret, 0); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': 1 } }"); } // test swap() TORRENT_TEST(swap) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "i1e"; bdecode_node e1; bdecode_node e2; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); ret = bdecode(b2, b2 + sizeof(b2)-1, e2, ec); TEST_EQUAL(ret, 0); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); std::printf("%s\n", print_entry(e1).c_str()); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); std::printf("%s\n", print_entry(e1).c_str()); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); std::printf("%s\n", print_entry(e1).c_str()); } // test swap() (one node is the root of the other node) TORRENT_TEST(swap_root) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e1; bdecode_node e2; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); e2 = e1.dict_find("c").list_at(0); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); std::printf("%s\n", print_entry(e1).c_str()); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); std::printf("%s\n", print_entry(e1).c_str()); // swap back e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); std::printf("%s\n", print_entry(e1).c_str()); } // test swap() (neither is a root and they don't share a root) TORRENT_TEST(swap_disjoint) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e1_root; bdecode_node e2_root; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1_root, ec); TEST_EQUAL(ret, 0); ret = bdecode(b2, b2 + sizeof(b2)-1, e2_root, ec); TEST_EQUAL(ret, 0); bdecode_node e1 = e1_root.dict_find("c").list_at(0); bdecode_node e2 = e2_root.list_at(1); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::string_t); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::string_t); TEST_EQUAL(e2.type(), bdecode_node::int_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); // swap back e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::int_t); TEST_EQUAL(e2.type(), bdecode_node::string_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); } // test swap() (one is a root and they don't share a root) TORRENT_TEST(swap_root_disjoint) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "li1e3:fooli1ei2eed1:xi1eee"; bdecode_node e1_root; bdecode_node e2; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1_root, ec); TEST_EQUAL(ret, 0); ret = bdecode(b2, b2 + sizeof(b2)-1, e2, ec); TEST_EQUAL(ret, 0); bdecode_node e1 = e1_root.dict_find("d"); std::string str1 = print_entry(e1); std::string str2 = print_entry(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::list_t); e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::list_t); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(print_entry(e1), str2); TEST_EQUAL(print_entry(e2), str1); // swap back e1.swap(e2); TEST_EQUAL(e1.type(), bdecode_node::dict_t); TEST_EQUAL(e2.type(), bdecode_node::list_t); TEST_EQUAL(print_entry(e1), str1); TEST_EQUAL(print_entry(e2), str2); } // make sure it's safe to reuse bdecode_nodes after clear() is called TORRENT_TEST(clear) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; char b2[] = "li1ei2ee"; bdecode_node e; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e, ec); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::dict_t); TEST_EQUAL(e.dict_size(), 4); TEST_EQUAL(e.dict_at(1).first, "b"); ret = bdecode(b2, b2 + sizeof(b2)-1, e, ec); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(ret, 0); TEST_EQUAL(e.type(), bdecode_node::list_t); TEST_EQUAL(e.list_size(), 2); TEST_EQUAL(e.list_int_value_at(1), 2); } // assignment/copy of root nodes TORRENT_TEST(copy_root) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e1; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e1.type(), bdecode_node::dict_t); std::printf("%s\n", print_entry(e1).c_str()); bdecode_node e2(e1); bdecode_node e3; e3 = e1; e1.clear(); TEST_EQUAL(e2.type(), bdecode_node::dict_t); TEST_EQUAL(e2.dict_size(), 4); TEST_EQUAL(e2.dict_at(1).first, "b"); TEST_EQUAL(e3.type(), bdecode_node::dict_t); TEST_EQUAL(e3.dict_size(), 4); TEST_EQUAL(e3.dict_at(1).first, "b"); } // non-owning references TORRENT_TEST(non_owning_refs) { char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee"; bdecode_node e1; error_code ec; int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec); TEST_EQUAL(ret, 0); TEST_EQUAL(e1.type(), bdecode_node::dict_t); std::printf("%s\n", print_entry(e1).c_str()); bdecode_node e2 = e1.non_owning(); TEST_EQUAL(e2.type(), bdecode_node::dict_t); e1.clear(); // e2 is invalid now } // test that a partial parse can be still be printed up to the // point where it faild TORRENT_TEST(partial_parse) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1-eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 35); TEST_EQUAL(e.type(), bdecode_node::dict_t); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': {} } }"); } TORRENT_TEST(partial_parse2) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:d-d1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 29); TEST_EQUAL(e.type(), bdecode_node::dict_t); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': {} }"); } TORRENT_TEST(partial_parse3) { char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee-1:dd1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 26); TEST_EQUAL(e.type(), bdecode_node::dict_t); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ] }"); } TORRENT_TEST(partial_parse4) { char b[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 22); TEST_EQUAL(e.type(), bdecode_node::dict_t); std::printf("%s\n", print_entry(e).c_str()); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1 ] }"); } // test switch_underlying_buffer TORRENT_TEST(switch_buffer) { char b1[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee"; char b2[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee"; bdecode_node e; error_code ec; int pos; int ret = bdecode(b1, b1 + sizeof(b1)-1, e, ec, &pos); TEST_EQUAL(ret, -1); TEST_EQUAL(pos, 22); TEST_EQUAL(e.type(), bdecode_node::dict_t); std::string string1 = print_entry(e); std::printf("%s\n", string1.c_str()); e.switch_underlying_buffer(b2); std::string string2 = print_entry(e); std::printf("%s\n", string2.c_str()); TEST_EQUAL(string1, string2); }