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