00001
00002
00003
00004
00005
00006
00007
00008 #ifdef _MSC_VER
00009 #pragma warning(push, 1)
00010 #endif
00011
00012 #include <boost/shared_ptr.hpp>
00013 #include <boost/lexical_cast.hpp>
00014 #include <boost/filesystem/fstream.hpp>
00015 #include <boost/filesystem/convenience.hpp>
00016 #include <boost/date_time/posix_time/posix_time.hpp>
00017
00018 #ifdef _MSC_VER
00019 #pragma warning(pop)
00020 #endif
00021
00022 #include <vector>
00023 #include <utility>
00024 #include <numeric>
00025
00026 #include "libtorrent/peer_connection.hpp"
00027 #include "libtorrent/bt_peer_connection.hpp"
00028 #include "libtorrent/hasher.hpp"
00029 #include "libtorrent/bencode.hpp"
00030 #include "libtorrent/torrent.hpp"
00031 #include "libtorrent/extensions.hpp"
00032 #include "libtorrent/extensions/metadata_transfer.hpp"
00033
00034 using boost::posix_time::second_clock;
00035
00036 namespace libtorrent { namespace
00037 {
00038 int div_round_up(int numerator, int denominator)
00039 {
00040 return (numerator + denominator - 1) / denominator;
00041 }
00042
00043 std::pair<int, int> req_to_offset(std::pair<int, int> req, int total_size)
00044 {
00045 assert(req.first >= 0);
00046 assert(req.second > 0);
00047 assert(req.second <= 256);
00048 assert(req.first + req.second <= 256);
00049
00050 int start = div_round_up(req.first * total_size, 256);
00051 int size = div_round_up((req.first + req.second) * total_size, 256) - start;
00052 return std::make_pair(start, size);
00053 }
00054
00055 std::pair<int, int> offset_to_req(std::pair<int, int> offset, int total_size)
00056 {
00057 int start = offset.first * 256 / total_size;
00058 int size = (offset.first + offset.second) * 256 / total_size - start;
00059
00060 std::pair<int, int> ret(start, size);
00061
00062 assert(start >= 0);
00063 assert(size > 0);
00064 assert(start <= 256);
00065 assert(start + size <= 256);
00066
00067
00068 #ifndef NDEBUG
00069 std::pair<int, int> identity = req_to_offset(ret, total_size);
00070 assert(offset == identity);
00071 #endif
00072 return ret;
00073 }
00074
00075 struct metadata_plugin : torrent_plugin
00076 {
00077 metadata_plugin(torrent& t)
00078 : m_torrent(t)
00079 , m_metadata_progress(0)
00080 , m_metadata_size(0)
00081 {
00082 m_requested_metadata.resize(256, 0);
00083 }
00084
00085 virtual boost::shared_ptr<peer_plugin> new_connection(
00086 peer_connection* pc);
00087
00088 std::vector<char> const& metadata() const
00089 {
00090 if (m_metadata.empty())
00091 {
00092 bencode(std::back_inserter(m_metadata)
00093 , m_torrent.torrent_file().create_info_metadata());
00094
00095 assert(hasher(&m_metadata[0], m_metadata.size()).final()
00096 == m_torrent.torrent_file().info_hash());
00097 }
00098 assert(!m_metadata.empty());
00099 return m_metadata;
00100 }
00101
00102 bool received_metadata(char const* buf, int size, int offset, int total_size)
00103 {
00104 if (m_torrent.valid_metadata()) return false;
00105
00106 if ((int)m_metadata.size() < total_size)
00107 m_metadata.resize(total_size);
00108
00109 std::copy(
00110 buf
00111 , buf + size
00112 , &m_metadata[offset]);
00113
00114 if (m_have_metadata.empty())
00115 m_have_metadata.resize(256, false);
00116
00117 std::pair<int, int> req = offset_to_req(std::make_pair(offset, size)
00118 , total_size);
00119
00120 assert(req.first + req.second <= (int)m_have_metadata.size());
00121
00122 std::fill(
00123 m_have_metadata.begin() + req.first
00124 , m_have_metadata.begin() + req.first + req.second
00125 , true);
00126
00127 bool have_all = std::count(
00128 m_have_metadata.begin()
00129 , m_have_metadata.end()
00130 , true) == 256;
00131
00132 if (!have_all) return false;
00133
00134 hasher h;
00135 h.update(&m_metadata[0], (int)m_metadata.size());
00136 sha1_hash info_hash = h.final();
00137
00138 if (info_hash != m_torrent.torrent_file().info_hash())
00139 {
00140 std::fill(
00141 m_have_metadata.begin()
00142 , m_have_metadata.begin() + req.first + req.second
00143 , false);
00144 m_metadata_progress = 0;
00145 m_metadata_size = 0;
00146
00147
00148 return false;
00149 }
00150
00151 entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
00152 m_torrent.set_metadata(metadata);
00153
00154
00155 std::vector<bool>().swap(m_have_metadata);
00156 std::vector<int>().swap(m_requested_metadata);
00157
00158 return true;
00159 }
00160
00161
00162
00163 std::pair<int, int> metadata_request();
00164
00165 void cancel_metadata_request(std::pair<int, int> req)
00166 {
00167 for (int i = req.first; i < req.first + req.second; ++i)
00168 {
00169 assert(m_requested_metadata[i] > 0);
00170 if (m_requested_metadata[i] > 0)
00171 --m_requested_metadata[i];
00172 }
00173 }
00174
00175
00176
00177 void metadata_progress(int total_size, int received)
00178 {
00179 m_metadata_progress += received;
00180 m_metadata_size = total_size;
00181 }
00182
00183 private:
00184 torrent& m_torrent;
00185
00186
00187
00188
00189
00190 mutable std::vector<char> m_metadata;
00191
00192 int m_metadata_progress;
00193 int m_metadata_size;
00194
00195
00196
00197
00198
00199
00200 std::vector<bool> m_have_metadata;
00201
00202
00203 std::vector<int> m_requested_metadata;
00204 };
00205
00206 struct metadata_peer_plugin : peer_plugin
00207 {
00208 metadata_peer_plugin(torrent& t, peer_connection& pc
00209 , metadata_plugin& tp)
00210 : m_waiting_metadata_request(false)
00211 , m_message_index(0)
00212 , m_metadata_progress(0)
00213 , m_no_metadata(
00214 boost::gregorian::date(1970, boost::date_time::Jan, 1)
00215 , boost::posix_time::seconds(0))
00216 , m_metadata_request(
00217 boost::gregorian::date(1970, boost::date_time::Jan, 1)
00218 , boost::posix_time::seconds(0))
00219 , m_torrent(t)
00220 , m_pc(pc)
00221 , m_tp(tp)
00222 {}
00223
00224
00225 virtual void add_handshake(entry& h)
00226 {
00227 entry& messages = h["m"];
00228 messages["LT_metadata"] = 14;
00229 }
00230
00231
00232 virtual bool on_extension_handshake(entry const& h)
00233 {
00234 entry const& messages = h["m"];
00235 if (entry const* index = messages.find_key("LT_metadata"))
00236 {
00237 m_message_index = index->integer();
00238 return true;
00239 }
00240 else
00241 {
00242 m_message_index = 0;
00243 return false;
00244 }
00245 }
00246
00247 void write_metadata_request(std::pair<int, int> req)
00248 {
00249 assert(req.first >= 0);
00250 assert(req.second > 0);
00251 assert(req.first + req.second <= 256);
00252 assert(!m_pc.associated_torrent().expired());
00253 assert(!m_pc.associated_torrent().lock()->valid_metadata());
00254
00255 int start = req.first;
00256 int size = req.second;
00257
00258
00259 if (m_message_index == 0) return;
00260
00261 buffer::interval i = m_pc.allocate_send_buffer(9);
00262
00263 detail::write_uint32(1 + 1 + 3, i.begin);
00264 detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
00265 detail::write_uint8(m_message_index, i.begin);
00266
00267 detail::write_uint8(0, i.begin);
00268 detail::write_uint8(start, i.begin);
00269 detail::write_uint8(size - 1, i.begin);
00270 assert(i.begin == i.end);
00271 m_pc.setup_send();
00272 }
00273
00274 void write_metadata(std::pair<int, int> req)
00275 {
00276 assert(req.first >= 0);
00277 assert(req.second > 0);
00278 assert(req.second <= 256);
00279 assert(req.first + req.second <= 256);
00280 assert(!m_pc.associated_torrent().expired());
00281
00282
00283 if (m_message_index == 0) return;
00284
00285
00286 if (m_torrent.valid_metadata() && !m_torrent.torrent_file().priv())
00287 {
00288 std::pair<int, int> offset
00289 = req_to_offset(req, (int)m_tp.metadata().size());
00290
00291 buffer::interval i = m_pc.allocate_send_buffer(15 + offset.second);
00292
00293
00294 detail::write_uint32(11 + offset.second, i.begin);
00295 detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
00296 detail::write_uint8(m_message_index, i.begin);
00297
00298 detail::write_uint8(1, i.begin);
00299 detail::write_uint32((int)m_tp.metadata().size(), i.begin);
00300 detail::write_uint32(offset.first, i.begin);
00301 std::vector<char> const& metadata = m_tp.metadata();
00302 std::copy(metadata.begin() + offset.first
00303 , metadata.begin() + offset.first + offset.second, i.begin);
00304 i.begin += offset.second;
00305 assert(i.begin == i.end);
00306 }
00307 else
00308 {
00309 buffer::interval i = m_pc.allocate_send_buffer(4 + 3);
00310
00311
00312 detail::write_uint32(1 + 2, i.begin);
00313 detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
00314 detail::write_uint8(m_message_index, i.begin);
00315
00316 detail::write_uint8(2, i.begin);
00317 assert(i.begin == i.end);
00318 }
00319 m_pc.setup_send();
00320 }
00321
00322 virtual bool on_extended(int length
00323 , int msg, buffer::const_interval body)
00324 {
00325 if (msg != 14) return false;
00326 if (m_message_index == 0) return false;
00327
00328 if (length > 500 * 1024)
00329 throw protocol_error("LT_metadata message larger than 500 kB");
00330
00331 if (body.left() < 1) return true;
00332 int type = detail::read_uint8(body.begin);
00333
00334 switch (type)
00335 {
00336 case 0:
00337 {
00338 if (body.left() < 2) return true;
00339 int start = detail::read_uint8(body.begin);
00340 int size = detail::read_uint8(body.begin) + 1;
00341
00342 if (length != 3)
00343 {
00344
00345 throw protocol_error("invalid metadata request");
00346 }
00347
00348 write_metadata(std::make_pair(start, size));
00349 }
00350 break;
00351 case 1:
00352 {
00353 if (body.left() < 8) return true;
00354
00355 int total_size = detail::read_int32(body.begin);
00356 int offset = detail::read_int32(body.begin);
00357 int data_size = length - 9;
00358
00359 if (total_size > 500 * 1024)
00360 throw protocol_error("metadata size larger than 500 kB");
00361 if (total_size <= 0)
00362 throw protocol_error("invalid metadata size");
00363 if (offset > total_size || offset < 0)
00364 throw protocol_error("invalid metadata offset");
00365 if (offset + data_size > total_size)
00366 throw protocol_error("invalid metadata message");
00367
00368 m_tp.metadata_progress(total_size
00369 , body.left() - m_metadata_progress);
00370 m_metadata_progress = body.left();
00371
00372 if (body.left() < data_size) return true;
00373
00374 m_waiting_metadata_request = false;
00375 m_tp.received_metadata(body.begin, data_size
00376 , offset, total_size);
00377 m_metadata_progress = 0;
00378 }
00379 break;
00380 case 2:
00381 m_no_metadata = second_clock::universal_time();
00382 if (m_waiting_metadata_request)
00383 m_tp.cancel_metadata_request(m_last_metadata_request);
00384 m_waiting_metadata_request = false;
00385 break;
00386 default:
00387 throw protocol_error("unknown metadata extension message: "
00388 + boost::lexical_cast<std::string>(type));
00389 }
00390 return true;
00391 }
00392
00393 virtual void tick()
00394 {
00395
00396
00397
00398
00399 if (!m_torrent.valid_metadata()
00400 && m_message_index != 0
00401 && !m_waiting_metadata_request
00402 && has_metadata())
00403 {
00404 m_last_metadata_request = m_tp.metadata_request();
00405 write_metadata_request(m_last_metadata_request);
00406 m_waiting_metadata_request = true;
00407 m_metadata_request = second_clock::universal_time();
00408 }
00409 }
00410
00411 bool has_metadata() const
00412 {
00413 using namespace boost::posix_time;
00414 return second_clock::universal_time() - m_no_metadata > minutes(5);
00415 }
00416
00417 private:
00418
00419
00420
00421
00422 bool m_waiting_metadata_request;
00423
00424
00425
00426 int m_message_index;
00427
00428
00429
00430
00431
00432
00433 int m_metadata_progress;
00434
00435
00436
00437 boost::posix_time::ptime m_no_metadata;
00438
00439
00440
00441 boost::posix_time::ptime m_metadata_request;
00442
00443
00444
00445 std::pair<int, int> m_last_metadata_request;
00446
00447 torrent& m_torrent;
00448 peer_connection& m_pc;
00449 metadata_plugin& m_tp;
00450 };
00451
00452 boost::shared_ptr<peer_plugin> metadata_plugin::new_connection(
00453 peer_connection* pc)
00454 {
00455 return boost::shared_ptr<peer_plugin>(new metadata_peer_plugin(m_torrent, *pc, *this));
00456 }
00457
00458 std::pair<int, int> metadata_plugin::metadata_request()
00459 {
00460
00461
00462 int peers = 0;
00463 #ifndef TORRENT_DISABLE_EXTENSIONS
00464 typedef std::map<tcp::endpoint, peer_connection*> conn_map;
00465 for (conn_map::iterator i = m_torrent.begin()
00466 , end(m_torrent.end()); i != end; ++i)
00467 {
00468 bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(i->second);
00469 if (c == 0) continue;
00470 metadata_peer_plugin* p
00471 = c->supports_extension<metadata_peer_plugin>();
00472 if (p == 0) continue;
00473 if (!p->has_metadata()) continue;
00474 ++peers;
00475 }
00476 #endif
00477
00478
00479 int num_blocks = 256 / (peers + 1);
00480 if (num_blocks < 1) num_blocks = 1;
00481 assert(num_blocks <= 128);
00482
00483 int min_element = std::numeric_limits<int>::max();
00484 int best_index = 0;
00485 for (int i = 0; i < 256 - num_blocks + 1; ++i)
00486 {
00487 int min = *std::min_element(m_requested_metadata.begin() + i
00488 , m_requested_metadata.begin() + i + num_blocks);
00489 min += std::accumulate(m_requested_metadata.begin() + i
00490 , m_requested_metadata.begin() + i + num_blocks, (int)0);
00491
00492 if (min_element > min)
00493 {
00494 best_index = i;
00495 min_element = min;
00496 }
00497 }
00498
00499 std::pair<int, int> ret(best_index, num_blocks);
00500 for (int i = ret.first; i < ret.first + ret.second; ++i)
00501 m_requested_metadata[i]++;
00502
00503 assert(ret.first >= 0);
00504 assert(ret.second > 0);
00505 assert(ret.second <= 256);
00506 assert(ret.first + ret.second <= 256);
00507
00508 return ret;
00509 }
00510
00511 } }
00512
00513 namespace libtorrent
00514 {
00515
00516 boost::shared_ptr<torrent_plugin> create_metadata_plugin(torrent* t)
00517 {
00518 return boost::shared_ptr<torrent_plugin>(new metadata_plugin(*t));
00519 }
00520
00521 }
00522