1 /*
2 
3 Copyright (c) 2016, Arvid Norberg
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 
10     * Redistributions of source code must retain the above copyright
11       notice, this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright
13       notice, this list of conditions and the following disclaimer in
14       the documentation and/or other materials provided with the distribution.
15     * Neither the name of the author nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18 
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30 
31 */
32 
33 #include "test.hpp"
34 #include "test_utils.hpp"
35 
36 #include <vector>
37 
38 #include "libtorrent/entry.hpp"
39 #include "libtorrent/torrent_info.hpp"
40 #include "libtorrent/random.hpp"
41 #include "libtorrent/create_torrent.hpp"
42 #include "libtorrent/bencode.hpp"
43 #include "libtorrent/add_torrent_params.hpp"
44 #include "libtorrent/read_resume_data.hpp"
45 #include "libtorrent/write_resume_data.hpp"
46 
47 using namespace lt;
48 
TORRENT_TEST(read_resume)49 TORRENT_TEST(read_resume)
50 {
51 	entry rd;
52 
53 	rd["file-format"] = "libtorrent resume file";
54 	rd["file-version"] = 1;
55 	rd["info-hash"] = "abcdefghijklmnopqrst";
56 	rd["pieces"] = "\x01\x01\x01\x01\x01\x01";
57 
58 	rd["total_uploaded"] = 1337;
59 	rd["total_downloaded"] = 1338;
60 	rd["active_time"] = 1339;
61 	rd["seeding_time"] = 1340;
62 	rd["upload_rate_limit"] = 1343;
63 	rd["download_rate_limit"] = 1344;
64 	rd["max_connections"] = 1345;
65 	rd["max_uploads"] = 1346;
66 	rd["seed_mode"] = 0;
67 	rd["super_seeding"] = 0;
68 	rd["added_time"] = 1347;
69 	rd["completed_time"] = 1348;
70 	rd["finished_time"] = 1352;
71 
72 	rd["piece_priority"] = "\x01\x02\x03\x04\x05\x06";
73 	rd["auto_managed"] = 0;
74 	rd["sequential_download"] = 0;
75 	rd["paused"] = 0;
76 
77 	std::vector<char> resume_data;
78 	bencode(std::back_inserter(resume_data), rd);
79 
80 	add_torrent_params atp = read_resume_data(resume_data);
81 
82 	TEST_EQUAL(atp.info_hash, sha1_hash("abcdefghijklmnopqrst"));
83 	TEST_EQUAL(atp.have_pieces.size(), 6);
84 	TEST_EQUAL(atp.have_pieces.count(), 6);
85 
86 	TEST_EQUAL(atp.total_uploaded, 1337);
87 	TEST_EQUAL(atp.total_downloaded, 1338);
88 	TEST_EQUAL(atp.active_time, 1339);
89 	TEST_EQUAL(atp.seeding_time, 1340);
90 	TEST_EQUAL(atp.upload_limit, 1343);
91 	TEST_EQUAL(atp.download_limit, 1344);
92 	TEST_EQUAL(atp.max_connections, 1345);
93 	TEST_EQUAL(atp.max_uploads, 1346);
94 
95 	torrent_flags_t const flags_mask
96 		= torrent_flags::seed_mode
97 		| torrent_flags::super_seeding
98 		| torrent_flags::auto_managed
99 		| torrent_flags::paused
100 		| torrent_flags::sequential_download;
101 
102 	TEST_CHECK(!(atp.flags & flags_mask));
103 	TEST_EQUAL(atp.added_time, 1347);
104 	TEST_EQUAL(atp.completed_time, 1348);
105 	TEST_EQUAL(atp.finished_time, 1352);
106 
107 	TEST_EQUAL(atp.piece_priorities.size(), 6);
108 	TEST_EQUAL(atp.piece_priorities[0], 1_pri);
109 	TEST_EQUAL(atp.piece_priorities[1], 2_pri);
110 	TEST_EQUAL(atp.piece_priorities[2], 3_pri);
111 	TEST_EQUAL(atp.piece_priorities[3], 4_pri);
112 	TEST_EQUAL(atp.piece_priorities[4], 5_pri);
113 	TEST_EQUAL(atp.piece_priorities[5], 6_pri);
114 }
115 
TORRENT_TEST(read_resume_missing_info_hash)116 TORRENT_TEST(read_resume_missing_info_hash)
117 {
118 	entry rd;
119 
120 	rd["file-format"] = "libtorrent resume file";
121 	rd["file-version"] = 1;
122 	// missing info-hash
123 
124 	std::vector<char> resume_data;
125 	bencode(std::back_inserter(resume_data), rd);
126 
127 	error_code ec;
128 	add_torrent_params atp = read_resume_data(resume_data, ec);
129 	TEST_EQUAL(ec, error_code(errors::missing_info_hash));
130 }
131 
TORRENT_TEST(read_resume_missing_file_format)132 TORRENT_TEST(read_resume_missing_file_format)
133 {
134 	entry rd;
135 
136 	// missing file-format
137 	rd["file-version"] = 1;
138 	rd["info-hash"] = "abcdefghijklmnopqrst";
139 
140 	std::vector<char> resume_data;
141 	bencode(std::back_inserter(resume_data), rd);
142 
143 	error_code ec;
144 	add_torrent_params atp = read_resume_data(resume_data, ec);
145 	TEST_EQUAL(ec, error_code(errors::invalid_file_tag));
146 }
147 
TORRENT_TEST(read_resume_mismatching_torrent)148 TORRENT_TEST(read_resume_mismatching_torrent)
149 {
150 	entry rd;
151 
152 	rd["file-format"] = "libtorrent resume file";
153 	rd["file-version"] = 1;
154 	rd["info-hash"] = "abcdefghijklmnopqrst";
155 	entry& info = rd["info"];
156 	info["piece length"] = 16384 * 16;
157 	info["name"] = "test";
158 
159 
160 	std::vector<char> resume_data;
161 	bencode(std::back_inserter(resume_data), rd);
162 
163 	// the info-hash field does not match the torrent in the "info" field, so it
164 	// will be ignored
165 	add_torrent_params atp = read_resume_data(resume_data);
166 	TEST_CHECK(!atp.ti);
167 }
168 
169 namespace {
generate_torrent()170 std::shared_ptr<torrent_info> generate_torrent()
171 {
172 	file_storage fs;
173 	fs.add_file("test_resume/tmp1", 128 * 1024 * 8);
174 	fs.add_file("test_resume/tmp2", 128 * 1024);
175 	fs.add_file("test_resume/tmp3", 128 * 1024);
176 	lt::create_torrent t(fs, 128 * 1024, 6);
177 
178 	t.add_tracker("http://torrent_file_tracker.com/announce");
179 	t.add_url_seed("http://torrent_file_url_seed.com/");
180 
181 	int num = t.num_pieces();
182 	TEST_CHECK(num > 0);
183 	for (auto const i : fs.piece_range())
184 	{
185 		sha1_hash ph;
186 		aux::random_bytes(ph);
187 		t.set_hash(i, ph);
188 	}
189 
190 	std::vector<char> buf;
191 	bencode(std::back_inserter(buf), t.generate());
192 	return std::make_shared<torrent_info>(buf, from_span);
193 }
194 } // anonymous namespace
195 
TORRENT_TEST(read_resume_torrent)196 TORRENT_TEST(read_resume_torrent)
197 {
198 	std::shared_ptr<torrent_info> ti = generate_torrent();
199 
200 	entry rd;
201 	rd["file-format"] = "libtorrent resume file";
202 	rd["file-version"] = 1;
203 	rd["info-hash"] = ti->info_hash().to_string();
204 	rd["info"] = bdecode({ti->metadata().get(), ti->metadata_size()});
205 
206 	std::vector<char> resume_data;
207 	bencode(std::back_inserter(resume_data), rd);
208 
209 	// the info-hash field does not match the torrent in the "info" field, so it
210 	// will be ignored
211 	add_torrent_params atp = read_resume_data(resume_data);
212 	TEST_CHECK(atp.ti);
213 
214 	TEST_EQUAL(atp.ti->info_hash(), ti->info_hash());
215 	TEST_EQUAL(atp.ti->name(), ti->name());
216 }
217 
218 namespace {
219 
test_roundtrip(add_torrent_params const & input)220 void test_roundtrip(add_torrent_params const& input)
221 {
222 	auto b = write_resume_data_buf(input);
223 	error_code ec;
224 	auto output = read_resume_data(b, ec);
225 	TEST_CHECK(write_resume_data_buf(output) == b);
226 }
227 
228 template <typename T>
bits()229 lt::typed_bitfield<T> bits()
230 {
231 	lt::typed_bitfield<T> b;
232 	b.resize(19);
233 	b.set_bit(T(2));
234 	b.set_bit(T(6));
235 	b.set_bit(T(12));
236 	return b;
237 }
238 
bits()239 lt::bitfield bits()
240 {
241 	lt::bitfield b;
242 	b.resize(19);
243 	b.set_bit(2);
244 	b.set_bit(6);
245 	b.set_bit(12);
246 	return b;
247 }
248 
249 template <typename T>
vec()250 std::vector<T> vec()
251 {
252 	std::vector<T> ret;
253 	ret.resize(10);
254 	ret[0] = T(1);
255 	ret[1] = T(2);
256 	ret[5] = T(3);
257 	ret[7] = T(4);
258 	return ret;
259 }
260 }
261 
TORRENT_TEST(round_trip_have_pieces)262 TORRENT_TEST(round_trip_have_pieces)
263 {
264 	add_torrent_params atp;
265 	atp.have_pieces = bits<piece_index_t>();
266 	test_roundtrip(atp);
267 }
268 
TORRENT_TEST(round_trip_verified_pieces)269 TORRENT_TEST(round_trip_verified_pieces)
270 {
271 	add_torrent_params atp;
272 	atp.verified_pieces = bits<piece_index_t>();
273 	test_roundtrip(atp);
274 }
275 
TORRENT_TEST(round_trip_prios)276 TORRENT_TEST(round_trip_prios)
277 {
278 	add_torrent_params atp;
279 	atp.piece_priorities = vec<download_priority_t>();
280 	test_roundtrip(atp);
281 }
282 
TORRENT_TEST(round_trip_unfinished)283 TORRENT_TEST(round_trip_unfinished)
284 {
285 	add_torrent_params atp;
286 	atp.unfinished_pieces = std::map<piece_index_t, bitfield>{{piece_index_t{42}, bits()}};
287 	test_roundtrip(atp);
288 }
289 
TORRENT_TEST(round_trip_trackers)290 TORRENT_TEST(round_trip_trackers)
291 {
292 	add_torrent_params atp;
293 	atp.flags |= torrent_flags::override_trackers;
294 	test_roundtrip(atp);
295 }
296 
TORRENT_TEST(round_trip_name)297 TORRENT_TEST(round_trip_name)
298 {
299 	add_torrent_params atp;
300 	atp.name = "foobar";
301 	test_roundtrip(atp);
302 }
303 
TORRENT_TEST(round_trip_flags)304 TORRENT_TEST(round_trip_flags)
305 {
306 	torrent_flags_t const flags[] = {
307 		torrent_flags::seed_mode,
308 		torrent_flags::upload_mode,
309 		torrent_flags::share_mode,
310 		torrent_flags::apply_ip_filter,
311 		torrent_flags::paused,
312 		torrent_flags::auto_managed,
313 		torrent_flags::duplicate_is_error,
314 		torrent_flags::update_subscribe,
315 		torrent_flags::super_seeding,
316 		torrent_flags::sequential_download,
317 		torrent_flags::stop_when_ready,
318 		torrent_flags::override_trackers,
319 		torrent_flags::override_web_seeds,
320 		torrent_flags::need_save_resume,
321 		torrent_flags::disable_dht,
322 		torrent_flags::disable_lsd,
323 		torrent_flags::disable_pex,
324 	};
325 
326 	for (auto const& f : flags)
327 	{
328 		add_torrent_params atp;
329 		atp.flags = f;
330 		test_roundtrip(atp);
331 	}
332 }
333