2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
Copyright (c) 2003, Arvid Norberg, Magnus Jonsson
|
2003-10-23 01:00:57 +02:00
|
|
|
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>
|
2003-10-25 03:31:06 +02:00
|
|
|
#include <cctype>
|
2003-10-27 16:43:33 +01:00
|
|
|
#include <algorithm>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push, 1)
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
#include <boost/filesystem/convenience.hpp>
|
2003-11-28 18:29:27 +01:00
|
|
|
#include <boost/filesystem/exception.hpp>
|
2003-12-15 18:46:47 +01:00
|
|
|
#include <boost/limits.hpp>
|
2004-01-17 21:04:19 +01:00
|
|
|
#include <boost/bind.hpp>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/peer_id.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/session.hpp"
|
2003-12-16 14:33:29 +01:00
|
|
|
#include "libtorrent/fingerprint.hpp"
|
2004-01-07 01:48:02 +01:00
|
|
|
#include "libtorrent/entry.hpp"
|
2004-01-18 20:12:18 +01:00
|
|
|
#include "libtorrent/alert_types.hpp"
|
2004-01-25 19:18:36 +01:00
|
|
|
#include "libtorrent/invariant_check.hpp"
|
2004-01-26 02:08:59 +01:00
|
|
|
#include "libtorrent/file.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
|
|
namespace std
|
|
|
|
{
|
|
|
|
using ::srand;
|
2003-10-25 03:31:06 +02:00
|
|
|
using ::isprint;
|
2003-10-23 01:00:57 +02:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
|
|
|
|
// This struct is used by control_upload_rates() below. It keeps
|
|
|
|
// track how much bandwidth has been allocated to each connection
|
|
|
|
// and other relevant information to assist in the allocation process.
|
|
|
|
struct connection_info
|
|
|
|
{
|
|
|
|
libtorrent::peer_connection* p; // which peer_connection this info refers to
|
|
|
|
int allocated_quota; // bandwidth allocated to this peer connection
|
|
|
|
int quota_limit; // bandwidth limit
|
|
|
|
int estimated_upload_capacity; // estimated channel bandwidth
|
|
|
|
|
2003-12-17 04:40:13 +01:00
|
|
|
bool operator < (const connection_info &other) const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
return estimated_upload_capacity < other.estimated_upload_capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
int give(int amount)
|
|
|
|
{
|
|
|
|
|
|
|
|
// if amount > 0, try to add amount to the allocated quota.
|
|
|
|
// if amount < 0, try to subtract abs(amount) from the allocated quota
|
|
|
|
//
|
|
|
|
// Quota will not go above quota_limit or below 0. This means that
|
|
|
|
// not all the amount given or taken may be accepted.
|
|
|
|
//
|
|
|
|
// return value: how much quota was actually added (or subtracted if negative).
|
|
|
|
|
|
|
|
int old_quota=allocated_quota;
|
|
|
|
allocated_quota+=amount;
|
2003-12-15 18:46:47 +01:00
|
|
|
if(quota_limit!=-1)
|
|
|
|
allocated_quota=std::min(allocated_quota,quota_limit);
|
2003-12-14 06:56:12 +01:00
|
|
|
allocated_quota=std::max(0,allocated_quota);
|
|
|
|
return allocated_quota-old_quota;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
// adjusts the upload rates of every peer connection
|
|
|
|
// to make sure the sum of all send quotas equals
|
|
|
|
// the given upload_limit. An upload limit of -1 means
|
|
|
|
// unlimited upload rate, but the rates of each peer
|
|
|
|
// has to be set anyway, since it depends on the download
|
|
|
|
// rate from the peer.
|
|
|
|
void control_upload_rates(
|
|
|
|
int upload_limit
|
|
|
|
, libtorrent::detail::session_impl::connection_map connections)
|
|
|
|
{
|
|
|
|
using namespace libtorrent;
|
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(upload_limit > 0 || upload_limit == -1);
|
2004-01-25 05:18:08 +01:00
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
if (connections.empty()) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (upload_limit == -1)
|
|
|
|
{
|
|
|
|
for (detail::session_impl::connection_map::iterator i = connections.begin();
|
|
|
|
i != connections.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
// there's no limit, set the quota to max
|
|
|
|
// allowed
|
|
|
|
peer_connection& p = *i->second;
|
|
|
|
p.set_send_quota(p.send_quota_limit());
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// There's an upload limit, so we need to distribute the available
|
|
|
|
// upload bandwidth among the peer_connections fairly, but not
|
|
|
|
// wastefully.
|
|
|
|
|
|
|
|
// For each peer_connection, keep some local data about their
|
|
|
|
// quota limit and estimated upload capacity, and how much quota
|
|
|
|
// has been allocated to them.
|
|
|
|
|
|
|
|
std::vector<connection_info> peer_info;
|
|
|
|
|
|
|
|
for (detail::session_impl::connection_map::iterator i = connections.begin();
|
|
|
|
i != connections.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
peer_connection& p = *i->second;
|
|
|
|
connection_info pi;
|
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
pi.p = &p;
|
|
|
|
pi.allocated_quota = 0; // we haven't given it any bandwith yet
|
|
|
|
pi.quota_limit = p.send_quota_limit();
|
2003-12-14 06:56:12 +01:00
|
|
|
|
|
|
|
pi.estimated_upload_capacity=
|
2003-12-15 18:46:47 +01:00
|
|
|
p.has_data() ? std::max(10,(int)ceil(p.statistics().upload_rate()*1.1f))
|
2003-12-14 06:56:12 +01:00
|
|
|
// If there's no data to send, upload capacity is practically 0.
|
|
|
|
// Here we set it to 1 though, because otherwise it will not be able
|
|
|
|
// to accept any quota at all, which may upset quota_limit balances.
|
|
|
|
: 1;
|
|
|
|
|
|
|
|
peer_info.push_back(pi);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sum all peer_connections' quota limit to get the total quota limit.
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
int sum_total_of_quota_limits=0;
|
2004-01-25 13:37:15 +01:00
|
|
|
for (int i = 0; i < (int)peer_info.size(); ++i)
|
2003-12-15 18:46:47 +01:00
|
|
|
{
|
2004-01-25 13:37:15 +01:00
|
|
|
int quota_limit = peer_info[i].quota_limit;
|
|
|
|
if (quota_limit == -1)
|
2003-12-15 18:46:47 +01:00
|
|
|
{
|
|
|
|
// quota_limit=-1 means infinite, so
|
|
|
|
// sum_total_of_quota_limits will be infinite too...
|
2004-01-25 13:37:15 +01:00
|
|
|
sum_total_of_quota_limits = std::numeric_limits<int>::max();
|
2003-12-15 18:46:47 +01:00
|
|
|
break;
|
|
|
|
}
|
2004-01-25 13:37:15 +01:00
|
|
|
sum_total_of_quota_limits += quota_limit;
|
2003-12-15 18:46:47 +01:00
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
// This is how much total bandwidth that can be distributed.
|
2004-01-25 13:37:15 +01:00
|
|
|
int quota_left_to_distribute = std::min(upload_limit,sum_total_of_quota_limits);
|
2003-12-14 06:56:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
// Sort w.r.t. channel capacitiy, lowest channel capacity first.
|
|
|
|
// Makes it easy to traverse the list in sorted order.
|
|
|
|
std::sort(peer_info.begin(),peer_info.end());
|
|
|
|
|
|
|
|
|
|
|
|
// Distribute quota until there's nothing more to distribute
|
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
while (quota_left_to_distribute != 0)
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(quota_left_to_distribute > 0);
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
for (int i = 0; i < (int)peer_info.size(); ++i)
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
// Traverse the peer list from slowest connection to fastest.
|
|
|
|
|
|
|
|
// In each step, share bandwidth equally between this peer_connection
|
|
|
|
// and the following faster peer_connections.
|
|
|
|
//
|
2004-01-25 13:37:15 +01:00
|
|
|
// Rounds upwards to avoid trying to give 0 bandwidth to someone
|
|
|
|
// (may get caught in an endless loop otherwise)
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
int num_peers_left_to_share_quota = (int)peer_info.size() - i;
|
2004-01-25 13:37:15 +01:00
|
|
|
int try_to_give_to_this_peer
|
|
|
|
= (quota_left_to_distribute + num_peers_left_to_share_quota - 1)
|
|
|
|
/ num_peers_left_to_share_quota;
|
2003-12-14 06:56:12 +01:00
|
|
|
|
|
|
|
// But do not allocate more than the estimated upload capacity.
|
2004-01-25 13:37:15 +01:00
|
|
|
try_to_give_to_this_peer = std::min(
|
|
|
|
peer_info[i].estimated_upload_capacity
|
|
|
|
, try_to_give_to_this_peer);
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
// Also, when the peer is given quota, it will
|
|
|
|
// not accept more than it's quota_limit.
|
|
|
|
int quota_actually_given_to_peer
|
|
|
|
= peer_info[i].give(try_to_give_to_this_peer);
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
quota_left_to_distribute -= quota_actually_given_to_peer;
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, inform the peers of how much quota they get.
|
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
for(int i = 0; i < (int)peer_info.size(); ++i)
|
2003-12-14 06:56:12 +01:00
|
|
|
peer_info[i].p->set_send_quota(peer_info[i].allocated_quota);
|
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
int sum_quota = 0;
|
|
|
|
int sum_quota_limit = 0;
|
2003-12-07 06:53:04 +01:00
|
|
|
for (detail::session_impl::connection_map::iterator i = connections.begin();
|
|
|
|
i != connections.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
peer_connection& p = *i->second;
|
2003-12-14 06:56:12 +01:00
|
|
|
sum_quota += p.send_quota();
|
|
|
|
|
2003-12-15 18:46:47 +01:00
|
|
|
if(p.send_quota_limit() == -1)
|
|
|
|
{
|
|
|
|
sum_quota_limit=std::numeric_limits<int>::max();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sum_quota_limit!=std::numeric_limits<int>::max())
|
|
|
|
{
|
|
|
|
sum_quota_limit += p.send_quota_limit();
|
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2003-12-15 18:46:47 +01:00
|
|
|
assert(sum_quota == std::min(upload_limit,sum_quota_limit));
|
2003-12-07 06:53:04 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-10-26 18:35:23 +01:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
|
|
|
namespace detail
|
|
|
|
{
|
2003-10-31 05:02:51 +01:00
|
|
|
void checker_impl::operator()()
|
|
|
|
{
|
2003-11-05 18:42:27 +01:00
|
|
|
eh_initializer();
|
2003-10-31 05:02:51 +01:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
piece_checker_data* t;
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock l(m_mutex);
|
|
|
|
|
|
|
|
// if the job queue is empty and
|
|
|
|
// we shouldn't abort
|
|
|
|
// wait for a signal
|
|
|
|
if (m_torrents.empty() && !m_abort)
|
|
|
|
m_cond.wait(l);
|
|
|
|
|
|
|
|
if (m_abort) return;
|
|
|
|
|
|
|
|
assert(!m_torrents.empty());
|
|
|
|
|
|
|
|
t = &m_torrents.front();
|
|
|
|
if (t->abort)
|
|
|
|
{
|
|
|
|
m_torrents.pop_front();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2003-12-07 06:53:04 +01:00
|
|
|
assert(t != 0);
|
|
|
|
t->torrent_ptr->check_files(*t, m_mutex);
|
2003-10-31 05:02:51 +01:00
|
|
|
// lock the session to add the new torrent
|
|
|
|
|
|
|
|
boost::mutex::scoped_lock l(m_mutex);
|
|
|
|
if (!t->abort)
|
|
|
|
{
|
2004-01-25 13:37:15 +01:00
|
|
|
boost::mutex::scoped_lock l(m_ses.m_mutex);
|
2003-10-31 05:02:51 +01:00
|
|
|
|
2004-01-25 13:37:15 +01:00
|
|
|
m_ses.m_torrents.insert(
|
2003-10-31 05:02:51 +01:00
|
|
|
std::make_pair(t->info_hash, t->torrent_ptr)).first;
|
2004-01-12 04:05:10 +01:00
|
|
|
|
|
|
|
peer_id id;
|
|
|
|
std::fill(id.begin(), id.end(), 0);
|
|
|
|
for (std::vector<address>::const_iterator i = t->peers.begin();
|
|
|
|
i != t->peers.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
t->torrent_ptr->get_policy().peer_from_tracker(*i, id);
|
|
|
|
}
|
2003-10-31 05:02:51 +01:00
|
|
|
}
|
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
catch(const std::exception& e)
|
2003-11-28 18:29:27 +01:00
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
std::cerr << "error while checking files: " << e.what() << "\n";
|
|
|
|
#endif
|
|
|
|
}
|
2003-10-31 05:02:51 +01:00
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
2003-11-05 00:27:06 +01:00
|
|
|
std::cerr << "error while checking files\n";
|
2003-10-31 05:02:51 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove ourself from the 'checking'-list
|
|
|
|
// (we're no longer in the checking state)
|
|
|
|
boost::mutex::scoped_lock l(m_mutex);
|
|
|
|
m_torrents.pop_front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
detail::piece_checker_data* checker_impl::find_torrent(const sha1_hash& info_hash)
|
|
|
|
{
|
|
|
|
for (std::deque<piece_checker_data>::iterator i
|
|
|
|
= m_torrents.begin();
|
|
|
|
i != m_torrents.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
if (i->info_hash == info_hash) return &(*i);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-01-26 11:29:00 +01:00
|
|
|
session_impl::session_impl(std::pair<int, int> listen_port_range,
|
2003-12-16 14:33:29 +01:00
|
|
|
const fingerprint& cl_fprint)
|
2004-01-26 11:29:00 +01:00
|
|
|
: m_tracker_manager(m_settings)
|
|
|
|
, m_listen_port_range(listen_port_range)
|
|
|
|
, m_listen_port(listen_port_range.first)
|
|
|
|
, m_abort(false)
|
2003-11-09 19:17:09 +01:00
|
|
|
, m_upload_rate(-1)
|
2003-12-22 08:14:35 +01:00
|
|
|
, m_incoming_connection(false)
|
2003-10-26 18:35:23 +01:00
|
|
|
{
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(listen_port_range.first > 0);
|
|
|
|
assert(listen_port_range.first < listen_port_range.second);
|
|
|
|
assert(m_listen_port > 0);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-26 18:35:23 +01:00
|
|
|
// ---- generate a peer id ----
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
std::srand((unsigned int)std::time(0));
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-12-16 14:33:29 +01:00
|
|
|
std::string print = cl_fprint.to_string();
|
|
|
|
assert(print.length() == 8);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-26 18:35:23 +01:00
|
|
|
// the client's fingerprint
|
2003-12-16 14:33:29 +01:00
|
|
|
std::copy(
|
|
|
|
print.begin()
|
|
|
|
, print.begin() + print.length()
|
|
|
|
, m_peer_id.begin());
|
2003-10-26 18:35:23 +01:00
|
|
|
|
|
|
|
// the random number
|
2003-12-16 14:33:29 +01:00
|
|
|
for (unsigned char* i = m_peer_id.begin() + print.length();
|
2003-10-26 18:35:23 +01:00
|
|
|
i != m_peer_id.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
*i = rand();
|
|
|
|
}
|
2003-10-25 03:31:06 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-20 12:01:50 +01:00
|
|
|
void session_impl::purge_connections()
|
|
|
|
{
|
|
|
|
while (!m_disconnect_peer.empty())
|
|
|
|
{
|
|
|
|
m_connections.erase(m_disconnect_peer.back());
|
|
|
|
m_disconnect_peer.pop_back();
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
2004-01-20 12:01:50 +01:00
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
void session_impl::operator()()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-11-05 18:42:27 +01:00
|
|
|
eh_initializer();
|
2003-11-09 19:17:09 +01:00
|
|
|
#ifndef NDEBUG
|
2003-10-25 03:31:06 +02:00
|
|
|
m_logger = create_log("main session");
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-11-10 14:15:41 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
// create listener socket
|
2004-01-26 11:29:00 +01:00
|
|
|
boost::shared_ptr<socket> listener(new socket(socket::tcp, false));
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2003-10-31 05:02:51 +01:00
|
|
|
listener->listen(m_listen_port, 5);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-26 02:08:59 +01:00
|
|
|
catch (std::exception&)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-10-31 05:02:51 +01:00
|
|
|
m_listen_port++;
|
2004-01-26 11:29:00 +01:00
|
|
|
if (m_listen_port > m_listen_port_range.second)
|
|
|
|
{
|
|
|
|
m_alerts.post_alert(listen_failed_alert(
|
|
|
|
"none of the ports in the given range could be opened"));
|
|
|
|
return;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-11-09 19:17:09 +01:00
|
|
|
#ifndef NDEBUG
|
2003-10-31 05:02:51 +01:00
|
|
|
(*m_logger) << "listening on port: " << m_listen_port << "\n";
|
2003-10-25 03:31:06 +02:00
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
m_selector.monitor_readability(listener);
|
|
|
|
m_selector.monitor_errors(listener);
|
|
|
|
|
|
|
|
std::vector<boost::shared_ptr<socket> > readable_clients;
|
|
|
|
std::vector<boost::shared_ptr<socket> > writable_clients;
|
|
|
|
std::vector<boost::shared_ptr<socket> > error_clients;
|
|
|
|
boost::posix_time::ptime timer = boost::posix_time::second_clock::local_time();
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
int loops_per_second = 0;
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
for(;;)
|
|
|
|
{
|
2003-11-09 19:17:09 +01:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2004-01-25 19:18:36 +01:00
|
|
|
check_invariant("loops_per_second++");
|
2003-12-01 06:01:40 +01:00
|
|
|
loops_per_second++;
|
2003-11-09 19:17:09 +01:00
|
|
|
#endif
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
// if nothing happens within 500000 microseconds (0.5 seconds)
|
|
|
|
// do the loop anyway to check if anything else has changed
|
2003-12-01 06:01:40 +01:00
|
|
|
// << "sleeping\n";
|
2003-10-23 01:00:57 +02:00
|
|
|
m_selector.wait(500000, readable_clients, writable_clients, error_clients);
|
|
|
|
|
2004-01-15 21:00:12 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
for (std::vector<boost::shared_ptr<libtorrent::socket> >::iterator i =
|
|
|
|
writable_clients.begin();
|
|
|
|
i != writable_clients.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
assert((*i)->is_writable());
|
|
|
|
}
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
boost::mutex::scoped_lock l(m_mutex);
|
2003-11-02 22:06:50 +01:00
|
|
|
|
2003-10-30 00:28:09 +01:00
|
|
|
// +1 for the listen socket
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
2003-10-30 00:28:09 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
if (m_abort)
|
|
|
|
{
|
|
|
|
m_tracker_manager.abort_all_requests();
|
|
|
|
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i =
|
|
|
|
m_torrents.begin();
|
|
|
|
i != m_torrents.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
i->second->abort();
|
2003-10-31 05:02:51 +01:00
|
|
|
m_tracker_manager.queue_request(i->second->generate_tracker_request(m_listen_port));
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
m_connections.clear();
|
|
|
|
m_torrents.clear();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-01-15 21:03:23 +01:00
|
|
|
#ifndef NDEBUG
|
2004-01-25 19:18:36 +01:00
|
|
|
check_invariant("before SEND SOCKETS");
|
2004-01-15 21:03:23 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// ************************
|
|
|
|
// SEND SOCKETS
|
|
|
|
// ************************
|
|
|
|
|
|
|
|
// let the writable clients send data
|
|
|
|
for (std::vector<boost::shared_ptr<socket> >::iterator i
|
|
|
|
= writable_clients.begin();
|
|
|
|
i != writable_clients.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
assert((*i)->is_writable());
|
|
|
|
connection_map::iterator p = m_connections.find(*i);
|
|
|
|
// the connection may have been disconnected in the receive phase
|
|
|
|
if (p == m_connections.end())
|
|
|
|
{
|
|
|
|
m_selector.remove(*i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
assert(m_selector.is_writability_monitored(p->first));
|
|
|
|
assert(p->second->has_data());
|
|
|
|
assert(p->second->get_socket()->is_writable());
|
|
|
|
p->second->send_data();
|
|
|
|
}
|
2004-01-26 02:08:59 +01:00
|
|
|
catch (file_error& e)
|
|
|
|
{
|
2004-01-26 11:29:00 +01:00
|
|
|
torrent* t = p->second->associated_torrent();
|
|
|
|
assert(t != 0);
|
|
|
|
|
2004-01-26 02:08:59 +01:00
|
|
|
if (m_alerts.should_post(alert::fatal))
|
|
|
|
{
|
|
|
|
m_alerts.post_alert(
|
|
|
|
file_error_alert(
|
2004-01-26 11:29:00 +01:00
|
|
|
t->get_handle()
|
2004-01-26 02:08:59 +01:00
|
|
|
, e.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
m_selector.remove(*i);
|
|
|
|
m_connections.erase(p);
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
|
|
|
t->abort();
|
2004-01-26 02:08:59 +01:00
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
2004-01-15 21:03:23 +01:00
|
|
|
{
|
2004-01-24 18:14:03 +01:00
|
|
|
// the connection wants to disconnect for some reason,
|
|
|
|
// remove it from the connection-list
|
|
|
|
if (m_alerts.should_post(alert::debug))
|
|
|
|
{
|
|
|
|
m_alerts.post_alert(
|
2004-01-31 11:20:19 +01:00
|
|
|
peer_error_alert(p->first->sender(), e.what()));
|
2004-01-24 18:14:03 +01:00
|
|
|
}
|
|
|
|
|
2004-01-15 21:03:23 +01:00
|
|
|
m_selector.remove(*i);
|
|
|
|
m_connections.erase(p);
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
2004-01-15 21:03:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-01-20 12:01:50 +01:00
|
|
|
purge_connections();
|
2004-01-15 21:03:23 +01:00
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
#ifndef NDEBUG
|
2004-01-25 19:18:36 +01:00
|
|
|
check_invariant("after SEND SOCKETS");
|
2003-12-01 06:01:40 +01:00
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
// ************************
|
|
|
|
// RECEIVE SOCKETS
|
|
|
|
// ************************
|
|
|
|
|
|
|
|
// let the readable clients receive data
|
2003-10-23 18:55:52 +02:00
|
|
|
for (std::vector<boost::shared_ptr<socket> >::iterator i = readable_clients.begin();
|
|
|
|
i != readable_clients.end();
|
|
|
|
++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
// special case for listener socket
|
|
|
|
if (*i == listener)
|
|
|
|
{
|
|
|
|
boost::shared_ptr<libtorrent::socket> s = (*i)->accept();
|
2003-11-23 04:00:45 +01:00
|
|
|
s->set_blocking(false);
|
2003-10-23 01:00:57 +02:00
|
|
|
if (s)
|
|
|
|
{
|
|
|
|
// we got a connection request!
|
2003-12-22 08:14:35 +01:00
|
|
|
m_incoming_connection = true;
|
2003-11-09 19:17:09 +01:00
|
|
|
#ifndef NDEBUG
|
2003-10-23 01:00:57 +02:00
|
|
|
(*m_logger) << s->sender().as_string() << " <== INCOMING CONNECTION\n";
|
|
|
|
#endif
|
2003-12-01 06:01:40 +01:00
|
|
|
// TODO: filter ip:s
|
|
|
|
|
2003-11-09 19:17:09 +01:00
|
|
|
boost::shared_ptr<peer_connection> c(
|
2003-12-07 06:53:04 +01:00
|
|
|
new peer_connection(*this, m_selector, s));
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2003-11-10 14:15:41 +01:00
|
|
|
if (m_upload_rate != -1) c->set_send_quota(0);
|
2003-10-23 01:00:57 +02:00
|
|
|
m_connections.insert(std::make_pair(s, c));
|
|
|
|
m_selector.monitor_readability(s);
|
|
|
|
m_selector.monitor_errors(s);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
connection_map::iterator p = m_connections.find(*i);
|
|
|
|
if(p == m_connections.end())
|
|
|
|
{
|
|
|
|
m_selector.remove(*i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2003-11-23 04:00:45 +01:00
|
|
|
// (*m_logger) << "readable: " << p->first->sender().as_string() << "\n";
|
2003-10-23 01:00:57 +02:00
|
|
|
p->second->receive_data();
|
|
|
|
}
|
2004-01-26 02:08:59 +01:00
|
|
|
catch (file_error& e)
|
|
|
|
{
|
2004-01-26 11:29:00 +01:00
|
|
|
torrent* t = p->second->associated_torrent();
|
|
|
|
assert(t != 0);
|
|
|
|
|
2004-01-26 02:08:59 +01:00
|
|
|
if (m_alerts.should_post(alert::fatal))
|
|
|
|
{
|
|
|
|
m_alerts.post_alert(
|
|
|
|
file_error_alert(
|
2004-01-26 11:29:00 +01:00
|
|
|
t->get_handle()
|
2004-01-26 02:08:59 +01:00
|
|
|
, e.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
m_selector.remove(*i);
|
|
|
|
m_connections.erase(p);
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
|
|
|
t->abort();
|
2004-01-26 02:08:59 +01:00
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-05 00:51:54 +01:00
|
|
|
if (m_alerts.should_post(alert::debug))
|
|
|
|
{
|
|
|
|
m_alerts.post_alert(
|
2004-01-31 11:20:19 +01:00
|
|
|
peer_error_alert(p->first->sender(), e.what()));
|
2004-01-05 00:51:54 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
// the connection wants to disconnect for some reason, remove it
|
|
|
|
// from the connection-list
|
|
|
|
m_selector.remove(*i);
|
|
|
|
m_connections.erase(p);
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-01-20 12:01:50 +01:00
|
|
|
purge_connections();
|
2003-12-01 06:01:40 +01:00
|
|
|
#ifndef NDEBUG
|
2004-01-25 19:18:36 +01:00
|
|
|
check_invariant("after RECEIVE SOCKETS");
|
2003-12-01 06:01:40 +01:00
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
// ************************
|
|
|
|
// ERROR SOCKETS
|
|
|
|
// ************************
|
|
|
|
|
|
|
|
|
|
|
|
// disconnect the one we couldn't connect to
|
2003-10-23 18:55:52 +02:00
|
|
|
for (std::vector<boost::shared_ptr<socket> >::iterator i = error_clients.begin();
|
|
|
|
i != error_clients.end();
|
|
|
|
++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
connection_map::iterator p = m_connections.find(*i);
|
2004-01-24 18:14:03 +01:00
|
|
|
if (m_alerts.should_post(alert::debug))
|
|
|
|
{
|
|
|
|
m_alerts.post_alert(
|
|
|
|
peer_error_alert(
|
2004-01-31 11:20:19 +01:00
|
|
|
p->first->sender()
|
2004-01-24 18:14:03 +01:00
|
|
|
, "socket received an exception"));
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
m_selector.remove(*i);
|
|
|
|
// the connection may have been disconnected in the receive or send phase
|
2004-01-24 18:14:03 +01:00
|
|
|
if (p != m_connections.end())
|
|
|
|
{
|
|
|
|
m_connections.erase(p);
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
2004-01-24 18:14:03 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-11-02 22:06:50 +01:00
|
|
|
#ifndef NDEBUG
|
2004-01-25 19:18:36 +01:00
|
|
|
check_invariant("after ERROR SOCKETS");
|
2003-11-02 22:06:50 +01:00
|
|
|
#endif
|
|
|
|
|
2003-11-09 19:17:09 +01:00
|
|
|
boost::posix_time::time_duration d = boost::posix_time::second_clock::local_time() - timer;
|
|
|
|
if (d.seconds() < 1) continue;
|
|
|
|
timer = boost::posix_time::second_clock::local_time();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
// ************************
|
2003-11-09 19:17:09 +01:00
|
|
|
// THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND
|
2003-10-23 01:00:57 +02:00
|
|
|
// ************************
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
// std::cout << "\n\nloops: " << loops_per_second << "\n";
|
|
|
|
loops_per_second = 0;
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
// do the second_tick() on each connection
|
|
|
|
// this will update their statistics (download and upload speeds)
|
2003-11-02 22:06:50 +01:00
|
|
|
// also purge sockets that have timed out
|
|
|
|
// and keep sockets open by keeping them alive.
|
|
|
|
for (connection_map::iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end();)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-11-02 22:06:50 +01:00
|
|
|
connection_map::iterator j = i;
|
|
|
|
++i;
|
|
|
|
// if this socket has timed out
|
|
|
|
// close it.
|
|
|
|
if (j->second->has_timed_out())
|
|
|
|
{
|
|
|
|
m_selector.remove(j->first);
|
|
|
|
m_connections.erase(j);
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1);
|
2003-11-02 22:06:50 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
j->second->keep_alive();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// check each torrent for abortion or
|
|
|
|
// tracker updates
|
2003-10-31 05:02:51 +01:00
|
|
|
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
|
|
|
|
= m_torrents.begin();
|
2003-10-23 01:00:57 +02:00
|
|
|
i != m_torrents.end();)
|
|
|
|
{
|
|
|
|
if (i->second->is_aborted())
|
|
|
|
{
|
2003-10-31 05:02:51 +01:00
|
|
|
m_tracker_manager.queue_request(
|
|
|
|
i->second->generate_tracker_request(m_listen_port));
|
2004-01-20 23:59:21 +01:00
|
|
|
i->second->disconnect_all();
|
2003-11-10 14:15:41 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
sha1_hash i_hash = i->second->torrent_file().info_hash();
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator j = i;
|
|
|
|
++i;
|
|
|
|
m_torrents.erase(j);
|
2003-11-10 14:15:41 +01:00
|
|
|
assert(m_torrents.find(i_hash) == m_torrents.end());
|
2003-10-23 01:00:57 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (i->second->should_request())
|
|
|
|
{
|
|
|
|
m_tracker_manager.queue_request(
|
2003-10-31 05:02:51 +01:00
|
|
|
i->second->generate_tracker_request(m_listen_port),
|
2003-10-23 01:00:57 +02:00
|
|
|
boost::get_pointer(i->second));
|
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
|
|
|
|
i->second->second_tick();
|
2003-10-23 01:00:57 +02:00
|
|
|
++i;
|
|
|
|
}
|
2004-01-20 12:01:50 +01:00
|
|
|
purge_connections();
|
|
|
|
|
2003-12-08 22:59:48 +01:00
|
|
|
// distribute the maximum upload rate among the peers
|
|
|
|
control_upload_rates(m_upload_rate, m_connections);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-11-23 04:00:45 +01:00
|
|
|
|
2003-12-08 22:59:48 +01:00
|
|
|
m_tracker_manager.tick();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
while (!m_tracker_manager.send_finished())
|
|
|
|
{
|
|
|
|
m_tracker_manager.tick();
|
|
|
|
boost::xtime t;
|
|
|
|
boost::xtime_get(&t, boost::TIME_UTC);
|
2003-12-07 06:53:04 +01:00
|
|
|
t.nsec += 100000000;
|
2003-10-23 01:00:57 +02:00
|
|
|
boost::thread::sleep(t);
|
|
|
|
}
|
2003-11-10 14:15:41 +01:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
}
|
2004-01-26 02:08:59 +01:00
|
|
|
catch (std::bad_cast& e)
|
2003-11-10 14:15:41 +01:00
|
|
|
{
|
|
|
|
std::cerr << e.what() << "\n";
|
|
|
|
}
|
2004-01-26 02:08:59 +01:00
|
|
|
catch (std::exception& e)
|
2003-11-10 14:15:41 +01:00
|
|
|
{
|
|
|
|
std::cerr << e.what() << "\n";
|
|
|
|
}
|
2004-01-26 02:08:59 +01:00
|
|
|
catch (...)
|
2003-11-10 14:15:41 +01:00
|
|
|
{
|
|
|
|
std::cerr << "error!\n";
|
|
|
|
}
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-11-07 02:44:30 +01:00
|
|
|
|
2003-10-27 16:43:33 +01:00
|
|
|
// the return value from this function is valid only as long as the
|
|
|
|
// session is locked!
|
2003-10-31 05:02:51 +01:00
|
|
|
torrent* session_impl::find_torrent(const sha1_hash& info_hash)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-10-27 16:43:33 +01:00
|
|
|
std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
|
|
|
|
= m_torrents.find(info_hash);
|
|
|
|
if (i != m_torrents.end()) return boost::get_pointer(i->second);
|
2003-10-30 00:28:09 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2003-10-27 16:43:33 +01:00
|
|
|
|
2003-11-09 19:17:09 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
boost::shared_ptr<logger> session_impl::create_log(std::string name)
|
|
|
|
{
|
|
|
|
name = "libtorrent_log_" + name + ".log";
|
|
|
|
// current options are file_logger and cout_logger
|
|
|
|
#if defined(TORRENT_VERBOSE_LOGGING)
|
|
|
|
return boost::shared_ptr<logger>(new file_logger(name.c_str()));
|
|
|
|
#else
|
|
|
|
return boost::shared_ptr<logger>(new null_logger());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
#ifndef NDEBUG
|
2004-01-25 19:18:36 +01:00
|
|
|
void session_impl::check_invariant(const char *place)
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2004-01-25 05:18:08 +01:00
|
|
|
assert(place);
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
for (connection_map::iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end();
|
|
|
|
++i)
|
|
|
|
{
|
2003-12-21 18:28:27 +01:00
|
|
|
if (i->second->has_data() != m_selector.is_writability_monitored(i->first))
|
|
|
|
{
|
|
|
|
std::ofstream error_log("error.log", std::ios_base::app);
|
|
|
|
boost::shared_ptr<peer_connection> p = i->second;
|
2004-01-25 19:18:36 +01:00
|
|
|
error_log << "session_imple::check_invariant()\n"
|
2003-12-21 18:28:27 +01:00
|
|
|
"peer_connection::has_data() != is_writability_monitored()\n";
|
|
|
|
error_log << "peer_connection::has_data() " << p->has_data() << "\n";
|
|
|
|
error_log << "peer_connection::send_quota_left " << p->send_quota_left() << "\n";
|
|
|
|
error_log << "peer_connection::send_quota " << p->send_quota() << "\n";
|
|
|
|
error_log << "peer_connection::get_peer_id " << p->get_peer_id() << "\n";
|
2004-01-15 23:29:13 +01:00
|
|
|
error_log << "place: " << place << "\n";
|
2003-12-21 18:28:27 +01:00
|
|
|
error_log.flush();
|
|
|
|
assert(false);
|
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
if (i->second->associated_torrent())
|
|
|
|
{
|
|
|
|
assert(i->second->associated_torrent()
|
|
|
|
->get_policy().has_connection(boost::get_pointer(i->second)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-26 11:29:00 +01:00
|
|
|
session::session(std::pair<int, int> listen_port_range, const fingerprint& id)
|
|
|
|
: m_impl(listen_port_range, id)
|
2004-01-25 13:37:15 +01:00
|
|
|
, m_checker_impl(m_impl)
|
2003-11-07 02:44:30 +01:00
|
|
|
, m_thread(boost::ref(m_impl))
|
|
|
|
, m_checker_thread(boost::ref(m_checker_impl))
|
|
|
|
{
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(listen_port_range.first > 0);
|
|
|
|
assert(listen_port_range.first < listen_port_range.second);
|
2003-11-07 02:44:30 +01:00
|
|
|
#ifndef NDEBUG
|
2003-12-16 14:33:29 +01:00
|
|
|
// this test was added after it came to my attention
|
|
|
|
// that devstudios managed c++ failed to generate
|
|
|
|
// correct code for boost.function
|
2003-11-07 02:44:30 +01:00
|
|
|
boost::function0<void> test = boost::ref(m_impl);
|
|
|
|
assert(!test.empty());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-01-26 11:29:00 +01:00
|
|
|
session::session(std::pair<int, int> listen_port_range)
|
|
|
|
: m_impl(listen_port_range, fingerprint("LT",0,0,1,0))
|
2004-01-25 13:37:15 +01:00
|
|
|
, m_checker_impl(m_impl)
|
2003-12-16 14:33:29 +01:00
|
|
|
, m_thread(boost::ref(m_impl))
|
|
|
|
, m_checker_thread(boost::ref(m_checker_impl))
|
|
|
|
{
|
2004-01-26 11:29:00 +01:00
|
|
|
assert(listen_port_range.first > 0);
|
|
|
|
assert(listen_port_range.first < listen_port_range.second);
|
2003-12-16 14:33:29 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
boost::function0<void> test = boost::ref(m_impl);
|
|
|
|
assert(!test.empty());
|
|
|
|
#endif
|
|
|
|
}
|
2003-11-07 02:44:30 +01:00
|
|
|
|
2003-11-26 15:54:56 +01:00
|
|
|
// TODO: add a check to see if filenames are accepted on the
|
|
|
|
// current platform.
|
2003-11-02 22:06:50 +01:00
|
|
|
// if the torrent already exists, this will throw duplicate_torrent
|
2004-01-07 01:48:02 +01:00
|
|
|
torrent_handle session::add_torrent(
|
2003-11-07 02:44:30 +01:00
|
|
|
const torrent_info& ti
|
2004-01-02 21:46:24 +01:00
|
|
|
, const boost::filesystem::path& save_path
|
2004-01-07 01:48:02 +01:00
|
|
|
, const entry& resume_data)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
{
|
|
|
|
// lock the session
|
|
|
|
boost::mutex::scoped_lock l(m_impl.m_mutex);
|
|
|
|
|
|
|
|
// is the torrent already active?
|
|
|
|
if (m_impl.find_torrent(ti.info_hash()))
|
2003-11-02 22:06:50 +01:00
|
|
|
throw duplicate_torrent();
|
2003-10-31 05:02:51 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
{
|
|
|
|
// lock the checker_thread
|
|
|
|
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
|
|
|
|
|
|
|
|
// is the torrent currently being checked?
|
|
|
|
if (m_checker_impl.find_torrent(ti.info_hash()))
|
2003-11-02 22:06:50 +01:00
|
|
|
throw duplicate_torrent();
|
2003-10-31 05:02:51 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
// create the torrent and the data associated with
|
|
|
|
// the checker thread and store it before starting
|
|
|
|
// the thread
|
2004-01-02 21:46:24 +01:00
|
|
|
boost::shared_ptr<torrent> torrent_ptr(
|
|
|
|
new torrent(m_impl, ti, save_path));
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
detail::piece_checker_data d;
|
|
|
|
d.torrent_ptr = torrent_ptr;
|
|
|
|
d.save_path = save_path;
|
|
|
|
d.info_hash = ti.info_hash();
|
2004-01-02 21:46:24 +01:00
|
|
|
d.parse_resume_data(resume_data, torrent_ptr->torrent_file());
|
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
// lock the checker thread
|
|
|
|
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
// add the torrent to the queue to be checked
|
|
|
|
m_checker_impl.m_torrents.push_back(d);
|
|
|
|
// and notify the thread that it got another
|
|
|
|
// job in its queue
|
|
|
|
m_checker_impl.m_cond.notify_one();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
return torrent_handle(&m_impl, &m_checker_impl, ti.info_hash());
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-11-08 03:16:26 +01:00
|
|
|
void session::remove_torrent(const torrent_handle& h)
|
|
|
|
{
|
|
|
|
if (h.m_ses != &m_impl) return;
|
2003-11-28 18:29:27 +01:00
|
|
|
assert(h.m_chk == &m_checker_impl);
|
|
|
|
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock l(m_impl.m_mutex);
|
|
|
|
torrent* t = m_impl.find_torrent(h.m_info_hash);
|
|
|
|
if (t != 0)
|
|
|
|
{
|
|
|
|
t->abort();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
|
|
|
|
|
|
|
|
detail::piece_checker_data* d = m_checker_impl.find_torrent(h.m_info_hash);
|
|
|
|
if (d != 0)
|
|
|
|
{
|
|
|
|
d->abort = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2003-11-08 03:16:26 +01:00
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void session::set_http_settings(const http_settings& s)
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock l(m_impl.m_mutex);
|
2003-10-25 03:31:06 +02:00
|
|
|
m_impl.m_settings = s;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
session::~session()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock l(m_impl.m_mutex);
|
|
|
|
m_impl.m_abort = true;
|
|
|
|
}
|
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
|
|
|
|
// abort the checker thread
|
|
|
|
m_checker_impl.m_abort = true;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
// abort the currently checking torrent
|
|
|
|
if (!m_checker_impl.m_torrents.empty())
|
|
|
|
{
|
|
|
|
m_checker_impl.m_torrents.front().abort = true;
|
|
|
|
}
|
|
|
|
m_checker_impl.m_cond.notify_one();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_thread.join();
|
|
|
|
m_checker_thread.join();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-11-09 19:17:09 +01:00
|
|
|
void session::set_upload_rate_limit(int bytes_per_second)
|
|
|
|
{
|
2003-11-10 14:15:41 +01:00
|
|
|
assert(bytes_per_second > 0 || bytes_per_second == -1);
|
2003-11-09 19:17:09 +01:00
|
|
|
boost::mutex::scoped_lock l(m_impl.m_mutex);
|
|
|
|
m_impl.m_upload_rate = bytes_per_second;
|
|
|
|
if (m_impl.m_upload_rate != -1 || !m_impl.m_connections.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (detail::session_impl::connection_map::iterator i
|
|
|
|
= m_impl.m_connections.begin();
|
|
|
|
i != m_impl.m_connections.end();)
|
|
|
|
{
|
|
|
|
i->second->set_send_quota(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-29 17:34:07 +01:00
|
|
|
std::auto_ptr<alert> session::pop_alert()
|
|
|
|
{
|
2003-12-22 08:14:35 +01:00
|
|
|
if (m_impl.m_alerts.pending())
|
|
|
|
return m_impl.m_alerts.get();
|
|
|
|
else
|
|
|
|
return std::auto_ptr<alert>(0);
|
2003-11-29 17:34:07 +01:00
|
|
|
}
|
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
void session::set_severity_level(alert::severity_t s)
|
2003-12-21 18:28:27 +01:00
|
|
|
{
|
2003-12-22 08:14:35 +01:00
|
|
|
m_impl.m_alerts.set_severity(s);
|
|
|
|
}
|
2003-10-25 03:31:06 +02:00
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
void detail::piece_checker_data::parse_resume_data(
|
|
|
|
const entry& resume_data
|
|
|
|
, const torrent_info& info)
|
|
|
|
{
|
|
|
|
// if we don't have any resume data, return
|
|
|
|
if (resume_data.type() == entry::undefined_t) return;
|
|
|
|
|
|
|
|
entry rd = resume_data;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (rd.dict()["file-format"].string() != "libtorrent resume file")
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (rd.dict()["file-version"].integer() != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// verify info_hash
|
|
|
|
const std::string &hash = rd.dict()["info-hash"].string();
|
2004-01-18 02:58:33 +01:00
|
|
|
std::string real_hash((char*)info.info_hash().begin(), (char*)info.info_hash().end());
|
2004-01-07 01:48:02 +01:00
|
|
|
if (hash != real_hash)
|
|
|
|
return;
|
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
// the peers
|
|
|
|
|
|
|
|
entry::list_type& peer_list = rd.dict()["peers"].list();
|
|
|
|
|
|
|
|
std::vector<address> tmp_peers;
|
|
|
|
tmp_peers.reserve(peer_list.size());
|
|
|
|
for (entry::list_type::iterator i = peer_list.begin();
|
|
|
|
i != peer_list.end();
|
|
|
|
++i)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
address a(
|
|
|
|
i->dict()["ip"].string()
|
|
|
|
, (unsigned short)i->dict()["port"].integer());
|
2004-01-17 21:04:19 +01:00
|
|
|
tmp_peers.push_back(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
peers.swap(tmp_peers);
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
// read piece map
|
|
|
|
const entry::list_type& slots = rd.dict()["slots"].list();
|
2004-01-25 05:18:08 +01:00
|
|
|
if ((int)slots.size() > info.num_pieces())
|
2004-01-07 01:48:02 +01:00
|
|
|
return;
|
|
|
|
|
2004-01-12 04:05:10 +01:00
|
|
|
std::vector<int> tmp_pieces;
|
2004-01-07 01:48:02 +01:00
|
|
|
tmp_pieces.reserve(slots.size());
|
|
|
|
for (entry::list_type::const_iterator i = slots.begin();
|
|
|
|
i != slots.end();
|
|
|
|
++i)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
int index = (int)i->integer();
|
2004-01-07 01:48:02 +01:00
|
|
|
if (index >= info.num_pieces() || index < -2)
|
|
|
|
return;
|
|
|
|
tmp_pieces.push_back(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
int num_blocks_per_piece = (int)rd.dict()["blocks per piece"].integer();
|
2004-01-15 17:45:34 +01:00
|
|
|
if (num_blocks_per_piece != info.piece_length() / torrent_ptr->block_size())
|
2004-01-07 01:48:02 +01:00
|
|
|
return;
|
|
|
|
|
2004-01-12 04:05:10 +01:00
|
|
|
// the unfinished pieces
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
entry::list_type& unfinished = rd.dict()["unfinished"].list();
|
2004-01-07 01:48:02 +01:00
|
|
|
|
2004-01-12 04:05:10 +01:00
|
|
|
std::vector<piece_picker::downloading_piece> tmp_unfinished;
|
2004-01-07 01:48:02 +01:00
|
|
|
tmp_unfinished.reserve(unfinished.size());
|
2004-01-12 21:31:27 +01:00
|
|
|
for (entry::list_type::iterator i = unfinished.begin();
|
2004-01-07 01:48:02 +01:00
|
|
|
i != unfinished.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
piece_picker::downloading_piece p;
|
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
p.index = (int)i->dict()["piece"].integer();
|
2004-01-07 01:48:02 +01:00
|
|
|
if (p.index < 0 || p.index >= info.num_pieces())
|
|
|
|
return;
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
const std::string& bitmask = i->dict()["bitmask"].string();
|
2004-01-07 01:48:02 +01:00
|
|
|
|
|
|
|
const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1);
|
2004-01-26 11:29:00 +01:00
|
|
|
if ((int)bitmask.size() != num_bitmask_bytes) return;
|
2004-01-07 01:48:02 +01:00
|
|
|
for (int j = 0; j < num_bitmask_bytes; ++j)
|
|
|
|
{
|
|
|
|
unsigned char bits = bitmask[j];
|
|
|
|
for (int k = 0; k < 8; ++k)
|
|
|
|
{
|
|
|
|
const int bit = j * 8 + k;
|
|
|
|
if (bits & (1 << k))
|
|
|
|
p.finished_blocks[bit] = true;
|
|
|
|
}
|
|
|
|
}
|
2004-01-24 18:14:03 +01:00
|
|
|
|
|
|
|
if (p.finished_blocks.count() == 0) continue;
|
|
|
|
|
|
|
|
std::vector<int>::iterator slot_iter
|
|
|
|
= std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index);
|
|
|
|
if (slot_iter == tmp_pieces.end())
|
|
|
|
{
|
|
|
|
// this piece is marked as unfinished
|
|
|
|
// but doesn't have any storage
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(*slot_iter == p.index);
|
2004-01-25 19:18:36 +01:00
|
|
|
int slot_index = static_cast<int>(slot_iter - tmp_pieces.begin());
|
2004-01-24 18:14:03 +01:00
|
|
|
unsigned long adler
|
|
|
|
= torrent_ptr->filesystem().piece_crc(
|
|
|
|
slot_index
|
|
|
|
, torrent_ptr->block_size()
|
|
|
|
, p.finished_blocks);
|
|
|
|
|
|
|
|
entry::dictionary_type::iterator ad = i->dict().find("adler32");
|
|
|
|
if (ad != i->dict().end())
|
|
|
|
{
|
|
|
|
// crc's didn't match, don't use the resume data
|
|
|
|
if (ad->second.integer() != adler)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
tmp_unfinished.push_back(p);
|
|
|
|
}
|
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
// verify file sizes
|
2004-01-12 04:05:10 +01:00
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
std::vector<size_type> file_sizes;
|
|
|
|
entry::list_type& l = rd.dict()["file sizes"].list();
|
2004-01-18 02:58:33 +01:00
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
|
|
for (entry::list_type::iterator i = l.begin();
|
|
|
|
i != l.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
file_sizes.push_back(i->integer());
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// typedef entry::integer_type (entry::*mem_fun_type)() const;
|
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
std::transform(
|
|
|
|
l.begin()
|
|
|
|
, l.end()
|
|
|
|
, std::back_inserter(file_sizes)
|
|
|
|
, boost::bind(&entry::integer, _1));
|
2004-01-18 02:58:33 +01:00
|
|
|
#endif
|
2004-01-17 21:04:19 +01:00
|
|
|
if (!match_filesizes(info, save_path, file_sizes))
|
|
|
|
return;
|
2004-01-12 04:05:10 +01:00
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
piece_map.swap(tmp_pieces);
|
|
|
|
unfinished_pieces.swap(tmp_unfinished);
|
|
|
|
}
|
|
|
|
catch (invalid_encoding)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
catch (type_error)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|