2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2003, 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 <ctime>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <iterator>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
|
|
|
#include <cctype>
|
2003-10-30 00:28:09 +01:00
|
|
|
#include <numeric>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
#include <boost/filesystem/convenience.hpp>
|
|
|
|
|
2003-10-26 18:35:23 +01:00
|
|
|
#include "libtorrent/torrent_handle.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/session.hpp"
|
|
|
|
#include "libtorrent/torrent_info.hpp"
|
|
|
|
#include "libtorrent/url_handler.hpp"
|
|
|
|
#include "libtorrent/bencode.hpp"
|
|
|
|
#include "libtorrent/hasher.hpp"
|
|
|
|
#include "libtorrent/entry.hpp"
|
|
|
|
#include "libtorrent/peer.hpp"
|
|
|
|
#include "libtorrent/peer_id.hpp"
|
2003-12-22 08:14:35 +01:00
|
|
|
#include "libtorrent/alert.hpp"
|
|
|
|
#include "libtorrent/identify_client.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
|
|
namespace std
|
|
|
|
{
|
|
|
|
using ::isalnum;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace libtorrent;
|
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
// wait 60 seconds before retrying a failed tracker
|
|
|
|
tracker_retry_delay = 60
|
|
|
|
};
|
|
|
|
|
|
|
|
int calculate_block_size(const torrent_info& i)
|
|
|
|
{
|
2003-12-21 18:28:27 +01:00
|
|
|
const int default_block_size = 16 * 1024;
|
|
|
|
|
|
|
|
// if pieces are too small, adjust the block size
|
|
|
|
if (i.piece_length() < default_block_size)
|
|
|
|
{
|
|
|
|
return i.piece_length();
|
|
|
|
}
|
|
|
|
|
|
|
|
// if pieces are too large, adjust the block size
|
|
|
|
if (i.piece_length() / default_block_size > 128)
|
|
|
|
{
|
|
|
|
return i.piece_length() / 128;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, go with the default
|
|
|
|
return default_block_size;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
peer extract_peer_info(const entry& e)
|
|
|
|
{
|
|
|
|
peer ret;
|
|
|
|
|
|
|
|
const entry::dictionary_type& info = e.dict();
|
|
|
|
|
2004-01-12 04:05:10 +01:00
|
|
|
// extract peer id (if any)
|
2003-10-23 01:00:57 +02:00
|
|
|
entry::dictionary_type::const_iterator i = info.find("peer id");
|
2004-01-12 04:05:10 +01:00
|
|
|
if (i != info.end())
|
|
|
|
{
|
|
|
|
if (i->second.string().length() != 20) throw std::runtime_error("invalid response from tracker");
|
|
|
|
std::copy(i->second.string().begin(), i->second.string().end(), ret.id.begin());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if there's no peer_id, just initialize it to a bunch of zeroes
|
|
|
|
std::fill_n(ret.id.begin(), 20, 0);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
// extract ip
|
|
|
|
i = info.find("ip");
|
|
|
|
if (i == info.end()) throw std::runtime_error("invalid response from tracker");
|
|
|
|
ret.ip = i->second.string();
|
|
|
|
|
|
|
|
// extract port
|
|
|
|
i = info.find("port");
|
|
|
|
if (i == info.end()) throw std::runtime_error("invalid response from tracker");
|
|
|
|
ret.port = i->second.integer();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string escape_string(const char* str, int len)
|
|
|
|
{
|
2004-01-02 00:23:17 +01:00
|
|
|
static const char special_chars[] = "$-_.+!*'(),";
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
std::stringstream ret;
|
|
|
|
ret << std::hex << std::setfill('0');
|
|
|
|
for (int i = 0; i < len; ++i)
|
|
|
|
{
|
2004-01-02 00:23:17 +01:00
|
|
|
if (std::isalnum(static_cast<unsigned char>(*str))
|
2004-01-02 21:46:24 +01:00
|
|
|
|| std::count(
|
2004-01-02 00:23:17 +01:00
|
|
|
special_chars
|
|
|
|
, special_chars+sizeof(special_chars)-1
|
|
|
|
, *str))
|
|
|
|
{
|
|
|
|
ret << *str;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret << "%"
|
|
|
|
<< std::setw(2)
|
|
|
|
<< (int)static_cast<unsigned char>(*str);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
++str;
|
|
|
|
}
|
|
|
|
return ret.str();
|
|
|
|
}
|
|
|
|
|
2004-01-12 04:05:10 +01:00
|
|
|
struct find_peer_by_id
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-12 04:05:10 +01:00
|
|
|
find_peer_by_id(const peer_id& i, const torrent* t): id(i), tor(t) {}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
bool operator()(const detail::session_impl::connection_map::value_type& c) const
|
|
|
|
{
|
|
|
|
if (c.second->get_peer_id() != id) return false;
|
|
|
|
if (tor != c.second->associated_torrent()) return false;
|
2004-01-12 04:05:10 +01:00
|
|
|
// have a special case for all zeros. We can have any number
|
|
|
|
// of peers with that id, since it's used to indicate no id.
|
|
|
|
if (std::count(id.begin(), id.end(), 0) == 20) return false;
|
2003-10-23 01:00:57 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
bool operator()(const peer_connection* p) const
|
|
|
|
{
|
|
|
|
if (p->get_peer_id() != id) return false;
|
|
|
|
if (tor != p->associated_torrent()) return false;
|
2004-01-12 04:05:10 +01:00
|
|
|
// have a special case for all zeros. We can have any number
|
|
|
|
// of peers with that id, since it's used to indicate no id.
|
|
|
|
if (std::count(id.begin(), id.end(), 0) == 20) return false;
|
2003-12-01 06:01:40 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
const peer_id& id;
|
|
|
|
const torrent* tor;
|
|
|
|
};
|
2004-01-12 04:05:10 +01:00
|
|
|
|
|
|
|
struct find_peer_by_ip
|
|
|
|
{
|
|
|
|
find_peer_by_ip(const address& a, const torrent* t): ip(a), tor(t) {}
|
|
|
|
|
|
|
|
bool operator()(const detail::session_impl::connection_map::value_type& c) const
|
|
|
|
{
|
|
|
|
if (c.first->sender() != ip) return false;
|
|
|
|
if (tor != c.second->associated_torrent()) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const address& ip;
|
|
|
|
const torrent* tor;
|
|
|
|
};
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
torrent::torrent(
|
|
|
|
detail::session_impl& ses
|
|
|
|
, const torrent_info& torrent_file
|
|
|
|
, const boost::filesystem::path& save_path)
|
2003-10-23 01:00:57 +02:00
|
|
|
: m_block_size(calculate_block_size(torrent_file))
|
|
|
|
, m_abort(false)
|
|
|
|
, m_event(event_started)
|
|
|
|
, m_torrent_file(torrent_file)
|
2003-12-07 06:53:04 +01:00
|
|
|
, m_storage(m_torrent_file, save_path)
|
2003-10-23 01:00:57 +02:00
|
|
|
, m_next_request(boost::posix_time::second_clock::local_time())
|
|
|
|
, m_duration(1800)
|
|
|
|
, m_policy(new policy(this))
|
|
|
|
, m_ses(ses)
|
|
|
|
, m_picker(torrent_file.piece_length() / m_block_size,
|
|
|
|
(torrent_file.total_size()+m_block_size-1)/m_block_size)
|
|
|
|
, m_last_working_tracker(0)
|
|
|
|
, m_currently_trying_tracker(0)
|
2003-12-01 06:01:40 +01:00
|
|
|
, m_time_scaler(0)
|
2003-12-07 06:53:04 +01:00
|
|
|
, m_priority(.5)
|
|
|
|
, m_num_pieces(0)
|
2003-12-22 08:14:35 +01:00
|
|
|
, m_got_tracker_response(false)
|
2004-01-12 04:05:10 +01:00
|
|
|
, m_ratio(0.f)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-12-07 06:53:04 +01:00
|
|
|
assert(torrent_file.begin_files() != torrent_file.end_files());
|
|
|
|
m_have_pieces.resize(torrent_file.num_pieces(), false);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
torrent::~torrent()
|
|
|
|
{
|
|
|
|
if (m_ses.m_abort) m_abort = true;
|
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void torrent::tracker_response(const entry& e)
|
|
|
|
{
|
2003-10-23 18:55:52 +02:00
|
|
|
std::vector<peer> peer_list;
|
2003-10-23 01:00:57 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// parse the response
|
2003-10-23 18:55:52 +02:00
|
|
|
parse_response(e, peer_list);
|
|
|
|
|
|
|
|
m_last_working_tracker
|
|
|
|
= m_torrent_file.prioritize_tracker(m_currently_trying_tracker);
|
|
|
|
m_next_request = boost::posix_time::second_clock::local_time()
|
|
|
|
+ boost::posix_time::seconds(m_duration);
|
|
|
|
m_currently_trying_tracker = 0;
|
|
|
|
|
|
|
|
// connect to random peers from the list
|
|
|
|
std::random_shuffle(peer_list.begin(), peer_list.end());
|
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
std::stringstream s;
|
|
|
|
s << "interval: " << m_duration << "\n";
|
|
|
|
s << "peers:\n";
|
2003-10-23 18:55:52 +02:00
|
|
|
for (std::vector<peer>::const_iterator i = peer_list.begin();
|
|
|
|
i != peer_list.end();
|
|
|
|
++i)
|
|
|
|
{
|
2004-01-12 21:31:27 +01:00
|
|
|
s << " " << std::setfill(' ') << std::setw(16) << i->ip
|
2003-11-05 00:27:06 +01:00
|
|
|
<< " " << std::setw(5) << std::dec << i->port << " "
|
2003-12-21 18:28:27 +01:00
|
|
|
<< i->id << " " << identify_client(i->id) << "\n";
|
2003-10-23 18:55:52 +02:00
|
|
|
}
|
2004-01-12 21:31:27 +01:00
|
|
|
debug_log(s.str());
|
|
|
|
#endif
|
2003-10-23 18:55:52 +02:00
|
|
|
// for each of the peers we got from the tracker
|
|
|
|
for (std::vector<peer>::iterator i = peer_list.begin();
|
|
|
|
i != peer_list.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
// don't make connections to ourself
|
2003-12-07 06:53:04 +01:00
|
|
|
if (i->id == m_ses.get_peer_id())
|
2003-10-23 18:55:52 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
address a(i->ip, i->port);
|
|
|
|
|
|
|
|
// if we aleady have a connection to the person, don't make another one
|
2004-01-12 04:05:10 +01:00
|
|
|
if (std::find_if(
|
|
|
|
m_ses.m_connections.begin()
|
|
|
|
, m_ses.m_connections.end()
|
|
|
|
, find_peer_by_id(i->id, this)) != m_ses.m_connections.end())
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::find_if(
|
|
|
|
m_ses.m_connections.begin()
|
|
|
|
, m_ses.m_connections.end()
|
|
|
|
, find_peer_by_ip(a, this)) != m_ses.m_connections.end())
|
2003-10-23 18:55:52 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_policy->peer_from_tracker(a, i->id);
|
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
catch(type_error& e)
|
|
|
|
{
|
|
|
|
tracker_request_error(e.what());
|
|
|
|
}
|
|
|
|
catch(std::runtime_error& e)
|
|
|
|
{
|
|
|
|
tracker_request_error(e.what());
|
|
|
|
}
|
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
m_got_tracker_response = true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
bool torrent::has_peer(const peer_id& id) const
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-12-01 06:01:40 +01:00
|
|
|
assert(std::count_if(m_connections.begin()
|
|
|
|
, m_connections.end()
|
2004-01-12 04:05:10 +01:00
|
|
|
, find_peer_by_id(id, this)) <= 1);
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
// pretend that we are connected to
|
|
|
|
// ourself to avoid real connections
|
|
|
|
// to ourself
|
|
|
|
if (id == m_ses.m_peer_id) return true;
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
return std::find_if(
|
|
|
|
m_connections.begin()
|
|
|
|
, m_connections.end()
|
2004-01-12 04:05:10 +01:00
|
|
|
, find_peer_by_id(id, this))
|
2003-12-01 06:01:40 +01:00
|
|
|
!= m_connections.end();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
torrent::size_type torrent::bytes_left() const
|
|
|
|
{
|
|
|
|
size_type have_bytes = m_num_pieces * m_torrent_file.piece_length();
|
|
|
|
int last_piece = m_torrent_file.num_pieces()-1;
|
|
|
|
if (m_have_pieces[last_piece])
|
|
|
|
{
|
|
|
|
have_bytes -= m_torrent_file.piece_length()
|
|
|
|
- m_torrent_file.piece_size(last_piece);
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_torrent_file.total_size()
|
|
|
|
- have_bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-12-01 22:27:27 +01:00
|
|
|
void torrent::piece_failed(int index)
|
|
|
|
{
|
2003-12-22 08:14:35 +01:00
|
|
|
if (m_ses.m_alerts.should_post(alert::info))
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "hash for piece " << index << " failed";
|
2004-01-07 01:48:02 +01:00
|
|
|
m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str()));
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
2004-01-12 21:31:27 +01:00
|
|
|
std::vector<address> downloaders;
|
2003-12-01 22:27:27 +01:00
|
|
|
m_picker.get_downloaders(downloaders, index);
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:45:57 +01:00
|
|
|
// std::cout << "hash-test failed. Some of these peers sent invalid data:\n";
|
|
|
|
// std::copy(downloaders.begin(), downloaders.end(), std::ostream_iterator<peer_id>(std::cout, "\n"));
|
2003-12-01 22:27:27 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// decrease the trust point of all peers that sent
|
|
|
|
// parts of this piece.
|
|
|
|
// TODO: implement this loop more efficient
|
|
|
|
for (std::vector<peer_connection*>::iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end();
|
|
|
|
++i)
|
|
|
|
{
|
2004-01-12 21:31:27 +01:00
|
|
|
if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_socket()->sender())
|
2003-12-14 06:56:12 +01:00
|
|
|
== downloaders.end()) continue;
|
|
|
|
|
|
|
|
(*i)->received_invalid_data();
|
|
|
|
if ((*i)->trust_points() <= -5)
|
2003-12-01 22:27:27 +01:00
|
|
|
{
|
2003-12-14 06:56:12 +01:00
|
|
|
// we don't trust this peer anymore
|
|
|
|
// ban it.
|
|
|
|
m_policy->ban_peer(*(*i));
|
2003-12-01 22:27:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// we have to let the piece_picker know that
|
|
|
|
// this piece failed the check as it can restore it
|
|
|
|
// and mark it as being interesting for download
|
|
|
|
// TODO: do this more intelligently! and keep track
|
|
|
|
// of how much crap (data that failed hash-check) and
|
|
|
|
// how much redundant data we have downloaded
|
|
|
|
// if some clients has sent more than one piece
|
|
|
|
// start with redownloading the pieces that the client
|
|
|
|
// that has sent the least number of pieces
|
|
|
|
m_picker.restore_piece(index);
|
2004-01-12 04:05:10 +01:00
|
|
|
m_storage.mark_failed(index);
|
2004-01-09 11:50:22 +01:00
|
|
|
|
|
|
|
// TODO: make sure restore_piece() works
|
|
|
|
assert(m_have_pieces[index] == false);
|
2003-12-01 22:27:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void torrent::announce_piece(int index)
|
|
|
|
{
|
2004-01-12 21:31:27 +01:00
|
|
|
std::vector<address> downloaders;
|
2003-12-01 22:27:27 +01:00
|
|
|
m_picker.get_downloaders(downloaders, index);
|
|
|
|
|
|
|
|
// increase the trust point of all peers that sent
|
|
|
|
// parts of this piece.
|
|
|
|
// TODO: implement this loop more efficient
|
|
|
|
for (std::vector<peer_connection*>::iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end();
|
|
|
|
++i)
|
|
|
|
{
|
2004-01-12 21:31:27 +01:00
|
|
|
if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_socket()->sender())
|
2003-12-01 22:27:27 +01:00
|
|
|
!= downloaders.end())
|
|
|
|
{
|
|
|
|
(*i)->received_valid_data();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
m_picker.we_have(index);
|
|
|
|
for (std::vector<peer_connection*>::iterator i = m_connections.begin(); i != m_connections.end(); ++i)
|
|
|
|
(*i)->announce_piece(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string torrent::generate_tracker_request(int port)
|
|
|
|
{
|
|
|
|
m_duration = 1800;
|
|
|
|
m_next_request = boost::posix_time::second_clock::local_time() + boost::posix_time::seconds(m_duration);
|
|
|
|
|
|
|
|
std::vector<char> buffer;
|
|
|
|
std::string request = m_torrent_file.trackers()[m_currently_trying_tracker].url;
|
|
|
|
|
|
|
|
request += "?info_hash=";
|
|
|
|
request += escape_string(reinterpret_cast<const char*>(m_torrent_file.info_hash().begin()), 20);
|
|
|
|
|
|
|
|
request += "&peer_id=";
|
2003-12-07 06:53:04 +01:00
|
|
|
request += escape_string(reinterpret_cast<const char*>(m_ses.get_peer_id().begin()), 20);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
request += "&port=";
|
|
|
|
request += boost::lexical_cast<std::string>(port);
|
|
|
|
|
|
|
|
request += "&uploaded=";
|
2003-12-22 08:14:35 +01:00
|
|
|
request += boost::lexical_cast<std::string>(m_stat.total_payload_upload());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
request += "&downloaded=";
|
2003-12-22 08:14:35 +01:00
|
|
|
request += boost::lexical_cast<std::string>(m_stat.total_payload_download());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
request += "&left=";
|
2003-12-07 06:53:04 +01:00
|
|
|
request += boost::lexical_cast<std::string>(bytes_left());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
if (m_event != event_none)
|
|
|
|
{
|
|
|
|
const char* event_string[] = {"started", "stopped", "completed"};
|
|
|
|
request += "&event=";
|
|
|
|
request += event_string[m_event];
|
|
|
|
m_event = event_none;
|
|
|
|
}
|
|
|
|
|
2004-01-12 04:05:10 +01:00
|
|
|
// extension that tells the tracker that
|
|
|
|
// we don't need any peer_id's in the response
|
|
|
|
request += "&no_peer_id=1";
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
2003-10-23 18:55:52 +02:00
|
|
|
void torrent::parse_response(const entry& e, std::vector<peer>& peer_list)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
entry::dictionary_type::const_iterator i = e.dict().find("failure reason");
|
|
|
|
if (i != e.dict().end())
|
|
|
|
{
|
|
|
|
throw std::runtime_error(i->second.string().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
const entry::dictionary_type& msg = e.dict();
|
|
|
|
i = msg.find("interval");
|
2003-12-08 02:37:30 +01:00
|
|
|
if (i == msg.end()) throw std::runtime_error("invalid response from tracker (no interval)");
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
m_duration = i->second.integer();
|
|
|
|
|
|
|
|
i = msg.find("peers");
|
2003-12-08 02:37:30 +01:00
|
|
|
if (i == msg.end()) throw std::runtime_error("invalid response from tracker (no peers)");
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-23 18:55:52 +02:00
|
|
|
peer_list.clear();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-23 18:55:52 +02:00
|
|
|
const entry::list_type& l = i->second.list();
|
|
|
|
for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
peer p = extract_peer_info(*i);
|
2003-10-23 18:55:52 +02:00
|
|
|
peer_list.push_back(p);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::remove_peer(peer_connection* p)
|
|
|
|
{
|
|
|
|
std::vector<peer_connection*>::iterator i = std::find(m_connections.begin(), m_connections.end(), p);
|
|
|
|
assert(i != m_connections.end());
|
|
|
|
|
|
|
|
// if the peer_connection was downloading any pieces
|
|
|
|
// abort them
|
2003-12-08 22:59:48 +01:00
|
|
|
for (std::deque<piece_block>::const_iterator i = p->download_queue().begin();
|
2003-10-23 01:00:57 +02:00
|
|
|
i != p->download_queue().end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
m_picker.abort_download(*i);
|
|
|
|
}
|
|
|
|
|
2003-12-18 04:30:41 +01:00
|
|
|
std::vector<int> piece_list;
|
|
|
|
const std::vector<bool>& pieces = p->get_bitfield();
|
|
|
|
|
|
|
|
for (std::vector<bool>::const_iterator i = pieces.begin();
|
|
|
|
i != pieces.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
if (*i) piece_list.push_back(i - pieces.begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::random_shuffle(piece_list.begin(), piece_list.end());
|
|
|
|
|
|
|
|
for (std::vector<int>::iterator i = piece_list.begin();
|
|
|
|
i != piece_list.end();
|
|
|
|
++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-12-18 04:30:41 +01:00
|
|
|
peer_lost(*i);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
// std::cout << p->get_socket()->sender().as_string() << " *** DISCONNECT\n";
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
m_policy->connection_closed(*p);
|
|
|
|
m_connections.erase(i);
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2003-12-22 08:14:35 +01:00
|
|
|
// m_picker.integrity_check(this);
|
2003-10-23 01:00:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
peer_connection& torrent::connect_to_peer(const address& a, const peer_id& id)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
boost::shared_ptr<socket> s(new socket(socket::tcp, false));
|
|
|
|
s->connect(a);
|
2003-11-05 00:27:06 +01:00
|
|
|
boost::shared_ptr<peer_connection> c(new peer_connection(
|
|
|
|
m_ses
|
2003-12-07 06:53:04 +01:00
|
|
|
, m_ses.m_selector
|
2003-11-05 00:27:06 +01:00
|
|
|
, this
|
|
|
|
, s
|
|
|
|
, id));
|
2003-12-07 06:53:04 +01:00
|
|
|
if (m_ses.m_upload_rate != -1) c->set_send_quota(0);
|
2003-10-23 01:00:57 +02:00
|
|
|
detail::session_impl::connection_map::iterator p =
|
2003-12-07 06:53:04 +01:00
|
|
|
m_ses.m_connections.insert(std::make_pair(s, c)).first;
|
2003-12-01 06:01:40 +01:00
|
|
|
|
|
|
|
// add the newly connected peer to this torrent's peer list
|
2004-01-12 04:05:10 +01:00
|
|
|
assert(std::find(
|
|
|
|
m_connections.begin()
|
2003-12-01 06:01:40 +01:00
|
|
|
, m_connections.end()
|
|
|
|
, boost::get_pointer(p->second))
|
|
|
|
== m_connections.end());
|
|
|
|
|
|
|
|
m_connections.push_back(boost::get_pointer(p->second));
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
m_ses.m_selector.monitor_readability(s);
|
|
|
|
m_ses.m_selector.monitor_errors(s);
|
2003-11-05 00:27:06 +01:00
|
|
|
// std::cout << "connecting to: " << a.as_string() << ":" << a.port() << "\n";
|
2003-12-14 06:56:12 +01:00
|
|
|
return *c;
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::attach_peer(peer_connection* p)
|
|
|
|
{
|
|
|
|
assert(std::find(m_connections.begin(), m_connections.end(), p) == m_connections.end());
|
|
|
|
m_connections.push_back(p);
|
|
|
|
detail::session_impl::connection_map::iterator i
|
2003-12-07 06:53:04 +01:00
|
|
|
= m_ses.m_connections.find(p->get_socket());
|
|
|
|
assert(i != m_ses.m_connections.end());
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
m_policy->new_connection(*i->second);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::close_all_connections()
|
|
|
|
{
|
2003-12-07 06:53:04 +01:00
|
|
|
for (detail::session_impl::connection_map::iterator i = m_ses.m_connections.begin();
|
|
|
|
i != m_ses.m_connections.end();)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
if (i->second->associated_torrent() == this)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
std::size_t num_connections = m_connections.size();
|
|
|
|
peer_connection* pc = boost::get_pointer(i->second);
|
|
|
|
#endif
|
|
|
|
assert(std::find(m_connections.begin(), m_connections.end(), pc) != m_connections.end());
|
|
|
|
detail::session_impl::connection_map::iterator j = i;
|
|
|
|
++i;
|
2003-12-07 06:53:04 +01:00
|
|
|
m_ses.m_connections.erase(j);
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(m_connections.size() + 1 == num_connections);
|
|
|
|
assert(std::find(m_connections.begin(), m_connections.end(), pc) == m_connections.end());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(std::find(m_connections.begin(), m_connections.end(), boost::get_pointer(i->second)) == m_connections.end());
|
2003-11-11 02:26:04 +01:00
|
|
|
++i;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(m_connections.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void torrent::try_next_tracker()
|
|
|
|
{
|
|
|
|
m_currently_trying_tracker++;
|
|
|
|
|
|
|
|
if (m_currently_trying_tracker >= m_torrent_file.trackers().size())
|
|
|
|
{
|
|
|
|
// if we've looped the tracker list, wait a bit before retrying
|
|
|
|
m_currently_trying_tracker = 0;
|
|
|
|
m_next_request = boost::posix_time::second_clock::local_time() + boost::posix_time::seconds(tracker_retry_delay);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// don't delay before trying the next tracker
|
|
|
|
m_next_request = boost::posix_time::second_clock::local_time();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
void torrent::check_files(detail::piece_checker_data& data,
|
|
|
|
boost::mutex& mutex)
|
2003-11-07 02:44:30 +01:00
|
|
|
{
|
2003-12-07 06:53:04 +01:00
|
|
|
m_storage.check_pieces(mutex, data, m_have_pieces);
|
2003-12-07 16:03:06 +01:00
|
|
|
m_num_pieces = std::accumulate(
|
|
|
|
m_have_pieces.begin()
|
|
|
|
, m_have_pieces.end()
|
|
|
|
, 0);
|
|
|
|
|
2004-01-03 03:10:11 +01:00
|
|
|
m_picker.files_checked(m_have_pieces, data.unfinished_pieces);
|
2003-11-07 02:44:30 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
m_picker.integrity_check(this);
|
2003-12-07 02:26:57 +01:00
|
|
|
#endif
|
2003-11-07 02:44:30 +01:00
|
|
|
}
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
alert_manager& torrent::alerts() const
|
|
|
|
{
|
|
|
|
return m_ses.m_alerts;
|
|
|
|
}
|
|
|
|
|
|
|
|
torrent_handle torrent::get_handle() const
|
|
|
|
{
|
|
|
|
return torrent_handle(&m_ses, 0, m_torrent_file.info_hash());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
void torrent::check_invariant()
|
|
|
|
{
|
|
|
|
assert(m_num_pieces
|
|
|
|
== std::count(m_have_pieces.begin(), m_have_pieces.end(), true));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
void torrent::second_tick()
|
|
|
|
{
|
|
|
|
m_time_scaler++;
|
|
|
|
if (m_time_scaler >= 10)
|
|
|
|
{
|
|
|
|
m_time_scaler = 0;
|
|
|
|
m_policy->pulse();
|
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
|
|
|
|
for (std::vector<peer_connection*>::iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
peer_connection* p = (*i);
|
|
|
|
const stat& s = p->statistics();
|
|
|
|
m_stat += s;
|
|
|
|
p->second_tick();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_stat.second_tick();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool torrent::verify_piece(int piece_index)
|
|
|
|
{
|
|
|
|
size_type size = m_torrent_file.piece_size(piece_index);
|
|
|
|
std::vector<char> buffer(size);
|
2003-12-17 17:37:20 +01:00
|
|
|
assert(size > 0);
|
2003-12-07 15:12:14 +01:00
|
|
|
m_storage.read(&buffer[0], piece_index, 0, size);
|
2003-12-07 06:53:04 +01:00
|
|
|
|
|
|
|
hasher h;
|
|
|
|
h.update(&buffer[0], size);
|
|
|
|
sha1_hash digest = h.final();
|
|
|
|
|
|
|
|
if (m_torrent_file.hash_for_piece(piece_index) != digest)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!m_have_pieces[piece_index])
|
|
|
|
m_num_pieces++;
|
|
|
|
m_have_pieces[piece_index] = true;
|
|
|
|
|
|
|
|
assert(std::accumulate(m_have_pieces.begin(), m_have_pieces.end(), 0)
|
|
|
|
== m_num_pieces);
|
|
|
|
return true;
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
torrent_status torrent::status() const
|
2003-10-26 18:35:23 +01:00
|
|
|
{
|
2003-10-31 05:02:51 +01:00
|
|
|
torrent_status st;
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
const std::vector<bool>& p = m_have_pieces;
|
|
|
|
assert(std::accumulate(p.begin(), p.end(), 0) == m_num_pieces);
|
2003-10-30 00:28:09 +01:00
|
|
|
|
|
|
|
int total_blocks
|
|
|
|
= (m_torrent_file.total_size()+m_block_size-1)/m_block_size;
|
|
|
|
int blocks_per_piece
|
|
|
|
= m_torrent_file.piece_length() / m_block_size;
|
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
int unverified_blocks = m_picker.unverified_blocks();
|
2003-10-30 00:28:09 +01:00
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
int blocks_we_have = m_num_pieces * blocks_per_piece;
|
2003-10-31 13:07:07 +01:00
|
|
|
const int last_piece = m_torrent_file.num_pieces()-1;
|
|
|
|
if (p[last_piece])
|
|
|
|
{
|
|
|
|
blocks_we_have += m_picker.blocks_in_piece(last_piece)
|
|
|
|
- blocks_per_piece;
|
|
|
|
}
|
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
// payload transfer
|
|
|
|
st.total_payload_download = m_stat.total_payload_download();
|
|
|
|
st.total_payload_upload = m_stat.total_payload_upload();
|
|
|
|
|
|
|
|
// total transfer
|
|
|
|
st.total_download = m_stat.total_payload_download()
|
|
|
|
+ m_stat.total_protocol_download();
|
|
|
|
st.total_upload = m_stat.total_payload_upload()
|
|
|
|
+ m_stat.total_protocol_upload();
|
|
|
|
|
|
|
|
// transfer rate
|
2003-12-07 06:53:04 +01:00
|
|
|
st.download_rate = m_stat.download_rate();
|
|
|
|
st.upload_rate = m_stat.upload_rate();
|
2003-12-22 08:14:35 +01:00
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
st.progress = (blocks_we_have + unverified_blocks)
|
2003-10-31 05:02:51 +01:00
|
|
|
/ static_cast<float>(total_blocks);
|
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
st.next_announce = next_announce()
|
|
|
|
- boost::posix_time::second_clock::local_time();
|
|
|
|
|
2003-11-20 20:58:29 +01:00
|
|
|
// TODO: this is not accurate because it assumes the last
|
|
|
|
// block is m_block_size bytes
|
2003-12-07 06:53:04 +01:00
|
|
|
// TODO: st.pieces could be a const pointer maybe?
|
2003-11-20 20:58:29 +01:00
|
|
|
st.total_done = (blocks_we_have + unverified_blocks) * m_block_size;
|
2003-12-07 06:53:04 +01:00
|
|
|
st.pieces = m_have_pieces;
|
2003-11-20 20:58:29 +01:00
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
if (m_got_tracker_response == false)
|
|
|
|
st.state = torrent_status::connecting_to_tracker;
|
|
|
|
else if (m_num_pieces == p.size())
|
2003-10-31 05:02:51 +01:00
|
|
|
st.state = torrent_status::seeding;
|
|
|
|
else
|
|
|
|
st.state = torrent_status::downloading;
|
|
|
|
|
|
|
|
return st;
|
2003-10-26 18:35:23 +01:00
|
|
|
}
|
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
void torrent::tracker_request_timed_out()
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
debug_log("*** tracker timed out");
|
|
|
|
#endif
|
|
|
|
if (m_ses.m_alerts.should_post(alert::warning))
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "tracker: \""
|
|
|
|
<< m_torrent_file.trackers()[m_currently_trying_tracker].url
|
|
|
|
<< "\" timed out";
|
2004-01-07 01:48:02 +01:00
|
|
|
m_ses.m_alerts.post_alert(tracker_alert(get_handle(), s.str()));
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
|
|
|
// TODO: increase the retry_delay for
|
|
|
|
// each failed attempt on the same tracker!
|
|
|
|
// maybe we should add a counter that keeps
|
|
|
|
// track of how many times a specific tracker
|
|
|
|
// has timed out?
|
|
|
|
try_next_tracker();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: this function should also take the
|
2004-01-08 18:03:04 +01:00
|
|
|
// HTTP-response code as an argument.
|
2003-12-22 08:14:35 +01:00
|
|
|
// with some codes, we should just consider
|
|
|
|
// the tracker as a failure and not retry
|
|
|
|
// it anymore
|
|
|
|
void torrent::tracker_request_error(const char* str)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
debug_log(std::string("*** tracker error: ") + str);
|
|
|
|
#endif
|
|
|
|
if (m_ses.m_alerts.should_post(alert::warning))
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "tracker: \""
|
|
|
|
<< m_torrent_file.trackers()[m_currently_trying_tracker].url
|
|
|
|
<< "\" " << str;
|
2004-01-07 01:48:02 +01:00
|
|
|
m_ses.m_alerts.post_alert(tracker_alert(get_handle(), s.str()));
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try_next_tracker();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-11-20 20:58:29 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
void torrent::debug_log(const std::string& line)
|
|
|
|
{
|
2003-12-07 06:53:04 +01:00
|
|
|
(*m_ses.m_logger) << line << "\n";
|
2003-11-20 20:58:29 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-10-24 04:18:11 +02:00
|
|
|
}
|
|
|
|
|