Update libtorrent to 0.14 trunk.
A few minor touch ups regarding torrent state and queue.
This commit is contained in:
parent
1861ef439f
commit
70bb78b833
|
@ -160,16 +160,19 @@ class Torrent:
|
|||
"""Updates the state based on what libtorrent's state for the torrent is"""
|
||||
# Set the initial state based on the lt state
|
||||
LTSTATE = deluge.common.LT_TORRENT_STATE
|
||||
ltstate = self.handle.status().state
|
||||
ltstate = int(self.handle.status().state)
|
||||
|
||||
log.debug("set_state_based_on_ltstate: %s", ltstate)
|
||||
|
||||
if ltstate == LTSTATE["Queued"] or ltstate == LTSTATE["Checking"]:
|
||||
self.set_state("Checking")
|
||||
self.state = "Checking"
|
||||
elif ltstate == LTSTATE["Connecting"] or ltstate == LTSTATE["Downloading"] or\
|
||||
ltstate == LTSTATE["Downloading Metadata"]:
|
||||
self.set_state("Downloading")
|
||||
self.state = "Downloading"
|
||||
elif ltstate == LTSTATE["Finished"] or ltstate == LTSTATE["Seeding"]:
|
||||
self.set_state("Seeding")
|
||||
self.state = "Seeding"
|
||||
elif ltstate == LTSTATE["Allocating"]:
|
||||
self.set_state("Allocating")
|
||||
self.state = "Allocating"
|
||||
|
||||
def set_state(self, state):
|
||||
"""Accepts state strings, ie, "Paused", "Seeding", etc."""
|
||||
|
@ -183,6 +186,7 @@ class Torrent:
|
|||
component.get("TorrentManager").append_not_state_paused(self.torrent_id)
|
||||
self.handle.pause()
|
||||
|
||||
log.debug("Setting %s's state to %s", self.torrent_id, state)
|
||||
self.state = state
|
||||
|
||||
# Update the torrentqueue on any state changes
|
||||
|
@ -276,7 +280,7 @@ class Torrent:
|
|||
country += " "
|
||||
else:
|
||||
country += c
|
||||
|
||||
|
||||
ret.append({
|
||||
"ip": "%s:%s" % (peer.ip[0], peer.ip[1]),
|
||||
"up_speed": peer.up_speed,
|
||||
|
@ -428,11 +432,11 @@ class Torrent:
|
|||
pass
|
||||
|
||||
if self.handle.is_finished():
|
||||
self.state = "Seeding"
|
||||
self.set_state("Seeding")
|
||||
else:
|
||||
# Only delete the .fastresume file if we're still downloading stuff
|
||||
self.delete_fastresume()
|
||||
self.state = "Downloading"
|
||||
self.set_state("Downloading")
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -305,15 +305,15 @@ class TorrentManager(component.Component):
|
|||
|
||||
# Resume the torrent if needed
|
||||
if state == "Queued":
|
||||
torrent.set_state("Queued")
|
||||
torrent.state = "Queued"
|
||||
elif state == "Paused" or state == "Error":
|
||||
torrent.set_state("Paused")
|
||||
torrent.state = "Paused"
|
||||
if state == None and not options["add_paused"]:
|
||||
torrent.handle.resume()
|
||||
# We set the state based on libtorrent's state
|
||||
torrent.set_state_based_on_ltstate()
|
||||
if state == None and options["add_paused"]:
|
||||
torrent.set_state("Paused")
|
||||
torrent.set_state = "Paused"
|
||||
|
||||
# Emit the torrent_added signal
|
||||
self.signals.emit("torrent_added", torrent.torrent_id)
|
||||
|
|
|
@ -65,7 +65,7 @@ class TorrentQueue(component.Component):
|
|||
stop_ratio = self.config["stop_seed_ratio"]
|
||||
|
||||
for torrent_id in self.torrents.get_torrent_list():
|
||||
if self.torrents[torrent_id].handle.is_seed():
|
||||
if self.torrents[torrent_id].handle.is_finished():
|
||||
if self.torrents[torrent_id].get_ratio() >= stop_ratio:
|
||||
# This torrent is at or exceeding the stop ratio so we need to
|
||||
# pause or remove it from the session.
|
||||
|
@ -94,7 +94,7 @@ class TorrentQueue(component.Component):
|
|||
elif self.torrents[torrent_id].state == "Downloading":
|
||||
self.downloading.append((self.queue.index(torrent_id), torrent_id))
|
||||
elif self.torrents[torrent_id].state == "Queued":
|
||||
if self.torrents[torrent_id].handle.is_seed():
|
||||
if self.torrents[torrent_id].handle.is_finished():
|
||||
self.queued_seeding.append((self.queue.index(torrent_id), torrent_id))
|
||||
else:
|
||||
self.queued_downloading.append((self.queue.index(torrent_id), torrent_id))
|
||||
|
@ -105,10 +105,10 @@ class TorrentQueue(component.Component):
|
|||
self.queued_downloading.sort()
|
||||
self.queued_seeding.sort()
|
||||
|
||||
# log.debug("total seeding: %s", len(self.seeding))
|
||||
# log.debug("total downloading: %s", len(self.downloading))
|
||||
# log.debug("queued seeding: %s", len(self.queued_seeding))
|
||||
# log.debug("queued downloading: %s", len(self.queued_downloading))
|
||||
#log.debug("total seeding: %s", len(self.seeding))
|
||||
#log.debug("total downloading: %s", len(self.downloading))
|
||||
#log.debug("queued seeding: %s", len(self.queued_seeding))
|
||||
#log.debug("queued downloading: %s", len(self.queued_downloading))
|
||||
|
||||
def update_order(self):
|
||||
# This will queue/resume torrents if the queueing order changes
|
||||
|
@ -118,6 +118,9 @@ class TorrentQueue(component.Component):
|
|||
# log.debug("min(queued_seeding): %s", min(self.queued_seeding)[0])
|
||||
#except:
|
||||
# pass
|
||||
|
||||
#log.debug("queued seeding: %s", self.queued_seeding)
|
||||
#log.debug("queued downloading: %s", self.queued_downloading)
|
||||
|
||||
if self.seeding != [] and self.queued_seeding != []:
|
||||
if min(self.queued_seeding)[0] < max(self.seeding)[0]:
|
||||
|
@ -137,6 +140,7 @@ class TorrentQueue(component.Component):
|
|||
|
||||
def update_max_active(self):
|
||||
if self.config["max_active_seeding"] > -1:
|
||||
log.debug("max_active_seeding: %s", self.config["max_active_seeding"])
|
||||
if len(self.seeding) > self.config["max_active_seeding"]:
|
||||
# We need to queue some more torrents because we're over the active limit
|
||||
num_to_queue = len(self.seeding) - self.config["max_active_seeding"]
|
||||
|
|
|
@ -27,6 +27,7 @@ extern char const* piece_finished_alert_doc;
|
|||
extern char const* block_finished_alert_doc;
|
||||
extern char const* block_downloading_alert_doc;
|
||||
extern char const* storage_moved_alert_doc;
|
||||
extern char const* torrent_deleted_alert_doc;
|
||||
extern char const* torrent_paused_alert_doc;
|
||||
extern char const* torrent_checked_alert_doc;
|
||||
extern char const* url_seed_alert_doc;
|
||||
|
@ -159,6 +160,10 @@ void bind_alert()
|
|||
"storage_moved_alert", storage_moved_alert_doc, no_init
|
||||
);
|
||||
|
||||
class_<torrent_deleted_alert, bases<torrent_alert>, noncopyable>(
|
||||
"torrent_deleted_alert", torrent_deleted_alert_doc, no_init
|
||||
);
|
||||
|
||||
class_<torrent_paused_alert, bases<torrent_alert>, noncopyable>(
|
||||
"torrent_paused_alert", torrent_paused_alert_doc, no_init
|
||||
);
|
||||
|
|
|
@ -270,6 +270,9 @@ char const* storage_moved_alert_doc =
|
|||
"It contains a `torrent_handle` to the torrent in question. This alert\n"
|
||||
"is generated as severity level `alert.severity_levels.warning`.";
|
||||
|
||||
char const* torrent_deleted_alert_doc =
|
||||
"";
|
||||
|
||||
char const* torrent_paused_alert_doc =
|
||||
"This alert is generated when a torrent switches from being a\n"
|
||||
"active to paused.\n"
|
||||
|
|
|
@ -48,6 +48,8 @@ void bind_peer_info()
|
|||
scope pi = class_<peer_info>("peer_info")
|
||||
.def_readonly("flags", &peer_info::flags)
|
||||
.def_readonly("source", &peer_info::source)
|
||||
.def_readonly("read_state", &peer_info::read_state)
|
||||
.def_readonly("write_state", &peer_info::write_state)
|
||||
.add_property("ip", get_ip)
|
||||
.def_readonly("up_speed", &peer_info::up_speed)
|
||||
.def_readonly("down_speed", &peer_info::down_speed)
|
||||
|
@ -66,6 +68,10 @@ void bind_peer_info()
|
|||
.def_readonly("num_hashfails", &peer_info::num_hashfails)
|
||||
#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
|
||||
.add_property("country", get_country)
|
||||
#endif
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
.def_readonly("inet_as_name", &peer_info::inet_as_name)
|
||||
.def_readonly("inet_as", &peer_info::inet_as)
|
||||
#endif
|
||||
.def_readonly("load_balancing", &peer_info::load_balancing)
|
||||
.def_readonly("download_queue_length", &peer_info::download_queue_length)
|
||||
|
@ -79,6 +85,9 @@ void bind_peer_info()
|
|||
.def_readonly("connection_type", &peer_info::connection_type)
|
||||
.def_readonly("remote_dl_rate", &peer_info::remote_dl_rate)
|
||||
.def_readonly("pending_disk_bytes", &peer_info::pending_disk_bytes)
|
||||
.def_readonly("send_quota", &peer_info::send_quota)
|
||||
.def_readonly("receive_quota", &peer_info::receive_quota)
|
||||
.def_readonly("rtt", &peer_info::rtt)
|
||||
;
|
||||
|
||||
// flags
|
||||
|
@ -108,5 +117,11 @@ void bind_peer_info()
|
|||
pi.attr("pex") = (int)peer_info::pex;
|
||||
pi.attr("lsd") = (int)peer_info::lsd;
|
||||
pi.attr("resume_data") = (int)peer_info::resume_data;
|
||||
|
||||
// read/write state
|
||||
pi.attr("bw_idle") = (int)peer_info::bw_idle;
|
||||
pi.attr("bw_torrent") = (int)peer_info::bw_torrent;
|
||||
pi.attr("bw_global") = (int)peer_info::bw_global;
|
||||
pi.attr("bw_network") = (int)peer_info::bw_network;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <libtorrent/extensions.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <libtorrent/peer_request.hpp>
|
||||
#include <libtorrent/disk_buffer_holder.hpp>
|
||||
#include <boost/python.hpp>
|
||||
|
||||
using namespace boost::python;
|
||||
|
@ -144,7 +145,7 @@ namespace
|
|||
return this->peer_plugin::on_request(req);
|
||||
}
|
||||
|
||||
bool on_piece(peer_request const& piece, char const* data)
|
||||
bool on_piece(peer_request const& piece, disk_buffer_holder& data)
|
||||
{
|
||||
if (override f = this->get_override("on_piece"))
|
||||
return f(piece, data);
|
||||
|
@ -152,7 +153,7 @@ namespace
|
|||
return peer_plugin::on_piece(piece, data);
|
||||
}
|
||||
|
||||
bool default_on_piece(peer_request const& piece, char const* data)
|
||||
bool default_on_piece(peer_request const& piece, disk_buffer_holder& data)
|
||||
{
|
||||
return this->peer_plugin::on_piece(piece, data);
|
||||
}
|
||||
|
|
|
@ -106,6 +106,32 @@ namespace
|
|||
return s.add_torrent(ti, save, resume, storage_mode, paused, default_storage_constructor);
|
||||
}
|
||||
|
||||
void start_natpmp(session& s)
|
||||
{
|
||||
allow_threading_guard guard;
|
||||
s.start_natpmp();
|
||||
return;
|
||||
}
|
||||
|
||||
void start_upnp(session& s)
|
||||
{
|
||||
allow_threading_guard guard;
|
||||
s.start_upnp();
|
||||
return;
|
||||
}
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
bool load_asnum_db(session& s, std::string file)
|
||||
{
|
||||
allow_threading_guard guard;
|
||||
return s.load_asnum_db(file.c_str());
|
||||
}
|
||||
|
||||
bool load_country_db(session& s, std::string file)
|
||||
{
|
||||
allow_threading_guard guard;
|
||||
return s.load_country_db(file.c_str());
|
||||
}
|
||||
#endif
|
||||
} // namespace unnamed
|
||||
|
||||
void bind_session()
|
||||
|
@ -250,6 +276,12 @@ void bind_session()
|
|||
.def("set_pe_settings", allow_threads(&session::set_pe_settings), session_set_pe_settings_doc)
|
||||
.def("get_pe_settings", allow_threads(&session::get_pe_settings), return_value_policy<copy_const_reference>())
|
||||
#endif
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
.def("load_asnum_db", &load_asnum_db)
|
||||
.def("load_country_db", &load_country_db)
|
||||
#endif
|
||||
.def("load_state", allow_threads(&session::load_state))
|
||||
.def("state", allow_threads(&session::state))
|
||||
.def(
|
||||
"set_severity_level", allow_threads(&session::set_severity_level)
|
||||
, session_set_severity_level_doc
|
||||
|
@ -259,11 +291,11 @@ void bind_session()
|
|||
.def("set_peer_proxy", allow_threads(&session::set_peer_proxy))
|
||||
.def("set_tracker_proxy", allow_threads(&session::set_tracker_proxy))
|
||||
.def("set_web_seed_proxy", allow_threads(&session::set_web_seed_proxy))
|
||||
.def("start_upnp", allow_threads(&session::start_upnp), session_start_upnp_doc)
|
||||
.def("start_upnp", &start_upnp, session_start_upnp_doc)
|
||||
.def("stop_upnp", allow_threads(&session::stop_upnp), session_stop_upnp_doc)
|
||||
.def("start_lsd", allow_threads(&session::start_lsd), session_start_lsd_doc)
|
||||
.def("stop_lsd", allow_threads(&session::stop_lsd), session_stop_lsd_doc)
|
||||
.def("start_natpmp", allow_threads(&session::start_natpmp), session_start_natpmp_doc)
|
||||
.def("start_natpmp", &start_natpmp, session_start_natpmp_doc)
|
||||
.def("stop_natpmp", allow_threads(&session::stop_natpmp), session_stop_natpmp_doc)
|
||||
.def("set_ip_filter", allow_threads(&session::set_ip_filter), session_set_ip_filter_doc)
|
||||
;
|
||||
|
|
|
@ -291,7 +291,7 @@ void bind_torrent_handle()
|
|||
.def("upload_limit", _(&torrent_handle::upload_limit))
|
||||
.def("set_download_limit", _(&torrent_handle::set_download_limit))
|
||||
.def("download_limit", _(&torrent_handle::download_limit))
|
||||
.def("set_sequenced_download_threshold", _(&torrent_handle::set_sequenced_download_threshold))
|
||||
.def("set_sequential_download", _(&torrent_handle::set_sequential_download))
|
||||
.def("set_peer_upload_limit", set_peer_upload_limit)
|
||||
.def("set_peer_download_limit", set_peer_download_limit)
|
||||
.def("connect_peer", connect_peer)
|
||||
|
@ -301,7 +301,7 @@ void bind_torrent_handle()
|
|||
.def("set_max_connections", _(&torrent_handle::set_max_connections))
|
||||
.def("set_tracker_login", _(&torrent_handle::set_tracker_login))
|
||||
.def("move_storage", _(&torrent_handle::move_storage))
|
||||
.def("info_hash", _(&torrent_handle::info_hash), copy)
|
||||
.def("info_hash", _(&torrent_handle::info_hash))
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
@ -291,6 +291,20 @@ namespace libtorrent
|
|||
{ return std::auto_ptr<alert>(new torrent_deleted_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT save_resume_data_alert: torrent_alert
|
||||
{
|
||||
save_resume_data_alert(boost::shared_ptr<entry> const& rd
|
||||
, torrent_handle const& h, std::string const& msg)
|
||||
: torrent_alert(h, alert::warning, msg)
|
||||
, resume_data(rd)
|
||||
{}
|
||||
|
||||
boost::shared_ptr<entry> resume_data;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new save_resume_data_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT torrent_paused_alert: torrent_alert
|
||||
{
|
||||
torrent_paused_alert(torrent_handle const& h, std::string const& msg)
|
||||
|
@ -331,11 +345,15 @@ namespace libtorrent
|
|||
struct TORRENT_EXPORT file_error_alert: torrent_alert
|
||||
{
|
||||
file_error_alert(
|
||||
const torrent_handle& h
|
||||
std::string const& f
|
||||
, const torrent_handle& h
|
||||
, const std::string& msg)
|
||||
: torrent_alert(h, alert::fatal, msg)
|
||||
, file(f)
|
||||
{}
|
||||
|
||||
std::string file;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new file_error_alert(*this)); }
|
||||
};
|
||||
|
@ -364,6 +382,36 @@ namespace libtorrent
|
|||
{ return std::auto_ptr<alert>(new metadata_received_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT udp_error_alert: alert
|
||||
{
|
||||
udp_error_alert(
|
||||
udp::endpoint const& ep
|
||||
, std::string const& msg)
|
||||
: alert(alert::info, msg)
|
||||
, endpoint(ep)
|
||||
{}
|
||||
|
||||
udp::endpoint endpoint;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new udp_error_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT external_ip_alert: alert
|
||||
{
|
||||
external_ip_alert(
|
||||
address const& ip
|
||||
, std::string const& msg)
|
||||
: alert(alert::info, msg)
|
||||
, external_address(ip)
|
||||
{}
|
||||
|
||||
address external_address;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new external_ip_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT listen_failed_alert: alert
|
||||
{
|
||||
listen_failed_alert(
|
||||
|
@ -396,20 +444,27 @@ namespace libtorrent
|
|||
|
||||
struct TORRENT_EXPORT portmap_error_alert: alert
|
||||
{
|
||||
portmap_error_alert(const std::string& msg)
|
||||
: alert(alert::warning, msg)
|
||||
portmap_error_alert(int i, int t, const std::string& msg)
|
||||
: alert(alert::warning, msg), mapping(i), type(t)
|
||||
{}
|
||||
|
||||
int mapping;
|
||||
int type;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new portmap_error_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT portmap_alert: alert
|
||||
{
|
||||
portmap_alert(const std::string& msg)
|
||||
: alert(alert::info, msg)
|
||||
portmap_alert(int i, int port, int t, const std::string& msg)
|
||||
: alert(alert::info, msg), mapping(i), external_port(port), type(t)
|
||||
{}
|
||||
|
||||
int mapping;
|
||||
int external_port;
|
||||
int type;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new portmap_alert(*this)); }
|
||||
};
|
||||
|
|
|
@ -33,7 +33,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef TORRENT_ASSERT
|
||||
|
||||
#include "libtorrent/config.hpp"
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#ifdef __GNUC__
|
||||
std::string demangle(char const* name);
|
||||
#endif
|
||||
|
||||
#if (defined __linux__ || defined __MACH__) && defined __GNUC__ && !defined(NDEBUG)
|
||||
|
||||
|
|
|
@ -40,6 +40,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <list>
|
||||
#include <deque>
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
#include "libtorrent/GeoIP.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
@ -94,74 +98,7 @@ namespace libtorrent
|
|||
{
|
||||
struct session_impl;
|
||||
|
||||
// this data is shared between the main thread and the
|
||||
// thread that initialize pieces
|
||||
struct piece_checker_data
|
||||
{
|
||||
piece_checker_data()
|
||||
: processing(false), progress(0.f), abort(false) {}
|
||||
|
||||
boost::shared_ptr<torrent> torrent_ptr;
|
||||
fs::path save_path;
|
||||
|
||||
sha1_hash info_hash;
|
||||
|
||||
void parse_resume_data(
|
||||
const entry& rd
|
||||
, const torrent_info& info
|
||||
, std::string& error);
|
||||
|
||||
std::vector<int> piece_map;
|
||||
std::vector<piece_picker::downloading_piece> unfinished_pieces;
|
||||
std::vector<piece_picker::block_info> block_info;
|
||||
std::vector<tcp::endpoint> peers;
|
||||
std::vector<tcp::endpoint> banned_peers;
|
||||
entry resume_data;
|
||||
|
||||
// this is true if this torrent is being processed (checked)
|
||||
// if it is not being processed, then it can be removed from
|
||||
// the queue without problems, otherwise the abort flag has
|
||||
// to be set.
|
||||
bool processing;
|
||||
|
||||
// is filled in by storage::initialize_pieces()
|
||||
// and represents the progress. It should be a
|
||||
// value in the range [0, 1]
|
||||
float progress;
|
||||
|
||||
// abort defaults to false and is typically
|
||||
// filled in by torrent_handle when the user
|
||||
// aborts the torrent
|
||||
bool abort;
|
||||
};
|
||||
|
||||
struct checker_impl: boost::noncopyable
|
||||
{
|
||||
checker_impl(session_impl& s): m_ses(s), m_abort(false) {}
|
||||
void operator()();
|
||||
piece_checker_data* find_torrent(const sha1_hash& info_hash);
|
||||
void remove_torrent(sha1_hash const& info_hash, int options);
|
||||
|
||||
#ifndef NDEBUG
|
||||
void check_invariant() const;
|
||||
#endif
|
||||
|
||||
// when the files has been checked
|
||||
// the torrent is added to the session
|
||||
session_impl& m_ses;
|
||||
|
||||
mutable boost::mutex m_mutex;
|
||||
boost::condition m_cond;
|
||||
|
||||
// a list of all torrents that are currently in queue
|
||||
// or checking their files
|
||||
std::deque<boost::shared_ptr<piece_checker_data> > m_torrents;
|
||||
std::deque<boost::shared_ptr<piece_checker_data> > m_processing;
|
||||
|
||||
bool m_abort;
|
||||
};
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
struct tracker_logger;
|
||||
#endif
|
||||
|
||||
|
@ -185,7 +122,7 @@ namespace libtorrent
|
|||
std::pair<int, int> listen_port_range
|
||||
, fingerprint const& cl_fprint
|
||||
, char const* listen_interface
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
, fs::path const& logpath
|
||||
#endif
|
||||
);
|
||||
|
@ -194,10 +131,18 @@ namespace libtorrent
|
|||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
void add_extension(boost::function<boost::shared_ptr<torrent_plugin>(
|
||||
torrent*, void*)> ext);
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
bool has_peer(peer_connection const* p) const
|
||||
{
|
||||
return std::find_if(m_connections.begin(), m_connections.end()
|
||||
, boost::bind(&boost::intrusive_ptr<peer_connection>::get, _1) == p)
|
||||
!= m_connections.end();
|
||||
}
|
||||
#endif
|
||||
void operator()();
|
||||
|
||||
void open_listen_port() throw();
|
||||
void open_listen_port();
|
||||
|
||||
// if we are listening on an IPv6 interface
|
||||
// this will return one of the IPv6 addresses on this
|
||||
|
@ -216,9 +161,8 @@ namespace libtorrent
|
|||
boost::weak_ptr<torrent> find_torrent(const sha1_hash& info_hash);
|
||||
peer_id const& get_peer_id() const { return m_peer_id; }
|
||||
|
||||
void close_connection(boost::intrusive_ptr<peer_connection> const& p);
|
||||
void connection_failed(boost::intrusive_ptr<peer_connection> const& p
|
||||
, tcp::endpoint const& a, char const* message);
|
||||
void close_connection(peer_connection const* p
|
||||
, char const* message);
|
||||
|
||||
void set_settings(session_settings const& s);
|
||||
session_settings const& settings() const { return m_settings; }
|
||||
|
@ -241,7 +185,8 @@ namespace libtorrent
|
|||
|
||||
// called when a port mapping is successful, or a router returns
|
||||
// a failure to map a port
|
||||
void on_port_mapping(int tcp_port, int udp_port, std::string const& errmsg);
|
||||
void on_port_mapping(int mapping, int port, std::string const& errmsg
|
||||
, int nat_transport);
|
||||
|
||||
bool is_aborted() const { return m_abort; }
|
||||
|
||||
|
@ -277,6 +222,9 @@ namespace libtorrent
|
|||
|
||||
std::vector<torrent_handle> get_torrents();
|
||||
|
||||
void check_torrent(boost::shared_ptr<torrent> const& t);
|
||||
void done_checking(boost::shared_ptr<torrent> const& t);
|
||||
|
||||
void set_severity_level(alert::severity_t s);
|
||||
std::auto_ptr<alert> pop_alert();
|
||||
|
||||
|
@ -334,11 +282,29 @@ namespace libtorrent
|
|||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
void set_dht_proxy(proxy_settings const& s)
|
||||
{ m_dht_proxy = s; }
|
||||
{
|
||||
m_dht_proxy = s;
|
||||
m_dht_socket.set_proxy_settings(s);
|
||||
}
|
||||
proxy_settings const& dht_proxy() const
|
||||
{ return m_dht_proxy; }
|
||||
#endif
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
std::string as_name_for_ip(address const& a);
|
||||
int as_for_ip(address const& a);
|
||||
std::pair<const int, int>* lookup_as(int as);
|
||||
bool load_asnum_db(char const* file);
|
||||
bool has_asnum_db() const { return m_asnum_db; }
|
||||
|
||||
bool load_country_db(char const* file);
|
||||
bool has_country_db() const { return m_country_db; }
|
||||
char const* country_for_ip(address const& a);
|
||||
#endif
|
||||
|
||||
void load_state(entry const& ses_state);
|
||||
entry state() const;
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
void log_buffer_usage()
|
||||
{
|
||||
|
@ -358,29 +324,36 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
void start_lsd();
|
||||
void start_natpmp();
|
||||
void start_upnp();
|
||||
natpmp* start_natpmp();
|
||||
upnp* start_upnp();
|
||||
|
||||
void stop_lsd();
|
||||
void stop_natpmp();
|
||||
void stop_upnp();
|
||||
|
||||
int next_port();
|
||||
|
||||
// handles delayed alerts
|
||||
alert_manager m_alerts;
|
||||
|
||||
std::pair<char*, int> allocate_buffer(int size);
|
||||
void free_buffer(char* buf, int size);
|
||||
|
||||
char* allocate_disk_buffer();
|
||||
void free_disk_buffer(char* buf);
|
||||
|
||||
address m_external_address;
|
||||
|
||||
void set_external_address(address const& ip);
|
||||
address const& external_address() const { return m_external_address; }
|
||||
|
||||
// private:
|
||||
|
||||
void on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih);
|
||||
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
// this pool is used to allocate and recycle send
|
||||
// buffers from.
|
||||
boost::pool<> m_send_buffers;
|
||||
#endif
|
||||
boost::mutex m_send_buffer_mutex;
|
||||
|
||||
// the file pool that all storages in this session's
|
||||
|
@ -391,20 +364,21 @@ namespace libtorrent
|
|||
// when they are destructed.
|
||||
file_pool m_files;
|
||||
|
||||
// this is where all active sockets are stored.
|
||||
// the selector can sleep while there's no activity on
|
||||
// them
|
||||
io_service m_io_service;
|
||||
|
||||
// handles disk io requests asynchronously
|
||||
// peers have pointers into the disk buffer
|
||||
// pool, and must be destructed before this
|
||||
// object. The disk thread relies on the file
|
||||
// pool object, and must be destructed before
|
||||
// m_files.
|
||||
// m_files. The disk io thread posts completion
|
||||
// events to the io service, and needs to be
|
||||
// constructed after it.
|
||||
disk_io_thread m_disk_thread;
|
||||
|
||||
// this is where all active sockets are stored.
|
||||
// the selector can sleep while there's no activity on
|
||||
// them
|
||||
io_service m_io_service;
|
||||
asio::strand m_strand;
|
||||
|
||||
// this is a list of half-open tcp connections
|
||||
// (only outgoing connections)
|
||||
// this has to be one of the last
|
||||
|
@ -422,6 +396,7 @@ namespace libtorrent
|
|||
|
||||
tracker_manager m_tracker_manager;
|
||||
torrent_map m_torrents;
|
||||
std::list<boost::shared_ptr<torrent> > m_queued_for_checking;
|
||||
|
||||
// this maps sockets to their peer_connection
|
||||
// object. It is the complete list of all connected
|
||||
|
@ -496,7 +471,14 @@ namespace libtorrent
|
|||
// should exit
|
||||
volatile bool m_abort;
|
||||
|
||||
// the max number of unchoked peers as set by the user
|
||||
int m_max_uploads;
|
||||
|
||||
// the number of unchoked peers as set by the auto-unchoker
|
||||
// this should always be >= m_max_uploads
|
||||
int m_allowed_upload_slots;
|
||||
|
||||
// the max number of connections, as set by the user
|
||||
int m_max_connections;
|
||||
|
||||
// the number of unchoked peers
|
||||
|
@ -533,6 +515,10 @@ namespace libtorrent
|
|||
void second_tick(asio::error_code const& e);
|
||||
ptime m_last_tick;
|
||||
|
||||
// when outgoing_ports is configured, this is the
|
||||
// port we'll bind the next outgoing socket to
|
||||
int m_next_port;
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
boost::intrusive_ptr<dht::dht_tracker> m_dht;
|
||||
dht_settings m_dht_settings;
|
||||
|
@ -545,6 +531,11 @@ namespace libtorrent
|
|||
// see m_external_listen_port. This is the same
|
||||
// but for the udp port used by the DHT.
|
||||
int m_external_udp_port;
|
||||
|
||||
udp_socket m_dht_socket;
|
||||
|
||||
void on_receive_udp(asio::error_code const& e
|
||||
, udp::endpoint const& ep, char const* buf, int len);
|
||||
#endif
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
|
@ -555,6 +546,10 @@ namespace libtorrent
|
|||
boost::intrusive_ptr<upnp> m_upnp;
|
||||
boost::intrusive_ptr<lsd> m_lsd;
|
||||
|
||||
// 0 is natpmp 1 is upnp
|
||||
int m_tcp_mapping[2];
|
||||
int m_udp_mapping[2];
|
||||
|
||||
// the timer used to fire the second_tick
|
||||
deadline_timer m_timer;
|
||||
|
||||
|
@ -575,7 +570,7 @@ namespace libtorrent
|
|||
// the number of send buffers that are allocated
|
||||
int m_buffer_allocations;
|
||||
#endif
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
boost::shared_ptr<logger> create_log(std::string const& name
|
||||
, int instance, bool append = true);
|
||||
|
||||
|
@ -588,7 +583,9 @@ namespace libtorrent
|
|||
public:
|
||||
boost::shared_ptr<logger> m_logger;
|
||||
private:
|
||||
|
||||
#endif
|
||||
address m_external_address;
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
typedef std::list<boost::function<boost::shared_ptr<
|
||||
|
@ -597,19 +594,22 @@ namespace libtorrent
|
|||
extension_list_t m_extensions;
|
||||
#endif
|
||||
|
||||
// data shared between the main thread
|
||||
// and the checker thread
|
||||
checker_impl m_checker_impl;
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
GeoIP* m_asnum_db;
|
||||
GeoIP* m_country_db;
|
||||
|
||||
// maps AS number to the peak download rate
|
||||
// we've seen from it. Entries are never removed
|
||||
// from this map. Pointers to its elements
|
||||
// are kept in the policy::peer structures.
|
||||
std::map<int, int> m_as_peak;
|
||||
#endif
|
||||
|
||||
// the main working thread
|
||||
boost::scoped_ptr<boost::thread> m_thread;
|
||||
|
||||
// the thread that calls initialize_pieces()
|
||||
// on all torrents before they start downloading
|
||||
boost::scoped_ptr<boost::thread> m_checker_thread;
|
||||
};
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
struct tracker_logger : request_callback
|
||||
{
|
||||
tracker_logger(session_impl& ses): m_ses(ses) {}
|
||||
|
@ -622,7 +622,8 @@ namespace libtorrent
|
|||
, std::vector<peer_entry>& peers
|
||||
, int interval
|
||||
, int complete
|
||||
, int incomplete)
|
||||
, int incomplete
|
||||
, address const& external_ip)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "TRACKER RESPONSE:\n"
|
||||
|
@ -636,6 +637,7 @@ namespace libtorrent
|
|||
if (!i->pid.is_all_zeros()) s << " " << i->pid;
|
||||
s << "\n";
|
||||
}
|
||||
s << "external ip: " << external_ip << "\n";
|
||||
debug_log(s.str());
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <boost/thread/mutex.hpp>
|
||||
#include <deque>
|
||||
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/invariant_check.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
@ -52,7 +56,6 @@ using boost::shared_ptr;
|
|||
using boost::intrusive_ptr;
|
||||
using boost::bind;
|
||||
|
||||
//#define TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
|
@ -80,7 +83,7 @@ struct history_entry
|
|||
};
|
||||
|
||||
template<class T>
|
||||
T clamp(T val, T ceiling, T floor) throw()
|
||||
T clamp(T val, T ceiling, T floor)
|
||||
{
|
||||
TORRENT_ASSERT(ceiling >= floor);
|
||||
if (val >= ceiling) return ceiling;
|
||||
|
@ -88,10 +91,23 @@ T clamp(T val, T ceiling, T floor) throw()
|
|||
return val;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct assign_at_exit
|
||||
{
|
||||
assign_at_exit(T& var, T val): var_(var), val_(val) {}
|
||||
~assign_at_exit() { var_ = val_; }
|
||||
T& var_;
|
||||
T val_;
|
||||
};
|
||||
|
||||
template<class PeerConnection, class Torrent>
|
||||
struct bandwidth_manager
|
||||
{
|
||||
bandwidth_manager(io_service& ios, int channel) throw()
|
||||
bandwidth_manager(io_service& ios, int channel
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
, bool log = false
|
||||
#endif
|
||||
)
|
||||
: m_ios(ios)
|
||||
, m_history_timer(m_ios)
|
||||
, m_limit(bandwidth_limit::inf)
|
||||
|
@ -99,16 +115,22 @@ struct bandwidth_manager
|
|||
, m_channel(channel)
|
||||
, m_in_hand_out_bandwidth(false)
|
||||
, m_abort(false)
|
||||
{}
|
||||
{
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
if (log)
|
||||
m_log.open("bandwidth_limiter.log", std::ios::trunc);
|
||||
m_start = time_now();
|
||||
#endif
|
||||
}
|
||||
|
||||
void throttle(int limit) throw()
|
||||
void throttle(int limit)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
TORRENT_ASSERT(limit >= 0);
|
||||
m_limit = limit;
|
||||
}
|
||||
|
||||
int throttle() const throw()
|
||||
int throttle() const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return m_limit;
|
||||
|
@ -124,6 +146,23 @@ struct bandwidth_manager
|
|||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool is_queued(PeerConnection const* peer) const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return is_queued(peer);
|
||||
}
|
||||
|
||||
bool is_queued(PeerConnection const* peer, boost::mutex::scoped_lock& l) const
|
||||
{
|
||||
TORRENT_ASSERT(l.locked());
|
||||
for (typename queue_t::const_iterator i = m_queue.begin()
|
||||
, end(m_queue.end()); i != end; ++i)
|
||||
{
|
||||
if (i->peer.get() == peer) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_in_history(PeerConnection const* peer) const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
@ -142,41 +181,35 @@ struct bandwidth_manager
|
|||
}
|
||||
#endif
|
||||
|
||||
int queue_size() const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
int queue_size() const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
// non prioritized means that, if there's a line for bandwidth,
|
||||
// others will cut in front of the non-prioritized peers.
|
||||
// this is used by web seeds
|
||||
void request_bandwidth(intrusive_ptr<PeerConnection> peer
|
||||
, int blk, int priority)
|
||||
void request_bandwidth(intrusive_ptr<PeerConnection> const& peer
|
||||
, int blk, int priority)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
INVARIANT_CHECK;
|
||||
if (m_abort) return;
|
||||
TORRENT_ASSERT(blk > 0);
|
||||
TORRENT_ASSERT(!is_queued(peer.get(), l));
|
||||
|
||||
// make sure this peer isn't already in line
|
||||
// waiting for bandwidth
|
||||
#ifndef NDEBUG
|
||||
for (typename queue_t::iterator i = m_queue.begin()
|
||||
, end(m_queue.end()); i != end; ++i)
|
||||
{
|
||||
TORRENT_ASSERT(i->peer < peer || peer < i->peer);
|
||||
}
|
||||
#endif
|
||||
TORRENT_ASSERT(peer->max_assignable_bandwidth(m_channel) > 0);
|
||||
TORRENT_ASSERT(peer->max_assignable_bandwidth(m_channel) > 0);
|
||||
|
||||
typename queue_t::reverse_iterator i(m_queue.rbegin());
|
||||
while (i != m_queue.rend() && priority > i->priority)
|
||||
{
|
||||
++i->priority;
|
||||
++i;
|
||||
}
|
||||
m_queue.insert(i.base(), bw_queue_entry<PeerConnection, Torrent>(peer, blk, priority));
|
||||
typename queue_t::reverse_iterator i(m_queue.rbegin());
|
||||
while (i != m_queue.rend() && priority > i->priority)
|
||||
{
|
||||
++i->priority;
|
||||
++i;
|
||||
}
|
||||
m_queue.insert(i.base(), bw_queue_entry<PeerConnection, Torrent>(peer, blk, priority));
|
||||
if (!m_queue.empty()) hand_out_bandwidth(l);
|
||||
}
|
||||
|
||||
|
@ -206,25 +239,32 @@ private:
|
|||
|
||||
void add_history_entry(history_entry<PeerConnection, Torrent> const& e)
|
||||
{
|
||||
try {
|
||||
INVARIANT_CHECK;
|
||||
|
||||
m_history.push_front(e);
|
||||
m_current_quota += e.amount;
|
||||
// in case the size > 1 there is already a timer
|
||||
// active that will be invoked, no need to set one up
|
||||
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
m_log << std::setw(7) << total_milliseconds(time_now() - m_start) << " + "
|
||||
" queue: " << std::setw(3) << m_queue.size()
|
||||
<< " used: " << std::setw(7) << m_current_quota
|
||||
<< " limit: " << std::setw(7) << m_limit
|
||||
<< " history: " << std::setw(3) << m_history.size()
|
||||
<< std::endl;
|
||||
#endif
|
||||
if (m_history.size() > 1) return;
|
||||
|
||||
if (m_abort) return;
|
||||
|
||||
m_history_timer.expires_at(e.expires_at);
|
||||
asio::error_code ec;
|
||||
m_history_timer.expires_at(e.expires_at, ec);
|
||||
m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1));
|
||||
}
|
||||
catch (std::exception&) {}
|
||||
}
|
||||
|
||||
void on_history_expire(asio::error_code const& e)
|
||||
{
|
||||
try {
|
||||
if (e) return;
|
||||
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
@ -240,6 +280,15 @@ private:
|
|||
m_history.pop_back();
|
||||
m_current_quota -= e.amount;
|
||||
TORRENT_ASSERT(m_current_quota >= 0);
|
||||
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
m_log << std::setw(7) << total_milliseconds(time_now() - m_start) << " - "
|
||||
" queue: " << std::setw(3) << m_queue.size()
|
||||
<< " used: " << std::setw(7) << m_current_quota
|
||||
<< " limit: " << std::setw(7) << m_limit
|
||||
<< " history: " << std::setw(3) << m_history.size()
|
||||
<< std::endl;
|
||||
#endif
|
||||
intrusive_ptr<PeerConnection> c = e.peer;
|
||||
shared_ptr<Torrent> t = e.tor.lock();
|
||||
l.unlock();
|
||||
|
@ -251,7 +300,8 @@ private:
|
|||
// now, wait for the next chunk to expire
|
||||
if (!m_history.empty() && !m_abort)
|
||||
{
|
||||
m_history_timer.expires_at(m_history.back().expires_at);
|
||||
asio::error_code ec;
|
||||
m_history_timer.expires_at(m_history.back().expires_at, ec);
|
||||
m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1));
|
||||
}
|
||||
|
||||
|
@ -259,8 +309,6 @@ private:
|
|||
// means we can hand out more (in case there
|
||||
// are still consumers in line)
|
||||
if (!m_queue.empty()) hand_out_bandwidth(l);
|
||||
}
|
||||
catch (std::exception&) {}
|
||||
}
|
||||
|
||||
void hand_out_bandwidth(boost::mutex::scoped_lock& l)
|
||||
|
@ -270,8 +318,9 @@ private:
|
|||
// to the loop further down on the callstack
|
||||
if (m_in_hand_out_bandwidth) return;
|
||||
m_in_hand_out_bandwidth = true;
|
||||
// set it to false when exiting function
|
||||
assign_at_exit<bool> sg(m_in_hand_out_bandwidth, false);
|
||||
|
||||
try {
|
||||
INVARIANT_CHECK;
|
||||
|
||||
ptime now(time_now());
|
||||
|
@ -281,18 +330,7 @@ private:
|
|||
// available bandwidth to hand out
|
||||
int amount = limit - m_current_quota;
|
||||
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
std::cerr << " hand_out_bandwidht. m_queue.size() = " << m_queue.size()
|
||||
<< " amount = " << amount
|
||||
<< " limit = " << limit
|
||||
<< " m_current_quota = " << m_current_quota << std::endl;
|
||||
#endif
|
||||
|
||||
if (amount <= 0)
|
||||
{
|
||||
m_in_hand_out_bandwidth = false;
|
||||
return;
|
||||
}
|
||||
if (amount <= 0) return;
|
||||
|
||||
queue_t tmp;
|
||||
while (!m_queue.empty() && amount > 0)
|
||||
|
@ -358,9 +396,12 @@ private:
|
|||
}
|
||||
if (block_size > qe.max_block_size) block_size = qe.max_block_size;
|
||||
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
std::cerr << " block_size = " << block_size << " amount = " << amount << std::endl;
|
||||
#endif
|
||||
if (amount < block_size / 4)
|
||||
{
|
||||
tmp.push_back(qe);
|
||||
// m_queue.push_front(qe);
|
||||
break;
|
||||
}
|
||||
|
||||
// so, hand out max_assignable, but no more than
|
||||
// the available bandwidth (amount) and no more
|
||||
|
@ -377,14 +418,7 @@ private:
|
|||
add_history_entry(history_entry<PeerConnection, Torrent>(
|
||||
qe.peer, t, hand_out_amount, now + bw_window_size));
|
||||
}
|
||||
if (!tmp.empty()) m_queue.insert(m_queue.begin(), tmp.begin(), tmp.end());
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
m_in_hand_out_bandwidth = false;
|
||||
throw;
|
||||
}
|
||||
m_in_hand_out_bandwidth = false;
|
||||
if (!tmp.empty()) m_queue.insert(m_queue.begin(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
|
||||
|
@ -423,6 +457,11 @@ private:
|
|||
bool m_in_hand_out_bandwidth;
|
||||
|
||||
bool m_abort;
|
||||
|
||||
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
|
||||
std::ofstream m_log;
|
||||
ptime m_start;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -103,28 +103,35 @@ namespace libtorrent
|
|||
namespace detail
|
||||
{
|
||||
template <class OutIt>
|
||||
void write_string(OutIt& out, const std::string& val)
|
||||
int write_string(OutIt& out, const std::string& val)
|
||||
{
|
||||
std::string::const_iterator end = val.begin() + val.length();
|
||||
std::copy(val.begin(), end, out);
|
||||
int ret = val.length();
|
||||
std::string::const_iterator end = val.begin() + ret;
|
||||
for (std::string::const_iterator i = val.begin()
|
||||
, end(val.begin() + ret); i != end; ++i)
|
||||
*out++ = *i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val);
|
||||
|
||||
template <class OutIt>
|
||||
void write_integer(OutIt& out, entry::integer_type val)
|
||||
int write_integer(OutIt& out, entry::integer_type val)
|
||||
{
|
||||
// the stack allocated buffer for keeping the
|
||||
// decimal representation of the number can
|
||||
// not hold number bigger than this:
|
||||
BOOST_STATIC_ASSERT(sizeof(entry::integer_type) <= 8);
|
||||
char buf[21];
|
||||
int ret = 0;
|
||||
for (char const* str = integer_to_str(buf, 21, val);
|
||||
*str != 0; ++str)
|
||||
{
|
||||
*out = *str;
|
||||
++out;
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class OutIt>
|
||||
|
@ -172,26 +179,31 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
// returns the number of bytes written
|
||||
template<class OutIt>
|
||||
void bencode_recursive(OutIt& out, const entry& e)
|
||||
int bencode_recursive(OutIt& out, const entry& e)
|
||||
{
|
||||
int ret = 0;
|
||||
switch(e.type())
|
||||
{
|
||||
case entry::int_t:
|
||||
write_char(out, 'i');
|
||||
write_integer(out, e.integer());
|
||||
ret += write_integer(out, e.integer());
|
||||
write_char(out, 'e');
|
||||
ret += 2;
|
||||
break;
|
||||
case entry::string_t:
|
||||
write_integer(out, e.string().length());
|
||||
ret += write_integer(out, e.string().length());
|
||||
write_char(out, ':');
|
||||
write_string(out, e.string());
|
||||
ret += write_string(out, e.string());
|
||||
ret += 1;
|
||||
break;
|
||||
case entry::list_t:
|
||||
write_char(out, 'l');
|
||||
for (entry::list_type::const_iterator i = e.list().begin(); i != e.list().end(); ++i)
|
||||
bencode_recursive(out, *i);
|
||||
ret += bencode_recursive(out, *i);
|
||||
write_char(out, 'e');
|
||||
ret += 2;
|
||||
break;
|
||||
case entry::dictionary_t:
|
||||
write_char(out, 'd');
|
||||
|
@ -199,18 +211,21 @@ namespace libtorrent
|
|||
i != e.dict().end(); ++i)
|
||||
{
|
||||
// write key
|
||||
write_integer(out, i->first.length());
|
||||
ret += write_integer(out, i->first.length());
|
||||
write_char(out, ':');
|
||||
write_string(out, i->first);
|
||||
ret += write_string(out, i->first);
|
||||
// write value
|
||||
bencode_recursive(out, i->second);
|
||||
ret += bencode_recursive(out, i->second);
|
||||
ret += 1;
|
||||
}
|
||||
write_char(out, 'e');
|
||||
ret += 2;
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class InIt>
|
||||
|
@ -225,6 +240,9 @@ namespace libtorrent
|
|||
if (in == end)
|
||||
{
|
||||
err = true;
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
switch (*in)
|
||||
|
@ -241,6 +259,9 @@ namespace libtorrent
|
|||
++in; // 'e'
|
||||
ret = entry(entry::int_t);
|
||||
ret.integer() = boost::lexical_cast<entry::integer_type>(val);
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
} break;
|
||||
|
||||
// ----------------------------------------------
|
||||
|
@ -254,13 +275,25 @@ namespace libtorrent
|
|||
ret.list().push_back(entry());
|
||||
entry& e = ret.list().back();
|
||||
bdecode_recursive(in, end, e, err, depth + 1);
|
||||
if (err) return;
|
||||
if (err)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (in == end)
|
||||
{
|
||||
err = true;
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
TORRENT_ASSERT(*in == 'e');
|
||||
++in; // 'e'
|
||||
} break;
|
||||
|
@ -275,16 +308,34 @@ namespace libtorrent
|
|||
{
|
||||
entry key;
|
||||
bdecode_recursive(in, end, key, err, depth + 1);
|
||||
if (err) return;
|
||||
if (err || key.type() != entry::string_t)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
entry& e = ret[key.string()];
|
||||
bdecode_recursive(in, end, e, err, depth + 1);
|
||||
if (err) return;
|
||||
if (err)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (in == end)
|
||||
{
|
||||
err = true;
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
TORRENT_ASSERT(*in == 'e');
|
||||
++in; // 'e'
|
||||
} break;
|
||||
|
@ -295,27 +346,45 @@ namespace libtorrent
|
|||
if (isdigit((unsigned char)*in))
|
||||
{
|
||||
std::string len_s = read_until(in, end, ':', err);
|
||||
if (err) return;
|
||||
if (err)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
TORRENT_ASSERT(*in == ':');
|
||||
++in; // ':'
|
||||
int len = std::atoi(len_s.c_str());
|
||||
ret = entry(entry::string_t);
|
||||
read_string(in, end, len, ret.string(), err);
|
||||
if (err) return;
|
||||
if (err)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err = true;
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
ret.m_type_queried = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class OutIt>
|
||||
void bencode(OutIt out, const entry& e)
|
||||
int bencode(OutIt out, const entry& e)
|
||||
{
|
||||
detail::bencode_recursive(out, e);
|
||||
return detail::bencode_recursive(out, e);
|
||||
}
|
||||
|
||||
template<class InIt>
|
||||
|
@ -324,6 +393,7 @@ namespace libtorrent
|
|||
entry e;
|
||||
bool err = false;
|
||||
detail::bdecode_recursive(start, end, e, err, 0);
|
||||
TORRENT_ASSERT(e.m_type_queried == false);
|
||||
if (err)
|
||||
{
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
|
@ -335,7 +405,25 @@ namespace libtorrent
|
|||
return e;
|
||||
}
|
||||
|
||||
template<class InIt>
|
||||
entry bdecode(InIt start, InIt end, int& len)
|
||||
{
|
||||
entry e;
|
||||
bool err = false;
|
||||
InIt s = start;
|
||||
detail::bdecode_recursive(start, end, e, err, 0);
|
||||
len = std::distance(s, start);
|
||||
TORRENT_ASSERT(len >= 0);
|
||||
if (err)
|
||||
{
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return entry();
|
||||
#else
|
||||
throw invalid_encoding();
|
||||
#endif
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TORRENT_BENCODE_HPP_INCLUDED
|
||||
|
||||
|
|
|
@ -74,12 +74,29 @@ namespace libtorrent
|
|||
boost::shared_ptr<datagram_socket> socket;
|
||||
char buffer[1024];
|
||||
udp::endpoint remote;
|
||||
void close()
|
||||
{
|
||||
if (!socket) return;
|
||||
asio::error_code ec;
|
||||
socket->close(ec);
|
||||
}
|
||||
};
|
||||
|
||||
void on_receive(socket_entry* s, asio::error_code const& ec
|
||||
, std::size_t bytes_transferred);
|
||||
void open_unicast_socket(io_service& ios, address const& addr);
|
||||
void open_multicast_socket(io_service& ios, address const& addr
|
||||
, bool loopback);
|
||||
|
||||
// these sockets are used to
|
||||
// join the multicast group (on each interface)
|
||||
// and receive multicast messages
|
||||
std::list<socket_entry> m_sockets;
|
||||
// these sockets are not bound to any
|
||||
// specific port and are used to
|
||||
// send messages to the multicast group
|
||||
// and receive unicast responses
|
||||
std::list<socket_entry> m_unicast_sockets;
|
||||
udp::endpoint m_multicast_endpoint;
|
||||
receive_handler_t m_on_receive;
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ namespace libtorrent
|
|||
, boost::shared_ptr<socket_type> s
|
||||
, policy::peer* peerinfo);
|
||||
|
||||
void start();
|
||||
|
||||
~bt_peer_connection();
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
|
@ -208,7 +210,7 @@ namespace libtorrent
|
|||
void write_cancel(peer_request const& r);
|
||||
void write_bitfield(std::vector<bool> const& bitfield);
|
||||
void write_have(int index);
|
||||
void write_piece(peer_request const& r, char* buffer);
|
||||
void write_piece(peer_request const& r, disk_buffer_holder& buffer);
|
||||
void write_handshake();
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
void write_extensions();
|
||||
|
@ -267,6 +269,8 @@ namespace libtorrent
|
|||
// initializes m_RC4_handler
|
||||
void init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key);
|
||||
|
||||
public:
|
||||
|
||||
// these functions encrypt the send buffer if m_rc4_encrypted
|
||||
// is true, otherwise it passes the call to the
|
||||
// peer_connection functions of the same names
|
||||
|
@ -283,6 +287,8 @@ namespace libtorrent
|
|||
}
|
||||
void setup_send();
|
||||
|
||||
private:
|
||||
|
||||
// Returns offset at which bytestream (src, src + src_size)
|
||||
// matches bytestream(target, target + target_size).
|
||||
// If no sync found, return -1
|
||||
|
|
|
@ -44,6 +44,11 @@ class buffer
|
|||
public:
|
||||
struct interval
|
||||
{
|
||||
interval()
|
||||
: begin(0)
|
||||
, end(0)
|
||||
{}
|
||||
|
||||
interval(char* begin, char* end)
|
||||
: begin(begin)
|
||||
, end(end)
|
||||
|
|
|
@ -40,6 +40,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/time.hpp"
|
||||
|
||||
#ifdef TORRENT_CONNECTION_LOGGING
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
|
@ -48,20 +52,20 @@ class connection_queue : public boost::noncopyable
|
|||
public:
|
||||
connection_queue(io_service& ios);
|
||||
|
||||
bool free_slots() const;
|
||||
// if there are no free slots, returns the negative
|
||||
// number of queued up connections
|
||||
int free_slots() const;
|
||||
|
||||
void enqueue(boost::function<void(int)> const& on_connect
|
||||
, boost::function<void()> const& on_timeout
|
||||
, time_duration timeout);
|
||||
, time_duration timeout, int priority = 0);
|
||||
void done(int ticket);
|
||||
void limit(int limit);
|
||||
int limit() const;
|
||||
void close();
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
void check_invariant() const;
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
@ -71,7 +75,7 @@ private:
|
|||
|
||||
struct entry
|
||||
{
|
||||
entry(): connecting(false), ticket(0), expires(max_time()) {}
|
||||
entry(): connecting(false), ticket(0), expires(max_time()), priority(0) {}
|
||||
// called when the connection is initiated
|
||||
boost::function<void(int)> on_connect;
|
||||
// called if done hasn't been called within the timeout
|
||||
|
@ -80,6 +84,7 @@ private:
|
|||
int ticket;
|
||||
ptime expires;
|
||||
time_duration timeout;
|
||||
int priority;
|
||||
};
|
||||
|
||||
std::list<entry> m_queue;
|
||||
|
@ -97,6 +102,9 @@ private:
|
|||
#ifndef NDEBUG
|
||||
bool m_in_timeout_function;
|
||||
#endif
|
||||
#ifdef TORRENT_CONNECTION_LOGGING
|
||||
std::ofstream m_log;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -60,17 +60,21 @@ namespace libtorrent
|
|||
{
|
||||
logger(fs::path const& logpath, fs::path const& filename, int instance, bool append = true)
|
||||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
#endif
|
||||
fs::path dir(fs::complete(logpath / ("libtorrent_logs" + boost::lexical_cast<std::string>(instance))));
|
||||
if (!fs::exists(dir)) fs::create_directories(dir);
|
||||
m_file.open((dir / filename).string().c_str(), std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out));
|
||||
*this << "\n\n\n*** starting log ***\n";
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "failed to create log '" << filename.string() << "': " << e.what() << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
|
|
@ -42,14 +42,24 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <boost/function.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/pool/pool.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_array.hpp>
|
||||
#include <list>
|
||||
#include "libtorrent/config.hpp"
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
#include <boost/pool/pool.hpp>
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
struct cached_piece_info
|
||||
{
|
||||
int piece;
|
||||
std::vector<bool> blocks;
|
||||
ptime last_use;
|
||||
};
|
||||
|
||||
struct disk_io_job
|
||||
{
|
||||
disk_io_job()
|
||||
|
@ -69,6 +79,9 @@ namespace libtorrent
|
|||
, move_storage
|
||||
, release_files
|
||||
, delete_files
|
||||
, check_fastresume
|
||||
, check_files
|
||||
, save_resume_data
|
||||
};
|
||||
|
||||
action_t action;
|
||||
|
@ -82,21 +95,61 @@ namespace libtorrent
|
|||
// to the error message
|
||||
std::string str;
|
||||
|
||||
// on error, this is set to the path of the
|
||||
// file the disk operation failed on
|
||||
std::string error_file;
|
||||
|
||||
// priority decides whether or not this
|
||||
// job will skip entries in the queue or
|
||||
// not. It always skips in front of entries
|
||||
// with lower priority
|
||||
int priority;
|
||||
|
||||
boost::shared_ptr<entry> resume_data;
|
||||
|
||||
// this is called when operation completes
|
||||
boost::function<void(int, disk_io_job const&)> callback;
|
||||
};
|
||||
|
||||
struct cache_status
|
||||
{
|
||||
cache_status()
|
||||
: blocks_written(0)
|
||||
, writes(0)
|
||||
, blocks_read(0)
|
||||
, blocks_read_hit(0)
|
||||
, reads(0)
|
||||
, cache_size(0)
|
||||
, read_cache_size(0)
|
||||
{}
|
||||
|
||||
// the number of 16kB blocks written
|
||||
size_type blocks_written;
|
||||
// the number of write operations used
|
||||
size_type writes;
|
||||
// (blocks_written - writes) / blocks_written represents the
|
||||
// "cache hit" ratio in the write cache
|
||||
// the number of blocks read
|
||||
|
||||
// the number of blocks passed back to the bittorrent engine
|
||||
size_type blocks_read;
|
||||
// the number of blocks that was just copied from the read cache
|
||||
size_type blocks_read_hit;
|
||||
// the number of read operations used
|
||||
size_type reads;
|
||||
|
||||
// the number of blocks in the cache (both read and write)
|
||||
int cache_size;
|
||||
|
||||
// the number of blocks in the cache used for read cache
|
||||
int read_cache_size;
|
||||
};
|
||||
|
||||
// this is a singleton consisting of the thread and a queue
|
||||
// of disk io jobs
|
||||
struct disk_io_thread : boost::noncopyable
|
||||
{
|
||||
disk_io_thread(int block_size = 16 * 1024);
|
||||
disk_io_thread(asio::io_service& ios, int block_size = 16 * 1024);
|
||||
~disk_io_thread();
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
|
@ -112,10 +165,6 @@ namespace libtorrent
|
|||
, boost::function<void(int, disk_io_job const&)> const& f
|
||||
= boost::function<void(int, disk_io_job const&)>());
|
||||
|
||||
#ifndef NDEBUG
|
||||
disk_io_job find_job(boost::intrusive_ptr<piece_manager> s
|
||||
, int action, int piece) const;
|
||||
#endif
|
||||
// keep track of the number of bytes in the job queue
|
||||
// at any given time. i.e. the sum of all buffer_size.
|
||||
// this is used to slow down the download global download
|
||||
|
@ -123,28 +172,116 @@ namespace libtorrent
|
|||
size_type queue_buffer_size() const
|
||||
{ return m_queue_buffer_size; }
|
||||
|
||||
void get_cache_info(sha1_hash const& ih
|
||||
, std::vector<cached_piece_info>& ret) const;
|
||||
|
||||
cache_status status() const;
|
||||
void set_cache_size(int s);
|
||||
void set_cache_expiry(int ex);
|
||||
|
||||
void operator()();
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool is_disk_buffer(char* buffer) const;
|
||||
#endif
|
||||
|
||||
char* allocate_buffer();
|
||||
void free_buffer(char* buf);
|
||||
|
||||
#ifndef NDEBUG
|
||||
void check_invariant() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
struct cached_piece_entry
|
||||
{
|
||||
int piece;
|
||||
// storage this piece belongs to
|
||||
boost::intrusive_ptr<piece_manager> storage;
|
||||
// the last time a block was writting to this piece
|
||||
ptime last_use;
|
||||
// the number of blocks in the cache for this piece
|
||||
int num_blocks;
|
||||
// the pointers to the block data
|
||||
boost::shared_array<char*> blocks;
|
||||
};
|
||||
|
||||
typedef boost::recursive_mutex mutex_t;
|
||||
typedef std::list<cached_piece_entry> cache_t;
|
||||
|
||||
char* allocate_buffer(mutex_t::scoped_lock& l);
|
||||
void free_buffer(char* buf, mutex_t::scoped_lock& l);
|
||||
|
||||
// cache operations
|
||||
cache_t::iterator find_cached_piece(
|
||||
cache_t& cache, disk_io_job const& j
|
||||
, mutex_t::scoped_lock& l);
|
||||
|
||||
// write cache operations
|
||||
void flush_oldest_piece(mutex_t::scoped_lock& l);
|
||||
void flush_expired_pieces(mutex_t::scoped_lock& l);
|
||||
void flush_and_remove(cache_t::iterator i, mutex_t::scoped_lock& l);
|
||||
void flush(cache_t::iterator i, mutex_t::scoped_lock& l);
|
||||
void cache_block(disk_io_job& j, mutex_t::scoped_lock& l);
|
||||
|
||||
// read cache operations
|
||||
bool clear_oldest_read_piece(cache_t::iterator ignore
|
||||
, mutex_t::scoped_lock& l);
|
||||
int read_into_piece(cached_piece_entry& p, int start_block, mutex_t::scoped_lock& l);
|
||||
int cache_read_block(disk_io_job const& j, mutex_t::scoped_lock& l);
|
||||
void free_piece(cached_piece_entry& p, mutex_t::scoped_lock& l);
|
||||
bool make_room(int num_blocks
|
||||
, cache_t::iterator ignore
|
||||
, mutex_t::scoped_lock& l);
|
||||
int try_read_from_cache(disk_io_job const& j, mutex_t::scoped_lock& l);
|
||||
|
||||
mutable mutex_t m_mutex;
|
||||
boost::condition m_signal;
|
||||
bool m_abort;
|
||||
std::list<disk_io_job> m_jobs;
|
||||
size_type m_queue_buffer_size;
|
||||
|
||||
// memory pool for read and write operations
|
||||
boost::pool<> m_pool;
|
||||
// write cache
|
||||
cache_t m_pieces;
|
||||
|
||||
// read cache
|
||||
cache_t m_read_pieces;
|
||||
|
||||
#ifndef NDEBUG
|
||||
int m_block_size;
|
||||
disk_io_job m_current;
|
||||
// total number of blocks in use by both the read
|
||||
// and the write cache. This is not supposed to
|
||||
// exceed m_cache_size
|
||||
cache_status m_cache_stats;
|
||||
int m_num_cached_blocks;
|
||||
|
||||
// in (16kB) blocks
|
||||
int m_cache_size;
|
||||
|
||||
// expiration time of cache entries in seconds
|
||||
int m_cache_expiry;
|
||||
|
||||
// if set to true, each piece flush will allocate
|
||||
// one piece worth of temporary memory on the heap
|
||||
// and copy the block data into it, and then perform
|
||||
// a single write operation from that buffer.
|
||||
// if memory is constrained, that temporary buffer
|
||||
// might is avoided by setting this to false.
|
||||
// in case the allocation fails, the piece flush
|
||||
// falls back to writing each block separately.
|
||||
bool m_coalesce_writes;
|
||||
bool m_coalesce_reads;
|
||||
bool m_use_read_cache;
|
||||
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
// memory pool for read and write operations
|
||||
// and disk cache
|
||||
boost::pool<> m_pool;
|
||||
#endif
|
||||
|
||||
// number of bytes per block. The BitTorrent
|
||||
// protocol defines the block size to 16 KiB.
|
||||
int m_block_size;
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
std::ofstream m_log;
|
||||
#endif
|
||||
|
@ -152,6 +289,11 @@ namespace libtorrent
|
|||
int m_allocations;
|
||||
#endif
|
||||
|
||||
size_type m_writes;
|
||||
size_type m_blocks_written;
|
||||
|
||||
asio::io_service& m_ios;
|
||||
|
||||
// thread for performing blocking disk io operations
|
||||
boost::thread m_disk_io_thread;
|
||||
};
|
||||
|
|
|
@ -204,6 +204,16 @@ namespace libtorrent
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
public:
|
||||
// in debug mode this is set to false by bdecode
|
||||
// to indicate that the program has not yet queried
|
||||
// the type of this entry, and sould not assume
|
||||
// that it has a certain type. This is asserted in
|
||||
// the accessor functions. This does not apply if
|
||||
// exceptions are used.
|
||||
mutable bool m_type_queried;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const entry& e)
|
||||
|
@ -212,11 +222,14 @@ namespace libtorrent
|
|||
return os;
|
||||
}
|
||||
|
||||
inline entry::data_type entry::type() const { return m_type; }
|
||||
inline entry::data_type entry::type() const
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
return m_type;
|
||||
}
|
||||
|
||||
inline entry::entry(): m_type(undefined_t) {}
|
||||
inline entry::entry(data_type t): m_type(t) { construct(t); }
|
||||
inline entry::entry(const entry& e) { copy(e); }
|
||||
inline entry::~entry() { destruct(); }
|
||||
|
||||
inline void entry::operator=(const entry& e)
|
||||
|
@ -225,12 +238,13 @@ namespace libtorrent
|
|||
copy(e);
|
||||
}
|
||||
|
||||
|
||||
inline entry::integer_type& entry::integer()
|
||||
{
|
||||
if (m_type == undefined_t) construct(int_t);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != int_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == int_t);
|
||||
return *reinterpret_cast<integer_type*>(data);
|
||||
|
@ -240,6 +254,8 @@ namespace libtorrent
|
|||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != int_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == int_t);
|
||||
return *reinterpret_cast<const integer_type*>(data);
|
||||
|
@ -250,6 +266,8 @@ namespace libtorrent
|
|||
if (m_type == undefined_t) construct(string_t);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != string_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == string_t);
|
||||
return *reinterpret_cast<string_type*>(data);
|
||||
|
@ -259,6 +277,8 @@ namespace libtorrent
|
|||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != string_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == string_t);
|
||||
return *reinterpret_cast<const string_type*>(data);
|
||||
|
@ -269,6 +289,8 @@ namespace libtorrent
|
|||
if (m_type == undefined_t) construct(list_t);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != list_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == list_t);
|
||||
return *reinterpret_cast<list_type*>(data);
|
||||
|
@ -278,6 +300,8 @@ namespace libtorrent
|
|||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != list_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == list_t);
|
||||
return *reinterpret_cast<const list_type*>(data);
|
||||
|
@ -288,6 +312,8 @@ namespace libtorrent
|
|||
if (m_type == undefined_t) construct(dictionary_t);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != dictionary_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == dictionary_t);
|
||||
return *reinterpret_cast<dictionary_type*>(data);
|
||||
|
@ -297,6 +323,8 @@ namespace libtorrent
|
|||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (m_type != dictionary_t) throw type_error("invalid type requested from entry");
|
||||
#elif !defined NDEBUG
|
||||
TORRENT_ASSERT(m_type_queried);
|
||||
#endif
|
||||
TORRENT_ASSERT(m_type == dictionary_t);
|
||||
return *reinterpret_cast<const dictionary_type*>(data);
|
||||
|
|
|
@ -56,9 +56,11 @@ namespace libtorrent
|
|||
|
||||
// returns true if the specified address is on the same
|
||||
// local network as us
|
||||
TORRENT_EXPORT bool in_local_network(asio::io_service& ios, address const& addr, asio::error_code& ec);
|
||||
TORRENT_EXPORT bool in_local_network(asio::io_service& ios, address const& addr
|
||||
, asio::error_code& ec);
|
||||
|
||||
TORRENT_EXPORT address router_for_interface(address const interface, asio::error_code& ec);
|
||||
TORRENT_EXPORT address get_default_gateway(asio::io_service& ios, address const& addr
|
||||
, asio::error_code& ec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#define TORRENT_ESCAPE_STRING_HPP_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <boost/optional.hpp>
|
||||
#include "libtorrent/config.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
|
@ -41,6 +42,15 @@ namespace libtorrent
|
|||
std::string TORRENT_EXPORT unescape_string(std::string const& s);
|
||||
std::string TORRENT_EXPORT escape_string(const char* str, int len);
|
||||
std::string TORRENT_EXPORT escape_path(const char* str, int len);
|
||||
|
||||
// encodes a string using the base64 scheme
|
||||
TORRENT_EXPORT std::string base64encode(std::string const& s);
|
||||
// encodes a string using the base32 scheme
|
||||
TORRENT_EXPORT std::string base32encode(std::string const& s);
|
||||
TORRENT_EXPORT std::string base32decode(std::string const& s);
|
||||
|
||||
TORRENT_EXPORT boost::optional<std::string> url_has_argument(
|
||||
std::string const& url, std::string argument);
|
||||
}
|
||||
|
||||
#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED
|
||||
|
|
|
@ -56,6 +56,7 @@ namespace libtorrent
|
|||
struct peer_request;
|
||||
class peer_connection;
|
||||
class entry;
|
||||
struct disk_buffer_holder;
|
||||
|
||||
struct TORRENT_EXPORT torrent_plugin
|
||||
{
|
||||
|
@ -143,7 +144,7 @@ namespace libtorrent
|
|||
virtual bool on_request(peer_request const& req)
|
||||
{ return false; }
|
||||
|
||||
virtual bool on_piece(peer_request const& piece, char const* data)
|
||||
virtual bool on_piece(peer_request const& piece, disk_buffer_holder& data)
|
||||
{ return false; }
|
||||
|
||||
virtual bool on_cancel(peer_request const& req)
|
||||
|
|
|
@ -109,9 +109,9 @@ namespace libtorrent
|
|||
file(fs::path const& p, open_mode m);
|
||||
~file();
|
||||
|
||||
void open(fs::path const& p, open_mode m);
|
||||
bool open(fs::path const& p, open_mode m);
|
||||
void close();
|
||||
void set_size(size_type size);
|
||||
bool set_size(size_type size);
|
||||
|
||||
size_type write(const char*, size_type num_bytes);
|
||||
size_type read(char*, size_type num_bytes);
|
||||
|
@ -119,6 +119,8 @@ namespace libtorrent
|
|||
size_type seek(size_type pos, seek_mode m = begin);
|
||||
size_type tell();
|
||||
|
||||
std::string const& error() const;
|
||||
|
||||
private:
|
||||
|
||||
struct impl;
|
||||
|
|
|
@ -65,7 +65,8 @@ namespace libtorrent
|
|||
{
|
||||
file_pool(int size = 40): m_size(size) {}
|
||||
|
||||
boost::shared_ptr<file> open_file(void* st, fs::path const& p, file::open_mode m);
|
||||
boost::shared_ptr<file> open_file(void* st, fs::path const& p
|
||||
, file::open_mode m, std::string& error);
|
||||
void release(void* st);
|
||||
void resize(int size);
|
||||
|
||||
|
@ -74,9 +75,7 @@ namespace libtorrent
|
|||
|
||||
struct lru_file_entry
|
||||
{
|
||||
lru_file_entry(boost::shared_ptr<file> const& f)
|
||||
: file_ptr(f)
|
||||
, last_use(time_now()) {}
|
||||
lru_file_entry(): last_use(time_now()) {}
|
||||
mutable boost::shared_ptr<file> file_ptr;
|
||||
fs::path file_path;
|
||||
void* key;
|
||||
|
|
|
@ -42,14 +42,22 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <string>
|
||||
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
#include "libtorrent/time.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
#include "libtorrent/ssl_stream.hpp"
|
||||
#include "libtorrent/variant_stream.hpp"
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
struct http_connection;
|
||||
class connection_queue;
|
||||
|
||||
typedef boost::function<void(asio::error_code const&
|
||||
, http_parser const&, char const* data, int size)> http_handler;
|
||||
|
@ -81,6 +89,8 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
|
|||
, m_redirects(5)
|
||||
, m_connection_ticket(-1)
|
||||
, m_cc(cc)
|
||||
, m_ssl(false)
|
||||
, m_priority(0)
|
||||
{
|
||||
TORRENT_ASSERT(!m_handler.empty());
|
||||
}
|
||||
|
@ -93,14 +103,22 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
|
|||
std::string sendbuffer;
|
||||
|
||||
void get(std::string const& url, time_duration timeout = seconds(30)
|
||||
, int handle_redirects = 5);
|
||||
, int prio = 0, proxy_settings const* ps = 0, int handle_redirects = 5
|
||||
, std::string const& user_agent = "", address const& bind_addr = address_v4::any());
|
||||
|
||||
void start(std::string const& hostname, std::string const& port
|
||||
, time_duration timeout, int handle_redirect = 5);
|
||||
, time_duration timeout, int prio = 0, proxy_settings const* ps = 0
|
||||
, bool ssl = false, int handle_redirect = 5
|
||||
, address const& bind_addr = address_v4::any());
|
||||
|
||||
void close();
|
||||
|
||||
tcp::socket const& socket() const { return m_sock; }
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
variant_stream<socket_type, ssl_stream<socket_type> > const& socket() const { return m_sock; }
|
||||
#else
|
||||
socket_type const& socket() const { return m_sock; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
void on_resolve(asio::error_code const& e
|
||||
|
@ -118,7 +136,11 @@ private:
|
|||
void callback(asio::error_code const& e, char const* data = 0, int size = 0);
|
||||
|
||||
std::vector<char> m_recvbuffer;
|
||||
tcp::socket m_sock;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
variant_stream<socket_type, ssl_stream<socket_type> > m_sock;
|
||||
#else
|
||||
socket_type m_sock;
|
||||
#endif
|
||||
int m_read_pos;
|
||||
tcp::resolver m_resolver;
|
||||
http_parser m_parser;
|
||||
|
@ -158,6 +180,21 @@ private:
|
|||
|
||||
int m_connection_ticket;
|
||||
connection_queue& m_cc;
|
||||
|
||||
// specifies whether or not the connection is
|
||||
// configured to use a proxy
|
||||
proxy_settings m_proxy;
|
||||
|
||||
// true if the connection is using ssl
|
||||
bool m_ssl;
|
||||
|
||||
// the address to bind to. address_v4::any()
|
||||
// means do not bind
|
||||
address m_bind_addr;
|
||||
|
||||
// the priority we have in the connection queue.
|
||||
// 0 is normal, 1 is high
|
||||
int m_priority;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -33,85 +33,30 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
|
||||
#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <ctime>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
#include "libtorrent/peer.hpp"
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/buffer.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/connection_queue.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
class http_parser
|
||||
{
|
||||
public:
|
||||
http_parser();
|
||||
std::string const& header(char const* key) const
|
||||
{
|
||||
static std::string empty;
|
||||
std::map<std::string, std::string>::const_iterator i
|
||||
= m_header.find(key);
|
||||
if (i == m_header.end()) return empty;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
std::string const& protocol() const { return m_protocol; }
|
||||
int status_code() const { return m_status_code; }
|
||||
std::string const& method() const { return m_method; }
|
||||
std::string const& path() const { return m_path; }
|
||||
std::string const& message() const { return m_server_message; }
|
||||
buffer::const_interval get_body() const;
|
||||
bool header_finished() const { return m_state == read_body; }
|
||||
bool finished() const { return m_finished; }
|
||||
boost::tuple<int, int> incoming(buffer::const_interval recv_buffer);
|
||||
int body_start() const { return m_body_start_pos; }
|
||||
int content_length() const { return m_content_length; }
|
||||
|
||||
void reset();
|
||||
|
||||
std::map<std::string, std::string> const& headers() const { return m_header; }
|
||||
|
||||
private:
|
||||
int m_recv_pos;
|
||||
int m_status_code;
|
||||
std::string m_method;
|
||||
std::string m_path;
|
||||
std::string m_protocol;
|
||||
std::string m_server_message;
|
||||
|
||||
int m_content_length;
|
||||
|
||||
enum { read_status, read_header, read_body } m_state;
|
||||
|
||||
std::map<std::string, std::string> m_header;
|
||||
buffer::const_interval m_recv_buffer;
|
||||
int m_body_start_pos;
|
||||
|
||||
bool m_finished;
|
||||
};
|
||||
struct http_connection;
|
||||
class entry;
|
||||
class http_parser;
|
||||
class connection_queue;
|
||||
struct session_settings;
|
||||
|
||||
class TORRENT_EXPORT http_tracker_connection
|
||||
: public tracker_connection
|
||||
|
@ -120,13 +65,10 @@ namespace libtorrent
|
|||
public:
|
||||
|
||||
http_tracker_connection(
|
||||
asio::strand& str
|
||||
io_service& ios
|
||||
, connection_queue& cc
|
||||
, tracker_manager& man
|
||||
, tracker_request const& req
|
||||
, std::string const& hostname
|
||||
, unsigned short port
|
||||
, std::string request
|
||||
, address bind_infc
|
||||
, boost::weak_ptr<request_callback> c
|
||||
, session_settings const& stn
|
||||
|
@ -140,43 +82,16 @@ namespace libtorrent
|
|||
boost::intrusive_ptr<http_tracker_connection> self()
|
||||
{ return boost::intrusive_ptr<http_tracker_connection>(this); }
|
||||
|
||||
void on_response();
|
||||
|
||||
void init_send_buffer(
|
||||
std::string const& hostname
|
||||
, std::string const& request);
|
||||
void on_response(asio::error_code const& ec, http_parser const& parser
|
||||
, char const* data, int size);
|
||||
|
||||
void name_lookup(asio::error_code const& error, tcp::resolver::iterator i);
|
||||
void connect(int ticket, tcp::endpoint target_address);
|
||||
void connected(asio::error_code const& error);
|
||||
void sent(asio::error_code const& error);
|
||||
void receive(asio::error_code const& error
|
||||
, std::size_t bytes_transferred);
|
||||
virtual void on_timeout() {}
|
||||
|
||||
virtual void on_timeout();
|
||||
|
||||
void parse(const entry& e);
|
||||
peer_entry extract_peer_info(const entry& e);
|
||||
void parse(int status_code, const entry& e);
|
||||
bool extract_peer_info(const entry& e, peer_entry& ret);
|
||||
|
||||
tracker_manager& m_man;
|
||||
http_parser m_parser;
|
||||
|
||||
asio::strand& m_strand;
|
||||
tcp::resolver m_name_lookup;
|
||||
int m_port;
|
||||
socket_type m_socket;
|
||||
int m_recv_pos;
|
||||
std::vector<char> m_buffer;
|
||||
std::string m_send_buffer;
|
||||
|
||||
session_settings const& m_settings;
|
||||
proxy_settings const& m_proxy;
|
||||
std::string m_password;
|
||||
|
||||
bool m_timed_out;
|
||||
|
||||
int m_connection_ticket;
|
||||
connection_queue& m_cc;
|
||||
boost::shared_ptr<http_connection> m_tracker_connection;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include <boost/detail/atomic_count.hpp>
|
||||
#include <boost/checked_delete.hpp>
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
||||
|
|
|
@ -50,9 +50,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/kademlia/node.hpp"
|
||||
#include "libtorrent/kademlia/node_id.hpp"
|
||||
#include "libtorrent/kademlia/traversal_algorithm.hpp"
|
||||
#include "libtorrent/kademlia/packet_iterator.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
#include "libtorrent/session_status.hpp"
|
||||
#include "libtorrent/udp_socket.hpp"
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
@ -70,16 +70,14 @@ namespace libtorrent { namespace dht
|
|||
{
|
||||
friend void intrusive_ptr_add_ref(dht_tracker const*);
|
||||
friend void intrusive_ptr_release(dht_tracker const*);
|
||||
dht_tracker(asio::io_service& ios, dht_settings const& settings
|
||||
, asio::ip::address listen_interface, entry const& bootstrap);
|
||||
dht_tracker(udp_socket& sock, dht_settings const& settings
|
||||
, entry const& bootstrap);
|
||||
void stop();
|
||||
|
||||
void add_node(udp::endpoint node);
|
||||
void add_node(std::pair<std::string, int> const& node);
|
||||
void add_router_node(std::pair<std::string, int> const& node);
|
||||
|
||||
void rebind(asio::ip::address listen_interface, int listen_port);
|
||||
|
||||
entry state() const;
|
||||
|
||||
void announce(sha1_hash const& ih, int listen_port
|
||||
|
@ -88,6 +86,10 @@ namespace libtorrent { namespace dht
|
|||
|
||||
void dht_status(session_status& s);
|
||||
|
||||
// translate bittorrent kademlia message into the generic kademlia message
|
||||
// used by the library
|
||||
void on_receive(udp::endpoint const& ep, char const* pkt, int size);
|
||||
|
||||
private:
|
||||
|
||||
boost::intrusive_ptr<dht_tracker> self()
|
||||
|
@ -101,22 +103,12 @@ namespace libtorrent { namespace dht
|
|||
void refresh_timeout(asio::error_code const& e);
|
||||
void tick(asio::error_code const& e);
|
||||
|
||||
// translate bittorrent kademlia message into the generic kademlia message
|
||||
// used by the library
|
||||
void on_receive(asio::error_code const& error, size_t bytes_transferred);
|
||||
void on_bootstrap();
|
||||
void send_packet(msg const& m);
|
||||
|
||||
asio::strand m_strand;
|
||||
asio::ip::udp::socket m_socket;
|
||||
|
||||
node_impl m_dht;
|
||||
udp_socket& m_sock;
|
||||
|
||||
// this is the index of the receive buffer we are currently receiving to
|
||||
// the other buffer is the one containing the last message
|
||||
int m_buffer;
|
||||
std::vector<char> m_in_buf[2];
|
||||
udp::endpoint m_remote_endpoint[2];
|
||||
std::vector<char> m_send_buf;
|
||||
|
||||
ptime m_last_new_key;
|
||||
|
|
|
@ -39,7 +39,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <libtorrent/kademlia/node_id.hpp>
|
||||
#include <libtorrent/kademlia/routing_table.hpp>
|
||||
#include <libtorrent/kademlia/rpc_manager.hpp>
|
||||
#include <libtorrent/kademlia/packet_iterator.hpp>
|
||||
#include <libtorrent/kademlia/observer.hpp>
|
||||
#include <libtorrent/kademlia/msg.hpp>
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include <libtorrent/socket.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <libtorrent/kademlia/packet_iterator.hpp>
|
||||
#include <libtorrent/kademlia/node_id.hpp>
|
||||
#include <libtorrent/kademlia/logging.hpp>
|
||||
#include <libtorrent/kademlia/node_entry.hpp>
|
||||
|
@ -94,6 +93,7 @@ public:
|
|||
void reply_with_ping(msg& m);
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t allocation_size() const;
|
||||
void check_invariant() const;
|
||||
#endif
|
||||
|
||||
|
@ -114,7 +114,7 @@ private:
|
|||
typedef boost::array<observer_ptr, max_transactions>
|
||||
transactions_t;
|
||||
transactions_t m_transactions;
|
||||
std::vector<observer_ptr > m_aborted_transactions;
|
||||
std::vector<observer_ptr> m_aborted_transactions;
|
||||
|
||||
// this is the next transaction id to be used
|
||||
int m_next_transaction_id;
|
||||
|
|
|
@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/intrusive_ptr_base.hpp"
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
#include <fstream>
|
||||
|
@ -45,8 +46,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace libtorrent
|
||||
{
|
||||
|
||||
// int: external tcp port
|
||||
// int: external udp port
|
||||
// int: port mapping index
|
||||
// int: external port
|
||||
// std::string: error message
|
||||
typedef boost::function<void(int, int, std::string const&)> portmap_callback_t;
|
||||
|
||||
|
@ -59,34 +60,38 @@ public:
|
|||
|
||||
// maps the ports, if a port is set to 0
|
||||
// it will not be mapped
|
||||
void set_mappings(int tcp, int udp);
|
||||
enum protocol_type { none = 0, udp = 1, tcp = 2 };
|
||||
int add_mapping(protocol_type p, int external_port, int local_port);
|
||||
void delete_mapping(int mapping_index);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
|
||||
void update_mapping(int i, int port);
|
||||
void update_mapping(int i);
|
||||
void send_map_request(int i);
|
||||
void resend_request(int i, asio::error_code const& e);
|
||||
void on_reply(asio::error_code const& e
|
||||
, std::size_t bytes_transferred);
|
||||
void try_next_mapping(int i);
|
||||
void update_expiration_timer();
|
||||
void refresh_mapping(int i);
|
||||
void mapping_expired(asio::error_code const& e, int i);
|
||||
|
||||
struct mapping
|
||||
void disable(char const* message);
|
||||
|
||||
struct mapping_t
|
||||
{
|
||||
mapping()
|
||||
: need_update(false)
|
||||
enum action_t { action_none, action_add, action_delete };
|
||||
mapping_t()
|
||||
: action(action_none)
|
||||
, local_port(0)
|
||||
, external_port(0)
|
||||
, protocol(1)
|
||||
, protocol(none)
|
||||
{}
|
||||
|
||||
// indicates that the mapping has changed
|
||||
// and needs an update
|
||||
bool need_update;
|
||||
int action;
|
||||
|
||||
// the time the port mapping will expire
|
||||
ptime expires;
|
||||
|
@ -100,14 +105,12 @@ private:
|
|||
// should announce to others
|
||||
int external_port;
|
||||
|
||||
// 1 = udp, 2 = tcp
|
||||
int protocol;
|
||||
};
|
||||
|
||||
portmap_callback_t m_callback;
|
||||
|
||||
// 0 is tcp and 1 is udp
|
||||
mapping m_mappings[2];
|
||||
std::vector<mapping_t> m_mappings;
|
||||
|
||||
// the endpoint to the nat router
|
||||
udp::endpoint m_nat_endpoint;
|
||||
|
@ -136,9 +139,17 @@ private:
|
|||
|
||||
// timer used to refresh mappings
|
||||
deadline_timer m_refresh_timer;
|
||||
|
||||
// the mapping index that will expire next
|
||||
int m_next_refresh;
|
||||
|
||||
bool m_disabled;
|
||||
|
||||
bool m_abort;
|
||||
|
||||
typedef boost::mutex mutex_t;
|
||||
mutex_t m_mutex;
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
std::ofstream m_log;
|
||||
#endif
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
#include <asio/io_service.hpp>
|
||||
#include <asio/deadline_timer.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <asio/strand.hpp>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#undef Protocol
|
||||
|
|
|
@ -75,6 +75,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/intrusive_ptr_base.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
#include "libtorrent/chained_buffer.hpp"
|
||||
#include "libtorrent/disk_buffer_holder.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -122,6 +123,12 @@ namespace libtorrent
|
|||
, boost::shared_ptr<socket_type> s
|
||||
, policy::peer* peerinfo);
|
||||
|
||||
// this function is called after it has been constructed and properly
|
||||
// reference counted. It is safe to call self() in this function
|
||||
// and schedule events with references to itself (that is not safe to
|
||||
// do in the constructor).
|
||||
virtual void start();
|
||||
|
||||
virtual ~peer_connection();
|
||||
|
||||
void set_peer_info(policy::peer* pi)
|
||||
|
@ -235,7 +242,7 @@ namespace libtorrent
|
|||
|
||||
void timed_out();
|
||||
// this will cause this peer_connection to be disconnected.
|
||||
void disconnect();
|
||||
void disconnect(char const* message);
|
||||
bool is_disconnecting() const { return m_disconnecting; }
|
||||
|
||||
// this is called when the connection attempt has succeeded
|
||||
|
@ -286,7 +293,16 @@ namespace libtorrent
|
|||
|
||||
int desired_queue_size() const { return m_desired_queue_size; }
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
// compares this connection against the given connection
|
||||
// for which one is more eligible for an unchoke.
|
||||
// returns true if this is more eligible
|
||||
bool unchoke_compare(boost::intrusive_ptr<peer_connection const> const& p) const;
|
||||
|
||||
// resets the byte counters that are used to measure
|
||||
// the number of bytes transferred within unchoke cycles
|
||||
void reset_choke_counters();
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
boost::shared_ptr<logger> m_logger;
|
||||
#endif
|
||||
|
||||
|
@ -306,6 +322,7 @@ namespace libtorrent
|
|||
void incoming_have(int piece_index);
|
||||
void incoming_bitfield(std::vector<bool> const& bitfield);
|
||||
void incoming_request(peer_request const& r);
|
||||
void incoming_piece(peer_request const& p, disk_buffer_holder& data);
|
||||
void incoming_piece(peer_request const& p, char const* data);
|
||||
void incoming_piece_fragment();
|
||||
void incoming_cancel(peer_request const& r);
|
||||
|
@ -399,6 +416,19 @@ namespace libtorrent
|
|||
int send_buffer_capacity() const
|
||||
{ return m_send_buffer.capacity(); }
|
||||
|
||||
int packet_size() const { return m_packet_size; }
|
||||
|
||||
bool packet_finished() const
|
||||
{ return m_packet_size <= m_recv_pos; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool piece_failed;
|
||||
#endif
|
||||
|
||||
// upload and download channel state
|
||||
// enum from peer_info::bw_state
|
||||
char m_channel_state[2];
|
||||
|
||||
protected:
|
||||
|
||||
virtual void get_specific_peer_info(peer_info& p) const = 0;
|
||||
|
@ -411,7 +441,7 @@ namespace libtorrent
|
|||
virtual void write_cancel(peer_request const& r) = 0;
|
||||
virtual void write_have(int index) = 0;
|
||||
virtual void write_keepalive() = 0;
|
||||
virtual void write_piece(peer_request const& r, char* buffer) = 0;
|
||||
virtual void write_piece(peer_request const& r, disk_buffer_holder& buffer) = 0;
|
||||
|
||||
virtual void write_reject_request(peer_request const& r) = 0;
|
||||
virtual void write_allow_fast(int piece) = 0;
|
||||
|
@ -427,10 +457,14 @@ namespace libtorrent
|
|||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
buffer::interval wr_recv_buffer()
|
||||
{
|
||||
TORRENT_ASSERT(m_disk_recv_buffer == 0);
|
||||
TORRENT_ASSERT(m_disk_recv_buffer_size == 0);
|
||||
if (m_recv_buffer.empty()) return buffer::interval(0,0);
|
||||
return buffer::interval(&m_recv_buffer[0]
|
||||
, &m_recv_buffer[0] + m_recv_pos);
|
||||
}
|
||||
|
||||
std::pair<buffer::interval, buffer::interval> wr_recv_buffers(int bytes);
|
||||
#endif
|
||||
|
||||
buffer::const_interval receive_buffer() const
|
||||
|
@ -440,15 +474,11 @@ namespace libtorrent
|
|||
, &m_recv_buffer[0] + m_recv_pos);
|
||||
}
|
||||
|
||||
bool allocate_disk_receive_buffer(int disk_buffer_size);
|
||||
char* release_disk_receive_buffer();
|
||||
bool has_disk_receive_buffer() const { return m_disk_recv_buffer; }
|
||||
void cut_receive_buffer(int size, int packet_size);
|
||||
|
||||
void reset_recv_buffer(int packet_size);
|
||||
int packet_size() const { return m_packet_size; }
|
||||
|
||||
bool packet_finished() const
|
||||
{
|
||||
return m_packet_size <= m_recv_pos;
|
||||
}
|
||||
|
||||
void setup_receive();
|
||||
|
||||
|
@ -499,6 +529,14 @@ namespace libtorrent
|
|||
char m_country[2];
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
boost::intrusive_ptr<peer_connection> self()
|
||||
{
|
||||
TORRENT_ASSERT(!m_in_constructor);
|
||||
return intrusive_ptr_base<peer_connection>::self();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
void fill_send_buffer();
|
||||
|
@ -525,6 +563,13 @@ namespace libtorrent
|
|||
int m_recv_pos;
|
||||
buffer m_recv_buffer;
|
||||
|
||||
// if this peer is receiving a piece, this
|
||||
// points to a disk buffer that the data is
|
||||
// read into. This eliminates a memcopy from
|
||||
// the receive buffer into the disk buffer
|
||||
int m_disk_recv_buffer_size;
|
||||
char* m_disk_recv_buffer;
|
||||
|
||||
chained_buffer m_send_buffer;
|
||||
|
||||
// the number of bytes we are currently reading
|
||||
|
@ -661,12 +706,6 @@ namespace libtorrent
|
|||
// connections.
|
||||
bool m_queued;
|
||||
|
||||
// these are true when there's a asynchronous write
|
||||
// or read operation in progress. Or an asyncronous bandwidth
|
||||
// request is in progress.
|
||||
bool m_writing;
|
||||
bool m_reading;
|
||||
|
||||
// if set to non-zero, this peer will always prefer
|
||||
// to request entire n pieces, rather than blocks.
|
||||
// where n is the value of this variable.
|
||||
|
@ -741,6 +780,30 @@ namespace libtorrent
|
|||
// immediate.
|
||||
bool m_fast_reconnect;
|
||||
|
||||
// the time when async_connect was called
|
||||
ptime m_connect;
|
||||
|
||||
// estimated round trip time to this peer
|
||||
// based on the time from when async_connect
|
||||
// was called to when on_connection_complete
|
||||
// was called. The rtt is specified in milliseconds
|
||||
int m_rtt;
|
||||
|
||||
// the total payload download bytes
|
||||
// at the last unchoke cycle. This is used to
|
||||
// measure the number of bytes transferred during
|
||||
// an unchoke cycle, to unchoke peers the more bytes
|
||||
// they sent us
|
||||
size_type m_downloaded_at_last_unchoke;
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
std::string m_inet_as_name;
|
||||
#endif
|
||||
|
||||
// max transfer rates seen on this peer
|
||||
int m_download_rate_peak;
|
||||
int m_upload_rate_peak;
|
||||
|
||||
#ifndef NDEBUG
|
||||
public:
|
||||
bool m_in_constructor;
|
||||
|
|
|
@ -48,8 +48,6 @@ namespace libtorrent
|
|||
|
||||
class TORRENT_EXPORT big_number
|
||||
{
|
||||
// private type
|
||||
struct private_pointer {};
|
||||
// the number of bytes of the number
|
||||
enum { number_size = 20 };
|
||||
public:
|
||||
|
@ -57,16 +55,19 @@ namespace libtorrent
|
|||
|
||||
big_number() {}
|
||||
|
||||
big_number(std::string const& s)
|
||||
explicit big_number(char const* s)
|
||||
{
|
||||
if (s == 0) clear();
|
||||
else std::memcpy(m_number, s, size);
|
||||
}
|
||||
|
||||
explicit big_number(std::string const& s)
|
||||
{
|
||||
TORRENT_ASSERT(s.size() >= 20);
|
||||
int sl = int(s.size()) < size ? int(s.size()) : size;
|
||||
std::memcpy(m_number, &s[0], sl);
|
||||
}
|
||||
|
||||
// when initialized with 0
|
||||
big_number(private_pointer*) { clear(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::fill(m_number,m_number+number_size,0);
|
||||
|
|
|
@ -78,6 +78,16 @@ namespace libtorrent
|
|||
|
||||
int source;
|
||||
|
||||
// bw_idle: the channel is not used
|
||||
// bw_torrent: the channel is waiting for torrent quota
|
||||
// bw_global: the channel is waiting for global quota
|
||||
// bw_network: the channel is waiting for an async write
|
||||
// for read operation to complete
|
||||
enum bw_state { bw_idle, bw_torrent, bw_global, bw_network };
|
||||
|
||||
char read_state;
|
||||
char write_state;
|
||||
|
||||
tcp::endpoint ip;
|
||||
float up_speed;
|
||||
float down_speed;
|
||||
|
@ -101,6 +111,9 @@ namespace libtorrent
|
|||
// the number bytes that's actually used of the send buffer
|
||||
int used_send_buffer;
|
||||
|
||||
int receive_buffer_size;
|
||||
int used_receive_buffer;
|
||||
|
||||
// the number of failed hashes for this peer
|
||||
int num_hashfails;
|
||||
|
||||
|
@ -112,6 +125,12 @@ namespace libtorrent
|
|||
char country[2];
|
||||
#endif
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
// atonomous system this peer belongs to
|
||||
std::string inet_as_name;
|
||||
int inet_as;
|
||||
#endif
|
||||
|
||||
size_type load_balancing;
|
||||
|
||||
// this is the number of requests
|
||||
|
@ -157,8 +176,31 @@ namespace libtorrent
|
|||
// number of bytes this peer has in
|
||||
// the disk write queue
|
||||
int pending_disk_bytes;
|
||||
|
||||
// numbers used for bandwidth limiting
|
||||
int send_quota;
|
||||
int receive_quota;
|
||||
|
||||
// estimated rtt to peer, in milliseconds
|
||||
int rtt;
|
||||
|
||||
// the highest transfer rates seen for this peer
|
||||
int download_rate_peak;
|
||||
int upload_rate_peak;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT peer_list_entry
|
||||
{
|
||||
enum flags_t
|
||||
{
|
||||
banned = 1,
|
||||
};
|
||||
|
||||
tcp::endpoint ip;
|
||||
int flags;
|
||||
boost::uint8_t failcount;
|
||||
boost::uint8_t source;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TORRENT_PEER_INFO_HPP_INCLUDED
|
||||
|
|
|
@ -133,22 +133,24 @@ namespace libtorrent
|
|||
|
||||
void get_availability(std::vector<int>& avail) const;
|
||||
|
||||
void set_sequenced_download_threshold(int sequenced_download_threshold);
|
||||
void sequential_download(bool sd);
|
||||
bool sequential_download() const { return m_sequential_download >= 0; }
|
||||
|
||||
// the vector tells which pieces we already have
|
||||
// and which we don't have.
|
||||
void files_checked(
|
||||
std::vector<bool> const& pieces
|
||||
, std::vector<downloading_piece> const& unfinished
|
||||
, std::vector<int>& verify_pieces);
|
||||
void init(std::vector<bool> const& pieces);
|
||||
|
||||
// increases the peer count for the given piece
|
||||
// (is used when a HAVE or BITFIELD message is received)
|
||||
// (is used when a HAVE message is received)
|
||||
void inc_refcount(int index);
|
||||
void dec_refcount(int index);
|
||||
|
||||
// increases the peer count for the given piece
|
||||
// (is used when a BITFIELD message is received)
|
||||
void inc_refcount(std::vector<bool> const& bitmask);
|
||||
// decreases the peer count for the given piece
|
||||
// (used when a peer disconnects)
|
||||
void dec_refcount(int index);
|
||||
void dec_refcount(std::vector<bool> const& bitmask);
|
||||
|
||||
// these will increase and decrease the peer count
|
||||
// of all pieces. They are used when seeds join
|
||||
|
@ -237,6 +239,7 @@ namespace libtorrent
|
|||
, piece_state_t s);
|
||||
void mark_as_writing(piece_block block, void* peer);
|
||||
void mark_as_finished(piece_block block, void* peer);
|
||||
void write_failed(piece_block block);
|
||||
int num_peers(piece_block block) const;
|
||||
|
||||
// returns information about the given piece
|
||||
|
@ -274,9 +277,11 @@ namespace libtorrent
|
|||
|
||||
#ifndef NDEBUG
|
||||
// used in debug mode
|
||||
void verify_priority(int start, int end, int prio) const;
|
||||
void check_invariant(const torrent* t = 0) const;
|
||||
void verify_pick(std::vector<piece_block> const& picked
|
||||
, std::vector<bool> const& bitfield) const;
|
||||
void print_pieces() const;
|
||||
#endif
|
||||
|
||||
// functor that compares indices on downloading_pieces
|
||||
|
@ -295,6 +300,8 @@ namespace libtorrent
|
|||
|
||||
private:
|
||||
|
||||
friend struct piece_pos;
|
||||
|
||||
bool can_pick(int piece, std::vector<bool> const& bitmask) const;
|
||||
std::pair<int, int> expand_piece(int piece, int whole_pieces
|
||||
, std::vector<bool> const& have) const;
|
||||
|
@ -346,26 +353,20 @@ namespace libtorrent
|
|||
bool filtered() const { return piece_priority == filter_priority; }
|
||||
void filtered(bool f) { piece_priority = f ? filter_priority : 0; }
|
||||
|
||||
int priority(int limit) const
|
||||
int priority(piece_picker const* picker) const
|
||||
{
|
||||
if (downloading || filtered() || have()) return 0;
|
||||
if (downloading || filtered()
|
||||
|| have() || peer_count + picker->m_seeds == 0)
|
||||
return -1;
|
||||
|
||||
// priority 5, 6 and 7 disregards availability of the piece
|
||||
if (piece_priority > 4) return 7 - piece_priority;
|
||||
|
||||
// pieces we are currently downloading have high priority
|
||||
int prio = peer_count * 2;
|
||||
// if the peer_count is 0 or 1, the priority cannot be higher
|
||||
if (prio <= 1) return prio;
|
||||
if (prio >= limit * 2) prio = limit * 2;
|
||||
// the different priority levels
|
||||
switch (piece_priority)
|
||||
{
|
||||
case 1: return prio;
|
||||
case 2: return prio - 1;
|
||||
case 3: return (std::max)(prio / 2, 1);
|
||||
case 4: return (std::max)(prio / 2 - 1, 1);
|
||||
case 5: return (std::max)(prio / 3, 1);
|
||||
case 6: return (std::max)(prio / 3 - 1, 1);
|
||||
case 7: return 1;
|
||||
}
|
||||
return prio;
|
||||
int prio = peer_count * 4;
|
||||
// if (prio >= picker->m_prio_limit * 6) prio = picker->m_prio_limit * 6;
|
||||
|
||||
return prio + (4 - piece_priority);
|
||||
}
|
||||
|
||||
bool operator!=(piece_pos p) const
|
||||
|
@ -378,27 +379,44 @@ namespace libtorrent
|
|||
|
||||
BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4);
|
||||
|
||||
bool is_ordered(int priority) const
|
||||
{
|
||||
return priority >= m_sequenced_download_threshold * 2;
|
||||
}
|
||||
void update_pieces() const;
|
||||
|
||||
// fills in the range [start, end) of pieces in
|
||||
// m_pieces that have priority 'prio'
|
||||
void priority_range(int prio, int* start, int* end);
|
||||
|
||||
// adds the piece 'index' to m_pieces
|
||||
void add(int index);
|
||||
void move(int vec_index, int elem_index);
|
||||
// removes the piece with the given priority and the
|
||||
// elem_index in the m_pieces vector
|
||||
void remove(int priority, int elem_index);
|
||||
// updates the position of the piece with the given
|
||||
// priority and the elem_index in the m_pieces vector
|
||||
void update(int priority, int elem_index);
|
||||
// shuffles the given piece inside it's priority range
|
||||
void shuffle(int priority, int elem_index);
|
||||
|
||||
void sort_piece(std::vector<downloading_piece>::iterator dp);
|
||||
|
||||
downloading_piece& add_download_piece();
|
||||
void erase_download_piece(std::vector<downloading_piece>::iterator i);
|
||||
|
||||
// this vector contains all pieces we don't have.
|
||||
// in the first entry (index 0) is a vector of all pieces
|
||||
// that no peer have, the vector at index 1 contains
|
||||
// all pieces that exactly one peer have, index 2 contains
|
||||
// all pieces exactly two peers have and so on.
|
||||
// this is not entirely true. The availibility of a piece
|
||||
// is adjusted depending on its priority. But the principle
|
||||
// is that the higher index, the lower priority a piece has.
|
||||
std::vector<std::vector<int> > m_piece_info;
|
||||
// the number of seeds. These are not added to
|
||||
// the availability counters of the pieces
|
||||
int m_seeds;
|
||||
|
||||
// the following vectors are mutable because they sometimes may
|
||||
// be updated lazily, triggered by const functions
|
||||
|
||||
// this vector contains all piece indices that are pickable
|
||||
// sorted by priority. Pieces are in random random order
|
||||
// among pieces with the same priority
|
||||
mutable std::vector<int> m_pieces;
|
||||
|
||||
// these are indices to the priority boundries inside
|
||||
// the m_pieces vector. priority 0 always start at
|
||||
// 0, priority 1 starts at m_priority_boundries[0] etc.
|
||||
mutable std::vector<int> m_priority_boundries;
|
||||
|
||||
// this maps indices to number of peers that has this piece and
|
||||
// index into the m_piece_info vectors.
|
||||
|
@ -406,7 +424,7 @@ namespace libtorrent
|
|||
// doesn't exist in the piece_info buckets
|
||||
// pieces with the filtered flag set doesn't have entries in
|
||||
// the m_piece_info buckets either
|
||||
std::vector<piece_pos> m_piece_map;
|
||||
mutable std::vector<piece_pos> m_piece_map;
|
||||
|
||||
// each piece that's currently being downloaded
|
||||
// has an entry in this list with block allocations.
|
||||
|
@ -438,9 +456,16 @@ namespace libtorrent
|
|||
// the number of pieces we have
|
||||
int m_num_have;
|
||||
|
||||
// the required popularity of a piece in order to download
|
||||
// it in sequence instead of random order.
|
||||
int m_sequenced_download_threshold;
|
||||
// -1 means sequential download is not active.
|
||||
// >= 0 means that pieces are requested in sequential order
|
||||
// and this variable is the next piece to request.
|
||||
// in that case m_pieces is cleared and not used.
|
||||
int m_sequential_download;
|
||||
|
||||
// if this is set to true, it means update_pieces()
|
||||
// has to be called before accessing m_pieces.
|
||||
mutable bool m_dirty;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool m_files_checked_called;
|
||||
#endif
|
||||
|
|
|
@ -85,13 +85,16 @@ namespace libtorrent
|
|||
// the tracker, pex, lsd or dht.
|
||||
policy::peer* peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid
|
||||
, int source, char flags);
|
||||
void update_peer_port(int port, policy::peer* p, int src);
|
||||
|
||||
// false means duplicate connection
|
||||
bool update_peer_port(int port, policy::peer* p, int src);
|
||||
|
||||
// called when an incoming connection is accepted
|
||||
void new_connection(peer_connection& c);
|
||||
// false means the connection was refused or failed
|
||||
bool new_connection(peer_connection& c);
|
||||
|
||||
// the given connection was just closed
|
||||
void connection_closed(const peer_connection& c) throw();
|
||||
void connection_closed(const peer_connection& c);
|
||||
|
||||
// the peer has got at least one interesting piece
|
||||
void peer_is_interesting(peer_connection& c);
|
||||
|
@ -123,56 +126,27 @@ namespace libtorrent
|
|||
struct peer
|
||||
{
|
||||
enum connection_type { not_connectable, connectable };
|
||||
|
||||
peer(tcp::endpoint const& ip, connection_type t, int src);
|
||||
|
||||
size_type total_download() const;
|
||||
size_type total_upload() const;
|
||||
|
||||
// the ip/port pair this peer is or was connected on
|
||||
// if it was a remote (incoming) connection, type is
|
||||
// set thereafter. If it was a peer we got from the
|
||||
// tracker, type is set to local_connection.
|
||||
tcp::endpoint ip;
|
||||
connection_type type;
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
// Hints encryption support of peer. Only effective for
|
||||
// and when the outgoing encryption policy allows both
|
||||
// encrypted and non encrypted connections
|
||||
// (pe_settings::out_enc_policy == enabled). The initial
|
||||
// state of this flag determines the initial connection
|
||||
// attempt type (true = encrypted, false = standard).
|
||||
// This will be toggled everytime either an encrypted or
|
||||
// non-encrypted handshake fails.
|
||||
bool pe_support;
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
#ifndef NDEBUG
|
||||
// only used in debug mode to assert that
|
||||
// the first entry in the AS pair keeps the same
|
||||
boost::uint16_t inet_as_num;
|
||||
#endif
|
||||
// The AS this peer belongs to
|
||||
std::pair<const int, int>* inet_as;
|
||||
#endif
|
||||
// the number of failed connection attempts this peer has
|
||||
int failcount;
|
||||
|
||||
// the number of times this peer has been
|
||||
// part of a piece that failed the hash check
|
||||
int hashfails;
|
||||
|
||||
// this is true if the peer is a seed
|
||||
bool seed;
|
||||
|
||||
int fast_reconnects;
|
||||
|
||||
// true if this peer currently is unchoked
|
||||
// because of an optimistic unchoke.
|
||||
// when the optimistic unchoke is moved to
|
||||
// another peer, this peer will be choked
|
||||
// if this is true
|
||||
bool optimistically_unchoked;
|
||||
|
||||
// the time when this peer was optimistically unchoked
|
||||
// the last time.
|
||||
libtorrent::ptime last_optimistically_unchoked;
|
||||
|
||||
// the time when the peer connected to us
|
||||
// or disconnected if it isn't connected right now
|
||||
libtorrent::ptime connected;
|
||||
// the number of failed connection attempts
|
||||
// this peer has
|
||||
boost::uint8_t failcount;
|
||||
|
||||
// for every valid piece we receive where this
|
||||
// peer was one of the participants, we increase
|
||||
|
@ -180,15 +154,68 @@ namespace libtorrent
|
|||
// where this peer was a participant, we decrease
|
||||
// this value. If it sinks below a threshold, its
|
||||
// considered a bad peer and will be banned.
|
||||
int trust_points;
|
||||
boost::int8_t trust_points;
|
||||
|
||||
// if this is true, the peer has previously participated
|
||||
// in a piece that failed the piece hash check. This will
|
||||
// put the peer on parole and only request entire pieces.
|
||||
// if a piece pass that was partially requested from this
|
||||
// peer it will leave parole mode and continue download
|
||||
// a bitmap combining the peer_source flags
|
||||
// from peer_info.
|
||||
boost::uint8_t source;
|
||||
|
||||
// the number of times this peer has been
|
||||
// part of a piece that failed the hash check
|
||||
boost::uint8_t hashfails;
|
||||
|
||||
// type specifies if the connection was incoming
|
||||
// or outgoing. If we ever saw this peer as connectable
|
||||
// it will remain as connectable
|
||||
unsigned type:4;
|
||||
|
||||
// the number of times we have allowed a fast
|
||||
// reconnect for this peer.
|
||||
unsigned fast_reconnects:4;
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
// Hints encryption support of peer. Only effective
|
||||
// for and when the outgoing encryption policy
|
||||
// allows both encrypted and non encrypted
|
||||
// connections (pe_settings::out_enc_policy
|
||||
// == enabled). The initial state of this flag
|
||||
// determines the initial connection attempt
|
||||
// type (true = encrypted, false = standard).
|
||||
// This will be toggled everytime either an
|
||||
// encrypted or non-encrypted handshake fails.
|
||||
bool pe_support:1;
|
||||
#endif
|
||||
// true if this peer currently is unchoked
|
||||
// because of an optimistic unchoke.
|
||||
// when the optimistic unchoke is moved to
|
||||
// another peer, this peer will be choked
|
||||
// if this is true
|
||||
bool optimistically_unchoked:1;
|
||||
|
||||
// this is true if the peer is a seed
|
||||
bool seed:1;
|
||||
|
||||
// if this is true, the peer has previously
|
||||
// participated in a piece that failed the piece
|
||||
// hash check. This will put the peer on parole
|
||||
// and only request entire pieces. If a piece pass
|
||||
// that was partially requested from this peer it
|
||||
// will leave parole mode and continue download
|
||||
// pieces as normal peers.
|
||||
bool on_parole;
|
||||
bool on_parole:1;
|
||||
|
||||
// is set to true if this peer has been banned
|
||||
bool banned:1;
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
// this is set to true when this peer as been
|
||||
// pinged by the DHT
|
||||
bool added_to_dht:1;
|
||||
#endif
|
||||
|
||||
// if the peer is connected now, this
|
||||
// will refer to a valid peer_connection
|
||||
peer_connection* connection;
|
||||
|
||||
// this is the accumulated amount of
|
||||
// uploaded and downloaded data to this
|
||||
|
@ -202,16 +229,13 @@ namespace libtorrent
|
|||
size_type prev_amount_upload;
|
||||
size_type prev_amount_download;
|
||||
|
||||
// is set to true if this peer has been banned
|
||||
bool banned;
|
||||
// the time when this peer was optimistically unchoked
|
||||
// the last time.
|
||||
libtorrent::ptime last_optimistically_unchoked;
|
||||
|
||||
// a bitmap combining the peer_source flags
|
||||
// from peer_info.
|
||||
int source;
|
||||
|
||||
// if the peer is connected now, this
|
||||
// will refer to a valid peer_connection
|
||||
peer_connection* connection;
|
||||
// the time when the peer connected to us
|
||||
// or disconnected if it isn't connected right now
|
||||
libtorrent::ptime connected;
|
||||
};
|
||||
|
||||
int num_peers() const { return m_peers.size(); }
|
||||
|
@ -226,23 +250,23 @@ namespace libtorrent
|
|||
bool connect_one_peer();
|
||||
bool disconnect_one_peer();
|
||||
|
||||
private:
|
||||
/*
|
||||
bool unchoke_one_peer();
|
||||
void choke_one_peer();
|
||||
iterator find_choke_candidate();
|
||||
iterator find_unchoke_candidate();
|
||||
bool has_peer(policy::peer const* p) const;
|
||||
|
||||
int num_seeds() const { return m_num_seeds; }
|
||||
int num_connect_candidates() const { return m_num_connect_candidates; }
|
||||
void recalculate_connect_candidates()
|
||||
{
|
||||
if (m_num_connect_candidates == 0)
|
||||
m_num_connect_candidates = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// the seed prefix means that the
|
||||
// function is used while seeding.
|
||||
bool seed_unchoke_one_peer();
|
||||
void seed_choke_one_peer();
|
||||
iterator find_seed_choke_candidate();
|
||||
iterator find_seed_unchoke_candidate();
|
||||
*/
|
||||
iterator find_disconnect_candidate();
|
||||
iterator find_connect_candidate();
|
||||
|
||||
bool is_connect_candidate(peer const& p, bool finished);
|
||||
|
||||
std::multimap<address, peer> m_peers;
|
||||
|
||||
torrent* m_torrent;
|
||||
|
@ -251,10 +275,16 @@ namespace libtorrent
|
|||
// been distributed yet.
|
||||
size_type m_available_free_upload;
|
||||
|
||||
// if there is a connection limit,
|
||||
// we disconnect one peer every minute in hope of
|
||||
// establishing a connection with a better peer
|
||||
// ptime m_last_optimistic_disconnect;
|
||||
// The number of peers in our peer list
|
||||
// that are connect candidates. i.e. they're
|
||||
// not already connected and they have not
|
||||
// yet reached their max try count and they
|
||||
// have the connectable state (we have a listen
|
||||
// port for them).
|
||||
int m_num_connect_candidates;
|
||||
|
||||
// the number of seeds in the peer list
|
||||
int m_num_seeds;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ public:
|
|||
return m_sock.read_some(buffers, ec);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
template <class Mutable_Buffers>
|
||||
std::size_t read_some(Mutable_Buffers const& buffers)
|
||||
{
|
||||
|
@ -86,6 +87,7 @@ public:
|
|||
{
|
||||
m_sock.io_control(ioc);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class IO_Control_Command>
|
||||
void io_control(IO_Control_Command& ioc, asio::error_code& ec)
|
||||
|
@ -99,32 +101,52 @@ public:
|
|||
m_sock.async_write_some(buffers, handler);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
template <class SettableSocketOption>
|
||||
void set_option(SettableSocketOption const& opt)
|
||||
{
|
||||
m_sock.set_option(opt);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class SettableSocketOption>
|
||||
asio::error_code set_option(SettableSocketOption const& opt, asio::error_code& ec)
|
||||
{
|
||||
return m_sock.set_option(opt, ec);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
void bind(endpoint_type const& endpoint)
|
||||
{
|
||||
m_sock.bind(endpoint);
|
||||
}
|
||||
#endif
|
||||
|
||||
void bind(endpoint_type const& endpoint, asio::error_code& ec)
|
||||
{
|
||||
m_sock.bind(endpoint, ec);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
void open(protocol_type const& p)
|
||||
{
|
||||
m_sock.open(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
void open(protocol_type const& p, asio::error_code& ec)
|
||||
{
|
||||
m_sock.open(p, ec);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
void close()
|
||||
{
|
||||
m_remote_endpoint = endpoint_type();
|
||||
m_sock.close();
|
||||
m_resolver.cancel();
|
||||
}
|
||||
#endif
|
||||
|
||||
void close(asio::error_code& ec)
|
||||
{
|
||||
|
@ -132,22 +154,26 @@ public:
|
|||
m_resolver.cancel();
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint()
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
endpoint_type remote_endpoint() const
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
#endif
|
||||
|
||||
endpoint_type remote_endpoint(asio::error_code& ec) const
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint(asio::error_code& ec)
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
|
||||
endpoint_type local_endpoint()
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
endpoint_type local_endpoint() const
|
||||
{
|
||||
return m_sock.local_endpoint();
|
||||
}
|
||||
#endif
|
||||
|
||||
endpoint_type local_endpoint(asio::error_code& ec)
|
||||
endpoint_type local_endpoint(asio::error_code& ec) const
|
||||
{
|
||||
return m_sock.local_endpoint(ec);
|
||||
}
|
||||
|
@ -161,6 +187,8 @@ public:
|
|||
{
|
||||
return m_sock.lowest_layer();
|
||||
}
|
||||
|
||||
bool is_open() const { return m_sock.is_open(); }
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/version.hpp"
|
||||
#include "libtorrent/fingerprint.hpp"
|
||||
#include "libtorrent/time.hpp"
|
||||
#include "libtorrent/disk_io_thread.hpp"
|
||||
|
||||
#include "libtorrent/storage.hpp"
|
||||
|
||||
|
@ -75,6 +76,8 @@ namespace libtorrent
|
|||
class ip_filter;
|
||||
class port_filter;
|
||||
class connection_queue;
|
||||
class natpmp;
|
||||
class upnp;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
|
@ -123,7 +126,7 @@ namespace libtorrent
|
|||
|
||||
session(fingerprint const& print = fingerprint("LT"
|
||||
, LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0)
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
, fs::path logpath = "."
|
||||
#endif
|
||||
);
|
||||
|
@ -131,7 +134,7 @@ namespace libtorrent
|
|||
fingerprint const& print
|
||||
, std::pair<int, int> listen_port_range
|
||||
, char const* listen_interface = "0.0.0.0"
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
, fs::path logpath = "."
|
||||
#endif
|
||||
);
|
||||
|
@ -176,6 +179,10 @@ namespace libtorrent
|
|||
session_proxy abort() { return session_proxy(m_impl); }
|
||||
|
||||
session_status status() const;
|
||||
cache_status get_cache_status() const;
|
||||
|
||||
void get_cache_info(sha1_hash const& ih
|
||||
, std::vector<cached_piece_info>& ret) const;
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
void start_dht(entry const& startup_state = entry());
|
||||
|
@ -195,6 +202,14 @@ namespace libtorrent
|
|||
void add_extension(boost::function<boost::shared_ptr<torrent_plugin>(torrent*, void*)> ext);
|
||||
#endif
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
bool load_asnum_db(char const* file);
|
||||
bool load_country_db(char const* file);
|
||||
#endif
|
||||
|
||||
void load_state(entry const& ses_state);
|
||||
entry state() const;
|
||||
|
||||
void set_ip_filter(ip_filter const& f);
|
||||
void set_port_filter(port_filter const& f);
|
||||
void set_peer_id(peer_id const& pid);
|
||||
|
@ -272,8 +287,8 @@ namespace libtorrent
|
|||
// starts/stops UPnP, NATPMP or LSD port mappers
|
||||
// they are stopped by default
|
||||
void start_lsd();
|
||||
void start_natpmp();
|
||||
void start_upnp();
|
||||
natpmp* start_natpmp();
|
||||
upnp* start_upnp();
|
||||
|
||||
void stop_lsd();
|
||||
void stop_natpmp();
|
||||
|
|
|
@ -102,7 +102,11 @@ namespace libtorrent
|
|||
, min_reconnect_time(60)
|
||||
, peer_connect_timeout(7)
|
||||
, ignore_limits_on_local_network(true)
|
||||
#if !defined NDEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS
|
||||
, connection_speed(2)
|
||||
#else
|
||||
, connection_speed(20)
|
||||
#endif
|
||||
, send_redundant_have(false)
|
||||
, lazy_bitfields(true)
|
||||
, inactivity_timeout(600)
|
||||
|
@ -118,6 +122,12 @@ namespace libtorrent
|
|||
#endif
|
||||
, free_torrent_hashes(true)
|
||||
, upnp_ignore_nonrouters(true)
|
||||
, send_buffer_watermark(80 * 1024)
|
||||
, auto_upload_slots(true)
|
||||
, cache_size(512)
|
||||
, cache_expiry(60)
|
||||
, outgoing_ports(0,0)
|
||||
, peer_tos(0)
|
||||
{}
|
||||
|
||||
// this is the user agent that will be sent to the tracker
|
||||
|
@ -298,6 +308,45 @@ namespace libtorrent
|
|||
// any upnp devices that don't have an address that matches
|
||||
// our currently configured router.
|
||||
bool upnp_ignore_nonrouters;
|
||||
|
||||
// if the send buffer has fewer bytes than this, we'll
|
||||
// read another 16kB block onto it. If set too small,
|
||||
// upload rate capacity will suffer. If set too high,
|
||||
// memory will be wasted.
|
||||
// The actual watermark may be lower than this in case
|
||||
// the upload rate is low, this is the upper limit.
|
||||
int send_buffer_watermark;
|
||||
|
||||
// if auto_upload_slots is true, and a global upload
|
||||
// limit is set and the upload rate is less than 90%
|
||||
// of the upload limit, on new slot is opened up. If
|
||||
// the upload rate is >= upload limit for an extended
|
||||
// period of time, one upload slot is closed. The
|
||||
// upload slots are never automatically decreased below
|
||||
// the manual settings, through max_uploads.
|
||||
bool auto_upload_slots;
|
||||
|
||||
// the disk write cache, specified in 16 KiB blocks.
|
||||
// default is 512 (= 8 MB)
|
||||
int cache_size;
|
||||
|
||||
// the number of seconds a write cache entry sits
|
||||
// idle in the cache before it's forcefully flushed
|
||||
// to disk. Default is 60 seconds.
|
||||
int cache_expiry;
|
||||
|
||||
// if != (0, 0), this is the range of ports that
|
||||
// outgoing connections will be bound to. This
|
||||
// is useful for users that have routers that
|
||||
// allow QoS settings based on local port.
|
||||
std::pair<int, int> outgoing_ports;
|
||||
|
||||
// the TOS byte of all peer traffic (including
|
||||
// web seeds) is set to this value. The default
|
||||
// is the QBSS scavenger service
|
||||
// http://qbone.internet2.edu/qbss/
|
||||
// For unmarked packets, set to 0
|
||||
char peer_tos;
|
||||
};
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
|
|
|
@ -54,6 +54,8 @@ namespace libtorrent
|
|||
size_type total_payload_upload;
|
||||
|
||||
int num_peers;
|
||||
int num_unchoked;
|
||||
int allowed_upload_slots;
|
||||
|
||||
int up_bandwidth_queue;
|
||||
int down_bandwidth_queue;
|
||||
|
|
|
@ -50,7 +50,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <asio/io_service.hpp>
|
||||
#include <asio/deadline_timer.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <asio/strand.hpp>
|
||||
#include <asio/time_traits.hpp>
|
||||
#include <asio/basic_deadline_timer.hpp>
|
||||
|
||||
|
@ -98,6 +97,15 @@ namespace libtorrent
|
|||
|
||||
typedef asio::basic_deadline_timer<libtorrent::ptime> deadline_timer;
|
||||
|
||||
inline std::ostream& print_address(std::ostream& os, address const& addr)
|
||||
{
|
||||
asio::error_code ec;
|
||||
std::string a = addr.to_string(ec);
|
||||
if (ec) return os;
|
||||
os << a;
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& print_endpoint(std::ostream& os, tcp::endpoint const& ep)
|
||||
{
|
||||
address const& addr = ep.address();
|
||||
|
@ -186,6 +194,19 @@ namespace libtorrent
|
|||
int m_value;
|
||||
};
|
||||
|
||||
struct type_of_service
|
||||
{
|
||||
type_of_service(char val): m_value(val) {}
|
||||
template<class Protocol>
|
||||
int level(Protocol const&) const { return IPPROTO_IP; }
|
||||
template<class Protocol>
|
||||
int name(Protocol const&) const { return IP_TOS; }
|
||||
template<class Protocol>
|
||||
char const* data(Protocol const&) const { return &m_value; }
|
||||
template<class Protocol>
|
||||
size_t size(Protocol const&) const { return sizeof(m_value); }
|
||||
char m_value;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TORRENT_SOCKET_HPP_INCLUDED
|
||||
|
|
|
@ -71,6 +71,7 @@ namespace libtorrent
|
|||
class session;
|
||||
struct file_pool;
|
||||
struct disk_io_job;
|
||||
struct disk_buffer_holder;
|
||||
|
||||
enum storage_mode_t
|
||||
{
|
||||
|
@ -119,31 +120,33 @@ namespace libtorrent
|
|||
// if allocate_files is true.
|
||||
// allocate_files is true if allocation mode
|
||||
// is set to full and sparse files are supported
|
||||
virtual void initialize(bool allocate_files) = 0;
|
||||
// false return value indicates an error
|
||||
virtual bool initialize(bool allocate_files) = 0;
|
||||
|
||||
// may throw file_error if storage for slot does not exist
|
||||
virtual size_type read(char* buf, int slot, int offset, int size) = 0;
|
||||
// negative return value indicates an error
|
||||
virtual int read(char* buf, int slot, int offset, int size) = 0;
|
||||
|
||||
// may throw file_error if storage for slot hasn't been allocated
|
||||
virtual void write(const char* buf, int slot, int offset, int size) = 0;
|
||||
// negative return value indicates an error
|
||||
virtual int write(const char* buf, int slot, int offset, int size) = 0;
|
||||
|
||||
// non-zero return value indicates an error
|
||||
virtual bool move_storage(fs::path save_path) = 0;
|
||||
|
||||
// verify storage dependent fast resume entries
|
||||
virtual bool verify_resume_data(entry& rd, std::string& error) = 0;
|
||||
virtual bool verify_resume_data(entry const& rd, std::string& error) = 0;
|
||||
|
||||
// write storage dependent fast resume entries
|
||||
virtual void write_resume_data(entry& rd) const = 0;
|
||||
virtual bool write_resume_data(entry& rd) const = 0;
|
||||
|
||||
// moves (or copies) the content in src_slot to dst_slot
|
||||
virtual void move_slot(int src_slot, int dst_slot) = 0;
|
||||
virtual bool move_slot(int src_slot, int dst_slot) = 0;
|
||||
|
||||
// swaps the data in slot1 and slot2
|
||||
virtual void swap_slots(int slot1, int slot2) = 0;
|
||||
virtual bool swap_slots(int slot1, int slot2) = 0;
|
||||
|
||||
// swaps the puts the data in slot1 in slot2, the data in slot2
|
||||
// in slot3 and the data in slot3 in slot1
|
||||
virtual void swap_slots3(int slot1, int slot2, int slot3) = 0;
|
||||
virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0;
|
||||
|
||||
// returns the sha1-hash for the data at the given slot
|
||||
virtual sha1_hash hash_for_slot(int slot, partial_hash& h, int piece_size) = 0;
|
||||
|
@ -151,10 +154,25 @@ namespace libtorrent
|
|||
// this will close all open files that are opened for
|
||||
// writing. This is called when a torrent has finished
|
||||
// downloading.
|
||||
virtual void release_files() = 0;
|
||||
// non-zero return value indicates an error
|
||||
virtual bool release_files() = 0;
|
||||
|
||||
// this will close all open files and delete them
|
||||
virtual void delete_files() = 0;
|
||||
// non-zero return value indicates an error
|
||||
virtual bool delete_files() = 0;
|
||||
|
||||
void set_error(std::string const& file, std::string const& msg) const
|
||||
{
|
||||
m_error_file = file;
|
||||
m_error = msg;
|
||||
}
|
||||
|
||||
std::string const& error() const { return m_error; }
|
||||
std::string const& error_file() const { return m_error_file; }
|
||||
void clear_error() { m_error.clear(); m_error_file.clear(); }
|
||||
|
||||
mutable std::string m_error;
|
||||
mutable std::string m_error_file;
|
||||
|
||||
virtual ~storage_interface() {}
|
||||
};
|
||||
|
@ -167,6 +185,10 @@ namespace libtorrent
|
|||
boost::intrusive_ptr<torrent_info const> ti
|
||||
, fs::path const& path, file_pool& fp);
|
||||
|
||||
TORRENT_EXPORT storage_interface* mapped_storage_constructor(
|
||||
boost::intrusive_ptr<torrent_info const> ti
|
||||
, fs::path const& path, file_pool& fp);
|
||||
|
||||
struct disk_io_thread;
|
||||
|
||||
class TORRENT_EXPORT piece_manager
|
||||
|
@ -183,50 +205,32 @@ namespace libtorrent
|
|||
, fs::path const& path
|
||||
, file_pool& fp
|
||||
, disk_io_thread& io
|
||||
, storage_constructor_type sc);
|
||||
, storage_constructor_type sc
|
||||
, storage_mode_t sm);
|
||||
|
||||
~piece_manager();
|
||||
|
||||
bool check_fastresume(aux::piece_checker_data& d
|
||||
, std::vector<bool>& pieces, int& num_pieces, storage_mode_t storage_mode
|
||||
, std::string& error_msg);
|
||||
std::pair<bool, float> check_files(std::vector<bool>& pieces
|
||||
, int& num_pieces, boost::recursive_mutex& mutex);
|
||||
|
||||
// frees a buffer that was returned from a read operation
|
||||
void free_buffer(char* buf);
|
||||
torrent_info const* info() const { return m_info.get(); }
|
||||
|
||||
void write_resume_data(entry& rd) const;
|
||||
bool verify_resume_data(entry& rd, std::string& error);
|
||||
|
||||
bool is_allocating() const
|
||||
{ return m_state == state_expand_pieces; }
|
||||
|
||||
void mark_failed(int index);
|
||||
|
||||
unsigned long piece_crc(
|
||||
int slot_index
|
||||
, int block_size
|
||||
, piece_picker::block_info const* bi);
|
||||
|
||||
int slot_for(int piece) const;
|
||||
int piece_for(int slot) const;
|
||||
void async_check_fastresume(entry const* resume_data
|
||||
, boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
void async_check_files(boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
void async_read(
|
||||
peer_request const& r
|
||||
, boost::function<void(int, disk_io_job const&)> const& handler
|
||||
, char* buffer = 0
|
||||
, int priority = 0);
|
||||
|
||||
void async_write(
|
||||
peer_request const& r
|
||||
, char const* buffer
|
||||
, disk_buffer_holder& buffer
|
||||
, boost::function<void(int, disk_io_job const&)> const& f);
|
||||
|
||||
void async_hash(int piece, boost::function<void(int, disk_io_job const&)> const& f);
|
||||
|
||||
fs::path save_path() const;
|
||||
|
||||
void async_release_files(
|
||||
boost::function<void(int, disk_io_job const&)> const& handler
|
||||
= boost::function<void(int, disk_io_job const&)>());
|
||||
|
@ -238,12 +242,48 @@ namespace libtorrent
|
|||
void async_move_storage(fs::path const& p
|
||||
, boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
// fills the vector that maps all allocated
|
||||
// slots to the piece that is stored (or
|
||||
// partially stored) there. -2 is the index
|
||||
// of unassigned pieces and -1 is unallocated
|
||||
void export_piece_map(std::vector<int>& pieces
|
||||
, std::vector<bool> const& have) const;
|
||||
void async_save_resume_data(
|
||||
boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
enum return_t
|
||||
{
|
||||
// return values from check_fastresume and check_files
|
||||
no_error = 0,
|
||||
need_full_check = -1,
|
||||
fatal_disk_error = -2,
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
fs::path save_path() const;
|
||||
|
||||
bool verify_resume_data(entry const& rd, std::string& error)
|
||||
{ return m_storage->verify_resume_data(rd, error); }
|
||||
|
||||
bool is_allocating() const
|
||||
{ return m_state == state_expand_pieces; }
|
||||
|
||||
void mark_failed(int index);
|
||||
|
||||
std::string const& error() const { return m_storage->error(); }
|
||||
std::string const& error_file() const { return m_storage->error_file(); }
|
||||
void clear_error() { m_storage->clear_error(); }
|
||||
|
||||
int slot_for(int piece) const;
|
||||
int piece_for(int slot) const;
|
||||
|
||||
// helper functions for check_dastresume
|
||||
int check_no_fastresume(std::string& error);
|
||||
int check_init_storage(std::string& error);
|
||||
|
||||
// if error is set and return value is 'no_error' or 'need_full_check'
|
||||
// the error message indicates that the fast resume data was rejected
|
||||
// if 'fatal_disk_error' is returned, the error message indicates what
|
||||
// when wrong in the disk access
|
||||
int check_fastresume(entry const& rd, std::string& error);
|
||||
|
||||
// this function returns true if the checking is complete
|
||||
int check_files(int& current_slot, int& have_piece, std::string& error);
|
||||
|
||||
bool compact_allocation() const
|
||||
{ return m_storage_mode == storage_mode_compact; }
|
||||
|
@ -251,36 +291,31 @@ namespace libtorrent
|
|||
#ifndef NDEBUG
|
||||
std::string name() const { return m_info->name(); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
bool allocate_slots(int num_slots, bool abort_on_disk = false);
|
||||
|
||||
int identify_data(
|
||||
const std::vector<char>& piece_data
|
||||
, int current_slot
|
||||
, std::vector<bool>& have_pieces
|
||||
, int& num_pieces
|
||||
, const std::multimap<sha1_hash, int>& hash_to_piece
|
||||
, boost::recursive_mutex& mutex);
|
||||
|
||||
size_type read_impl(
|
||||
int read_impl(
|
||||
char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
, int size);
|
||||
|
||||
void write_impl(
|
||||
int write_impl(
|
||||
const char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
, int size);
|
||||
|
||||
bool check_one_piece(int& have_piece);
|
||||
int identify_data(
|
||||
const std::vector<char>& piece_data
|
||||
, int current_slot);
|
||||
|
||||
void switch_to_full_mode();
|
||||
sha1_hash hash_for_piece_impl(int piece);
|
||||
|
||||
void release_files_impl() { m_storage->release_files(); }
|
||||
void delete_files_impl() { m_storage->delete_files(); }
|
||||
int release_files_impl() { return m_storage->release_files(); }
|
||||
int delete_files_impl() { return m_storage->delete_files(); }
|
||||
|
||||
bool move_storage_impl(fs::path const& save_path);
|
||||
|
||||
|
@ -330,8 +365,6 @@ namespace libtorrent
|
|||
state_none,
|
||||
// the file checking is complete
|
||||
state_finished,
|
||||
// creating the directories
|
||||
state_create_files,
|
||||
// checking the files
|
||||
state_full_check,
|
||||
// move pieces to their final position
|
||||
|
@ -376,9 +409,6 @@ namespace libtorrent
|
|||
// the piece_manager destructs. This is because
|
||||
// the torrent_info object is owned by the torrent.
|
||||
boost::shared_ptr<void> m_torrent;
|
||||
#ifndef NDEBUG
|
||||
bool m_resume_data_verified;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -98,20 +98,19 @@ namespace libtorrent
|
|||
|
||||
torrent(
|
||||
aux::session_impl& ses
|
||||
, aux::checker_impl& checker
|
||||
, boost::intrusive_ptr<torrent_info> tf
|
||||
, fs::path const& save_path
|
||||
, tcp::endpoint const& net_interface
|
||||
, storage_mode_t m_storage_mode
|
||||
, int block_size
|
||||
, storage_constructor_type sc
|
||||
, bool paused);
|
||||
, bool paused
|
||||
, entry const& resume_data);
|
||||
|
||||
// used with metadata-less torrents
|
||||
// (the metadata is downloaded from the peers)
|
||||
torrent(
|
||||
aux::session_impl& ses
|
||||
, aux::checker_impl& checker
|
||||
, char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, char const* name
|
||||
|
@ -120,7 +119,8 @@ namespace libtorrent
|
|||
, storage_mode_t m_storage_mode
|
||||
, int block_size
|
||||
, storage_constructor_type sc
|
||||
, bool paused);
|
||||
, bool paused
|
||||
, entry const& resume_data);
|
||||
|
||||
~torrent();
|
||||
|
||||
|
@ -133,10 +133,21 @@ namespace libtorrent
|
|||
, void* userdata);
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool has_peer(peer_connection* p) const
|
||||
{ return m_connections.find(p) != m_connections.end(); }
|
||||
#endif
|
||||
|
||||
// this is called when the torrent has metadata.
|
||||
// it will initialize the storage and the piece-picker
|
||||
void init();
|
||||
|
||||
void on_resume_data_checked(int ret, disk_io_job const& j);
|
||||
void on_piece_checked(int ret, disk_io_job const& j);
|
||||
void files_checked();
|
||||
void start_checking();
|
||||
|
||||
storage_mode_t storage_mode() const { return m_storage_mode; }
|
||||
// this will flag the torrent as aborted. The main
|
||||
// loop in session_impl will check for this state
|
||||
// on all torrents once every second, and take
|
||||
|
@ -144,19 +155,12 @@ namespace libtorrent
|
|||
void abort();
|
||||
bool is_aborted() const { return m_abort; }
|
||||
|
||||
// returns true if this torrent is being allocated
|
||||
// by the checker thread.
|
||||
bool is_allocating() const;
|
||||
|
||||
session_settings const& settings() const;
|
||||
|
||||
aux::session_impl& session() { return m_ses; }
|
||||
|
||||
void set_sequenced_download_threshold(int threshold);
|
||||
void set_sequential_download(bool sd);
|
||||
|
||||
bool verify_resume_data(entry& rd, std::string& error)
|
||||
{ TORRENT_ASSERT(m_storage); return m_storage->verify_resume_data(rd, error); }
|
||||
|
||||
void second_tick(stat& accumulator, float tick_interval);
|
||||
|
||||
// debug purpose only
|
||||
|
@ -164,11 +168,6 @@ namespace libtorrent
|
|||
|
||||
std::string name() const;
|
||||
|
||||
bool check_fastresume(aux::piece_checker_data&);
|
||||
std::pair<bool, float> check_files();
|
||||
void files_checked(std::vector<piece_picker::downloading_piece> const&
|
||||
unfinished_pieces);
|
||||
|
||||
stat statistics() const { return m_stat; }
|
||||
size_type bytes_left() const;
|
||||
boost::tuples::tuple<size_type, size_type> bytes_done() const;
|
||||
|
@ -179,6 +178,7 @@ namespace libtorrent
|
|||
void pause();
|
||||
void resume();
|
||||
bool is_paused() const { return m_paused; }
|
||||
void save_resume_data();
|
||||
|
||||
void delete_files();
|
||||
|
||||
|
@ -229,12 +229,12 @@ namespace libtorrent
|
|||
|
||||
void request_bandwidth(int channel
|
||||
, boost::intrusive_ptr<peer_connection> const& p
|
||||
, int priority);
|
||||
, int max_block_size, int priority);
|
||||
|
||||
void perform_bandwidth_request(int channel
|
||||
, boost::intrusive_ptr<peer_connection> const& p
|
||||
, int block_size, int priority);
|
||||
|
||||
|
||||
void expire_bandwidth(int channel, int amount);
|
||||
void assign_bandwidth(int channel, int amount, int blk);
|
||||
|
||||
|
@ -243,6 +243,8 @@ namespace libtorrent
|
|||
int max_assignable_bandwidth(int channel) const
|
||||
{ return m_bandwidth_limit[channel].max_assignable(); }
|
||||
|
||||
int bandwidth_queue_size(int channel) const;
|
||||
|
||||
// --------------------------------------------
|
||||
// PEER MANAGEMENT
|
||||
|
||||
|
@ -268,7 +270,8 @@ namespace libtorrent
|
|||
// used by peer_connection to attach itself to a torrent
|
||||
// since incoming connections don't know what torrent
|
||||
// they're a part of until they have received an info_hash.
|
||||
void attach_peer(peer_connection* p);
|
||||
// false means attach failed
|
||||
bool attach_peer(peer_connection* p);
|
||||
|
||||
// this will remove the peer and make sure all
|
||||
// the pieces it had have their reference counter
|
||||
|
@ -279,6 +282,7 @@ namespace libtorrent
|
|||
|
||||
bool want_more_peers() const;
|
||||
bool try_connect_peer();
|
||||
void give_connect_points(int points);
|
||||
|
||||
// the number of peers that belong to this torrent
|
||||
int num_peers() const { return (int)m_connections.size(); }
|
||||
|
@ -295,6 +299,7 @@ namespace libtorrent
|
|||
|
||||
void resolve_peer_country(boost::intrusive_ptr<peer_connection> const& p) const;
|
||||
|
||||
void get_full_peer_list(std::vector<peer_list_entry>& v) const;
|
||||
void get_peer_info(std::vector<peer_info>& v);
|
||||
void get_download_queue(std::vector<partial_piece_info>& queue);
|
||||
|
||||
|
@ -308,7 +313,7 @@ namespace libtorrent
|
|||
virtual void tracker_response(
|
||||
tracker_request const& r
|
||||
, std::vector<peer_entry>& e, int interval
|
||||
, int complete, int incomplete);
|
||||
, int complete, int incomplete, address const& external_ip);
|
||||
virtual void tracker_request_timed_out(
|
||||
tracker_request const& r);
|
||||
virtual void tracker_request_error(tracker_request const& r
|
||||
|
@ -364,8 +369,7 @@ namespace libtorrent
|
|||
|
||||
int num_pieces() const { return m_num_pieces; }
|
||||
|
||||
// when we get a have- or bitfield- messages, this is called for every
|
||||
// piece a peer has gained.
|
||||
// when we get a have message, this is called for that piece
|
||||
void peer_has(int index)
|
||||
{
|
||||
if (m_picker.get())
|
||||
|
@ -382,6 +386,22 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
// when we get a bitfield message, this is called for that piece
|
||||
void peer_has(std::vector<bool> const& bitfield)
|
||||
{
|
||||
if (m_picker.get())
|
||||
{
|
||||
TORRENT_ASSERT(!is_seed());
|
||||
m_picker->inc_refcount(bitfield);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
else
|
||||
{
|
||||
TORRENT_ASSERT(is_seed());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void peer_has_all()
|
||||
{
|
||||
if (m_picker.get())
|
||||
|
@ -397,7 +417,6 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
// when peer disconnects, this is called for every piece it had
|
||||
void peer_lost(int index)
|
||||
{
|
||||
if (m_picker.get())
|
||||
|
@ -450,12 +469,12 @@ namespace libtorrent
|
|||
// completed() is called immediately after it.
|
||||
void finished();
|
||||
|
||||
void async_verify_piece(int piece_index, boost::function<void(bool)> const&);
|
||||
void async_verify_piece(int piece_index, boost::function<void(int)> const&);
|
||||
|
||||
// this is called from the peer_connection
|
||||
// each time a piece has failed the hash
|
||||
// test
|
||||
void piece_finished(int index, bool passed_hash_check);
|
||||
void piece_finished(int index, int passed_hash_check);
|
||||
void piece_failed(int index);
|
||||
void received_redundant_data(int num_bytes)
|
||||
{ TORRENT_ASSERT(num_bytes > 0); m_total_redundant_bytes += num_bytes; }
|
||||
|
@ -496,10 +515,12 @@ namespace libtorrent
|
|||
|
||||
void replace_trackers(std::vector<announce_entry> const& urls);
|
||||
|
||||
torrent_handle get_handle() const;
|
||||
torrent_handle get_handle();
|
||||
|
||||
void write_resume_data(entry& rd) const;
|
||||
|
||||
// LOGGING
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
virtual void debug_log(const std::string& line);
|
||||
#endif
|
||||
|
||||
|
@ -536,7 +557,8 @@ namespace libtorrent
|
|||
// bencoded tree and moves the torrent
|
||||
// to the checker thread for initial checking
|
||||
// of the storage.
|
||||
void set_metadata(entry const&);
|
||||
// a return value of false indicates an error
|
||||
bool set_metadata(entry const& metadata, std::string& error);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -544,9 +566,10 @@ namespace libtorrent
|
|||
void on_files_released(int ret, disk_io_job const& j);
|
||||
void on_torrent_paused(int ret, disk_io_job const& j);
|
||||
void on_storage_moved(int ret, disk_io_job const& j);
|
||||
void on_save_resume_data(int ret, disk_io_job const& j);
|
||||
|
||||
void on_piece_verified(int ret, disk_io_job const& j
|
||||
, boost::function<void(bool)> f);
|
||||
, boost::function<void(int)> f);
|
||||
|
||||
void try_next_tracker();
|
||||
int prioritize_tracker(int tracker_index);
|
||||
|
@ -554,7 +577,7 @@ namespace libtorrent
|
|||
, boost::intrusive_ptr<peer_connection> p) const;
|
||||
bool request_bandwidth_from_session(int channel) const;
|
||||
|
||||
void update_peer_interest();
|
||||
void update_peer_interest(bool was_finished);
|
||||
|
||||
boost::intrusive_ptr<torrent_info> m_torrent_file;
|
||||
|
||||
|
@ -683,7 +706,6 @@ namespace libtorrent
|
|||
// a back reference to the session
|
||||
// this torrent belongs to.
|
||||
aux::session_impl& m_ses;
|
||||
aux::checker_impl& m_checker;
|
||||
|
||||
boost::scoped_ptr<piece_picker> m_picker;
|
||||
|
||||
|
@ -717,7 +739,7 @@ namespace libtorrent
|
|||
// in case the piece picker hasn't been constructed
|
||||
// when this settings is set, this variable will keep
|
||||
// its value until the piece picker is created
|
||||
int m_sequenced_download_threshold;
|
||||
bool m_sequential_download;
|
||||
|
||||
// is false by default and set to
|
||||
// true when the first tracker reponse
|
||||
|
@ -746,6 +768,12 @@ namespace libtorrent
|
|||
// determines the storage state for this torrent.
|
||||
storage_mode_t m_storage_mode;
|
||||
|
||||
// the state of this torrent (queued, checking, downloading)
|
||||
torrent_status::state_t m_state;
|
||||
float m_progress;
|
||||
|
||||
entry m_resume_data;
|
||||
|
||||
// defaults to 16 kiB, but can be set by the user
|
||||
// when creating the torrent
|
||||
const int m_default_block_size;
|
||||
|
@ -795,6 +823,19 @@ namespace libtorrent
|
|||
// total_done - m_initial_done <= total_payload_download
|
||||
size_type m_initial_done;
|
||||
#endif
|
||||
// this is the deficit counter in the Deficit Round Robin
|
||||
// used to determine which torrent gets the next
|
||||
// connection attempt. See:
|
||||
// http://www.ecs.umass.edu/ece/wolf/courses/ECE697J/papers/DRR.pdf
|
||||
// The quanta assigned to each torrent depends on the torrents
|
||||
// priority, whether it's seed and the number of connected
|
||||
// peers it has. This has the effect that some torrents
|
||||
// will have more connection attempts than other. Each
|
||||
// connection attempt costs 100 points from the deficit
|
||||
// counter. points are deducted in try_connect_peer and
|
||||
// increased in give_connect_points. Outside of the
|
||||
// torrent object, these points are called connect_points.
|
||||
int m_deficit_counter;
|
||||
|
||||
policy m_policy;
|
||||
};
|
||||
|
|
|
@ -187,6 +187,10 @@ namespace libtorrent
|
|||
// (including seeds), but are not necessarily
|
||||
// connected to
|
||||
int list_peers;
|
||||
|
||||
// the number of peers in our peerlist that
|
||||
// we potentially could connect to
|
||||
int connect_candidates;
|
||||
|
||||
const std::vector<bool>* pieces;
|
||||
|
||||
|
@ -278,8 +282,9 @@ namespace libtorrent
|
|||
friend struct aux::session_impl;
|
||||
friend class torrent;
|
||||
|
||||
torrent_handle(): m_ses(0), m_chk(0), m_info_hash(0) {}
|
||||
torrent_handle() {}
|
||||
|
||||
void get_full_peer_list(std::vector<peer_list_entry>& v) const;
|
||||
void get_peer_info(std::vector<peer_info>& v) const;
|
||||
torrent_status status() const;
|
||||
void get_download_queue(std::vector<partial_piece_info>& queue) const;
|
||||
|
@ -310,6 +315,7 @@ namespace libtorrent
|
|||
bool is_paused() const;
|
||||
void pause() const;
|
||||
void resume() const;
|
||||
void save_resume_data() const;
|
||||
|
||||
#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
|
||||
void resolve_countries(bool r);
|
||||
|
@ -349,7 +355,9 @@ namespace libtorrent
|
|||
// to.
|
||||
void use_interface(const char* net_interface) const;
|
||||
|
||||
entry write_resume_data() const;
|
||||
// use save_resume_data() instead. It is async. and
|
||||
// will return the resume data in an alert
|
||||
entry write_resume_data() const TORRENT_DEPRECATED;
|
||||
|
||||
// forces this torrent to reannounce
|
||||
// (make a rerequest from the tracker)
|
||||
|
@ -378,7 +386,7 @@ namespace libtorrent
|
|||
void set_download_limit(int limit) const;
|
||||
int download_limit() const;
|
||||
|
||||
void set_sequenced_download_threshold(int threshold) const;
|
||||
void set_sequential_download(bool sd) const;
|
||||
|
||||
void set_peer_upload_limit(tcp::endpoint ip, int limit) const;
|
||||
void set_peer_download_limit(tcp::endpoint ip, int limit) const;
|
||||
|
@ -404,38 +412,28 @@ namespace libtorrent
|
|||
// post condition: save_path() == save_path if true is returned
|
||||
void move_storage(fs::path const& save_path) const;
|
||||
|
||||
const sha1_hash& info_hash() const
|
||||
{ return m_info_hash; }
|
||||
sha1_hash info_hash() const;
|
||||
|
||||
bool operator==(const torrent_handle& h) const
|
||||
{ return m_info_hash == h.m_info_hash; }
|
||||
{ return m_torrent.lock() == h.m_torrent.lock(); }
|
||||
|
||||
bool operator!=(const torrent_handle& h) const
|
||||
{ return m_info_hash != h.m_info_hash; }
|
||||
{ return m_torrent.lock() != h.m_torrent.lock(); }
|
||||
|
||||
bool operator<(const torrent_handle& h) const
|
||||
{ return m_info_hash < h.m_info_hash; }
|
||||
{ return m_torrent.lock() < h.m_torrent.lock(); }
|
||||
|
||||
private:
|
||||
|
||||
torrent_handle(aux::session_impl* s,
|
||||
aux::checker_impl* c,
|
||||
const sha1_hash& h)
|
||||
: m_ses(s)
|
||||
, m_chk(c)
|
||||
, m_info_hash(h)
|
||||
{
|
||||
TORRENT_ASSERT(m_ses != 0);
|
||||
TORRENT_ASSERT(m_chk != 0);
|
||||
}
|
||||
torrent_handle(boost::weak_ptr<torrent> const& t)
|
||||
: m_torrent(t)
|
||||
{}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void check_invariant() const;
|
||||
#endif
|
||||
|
||||
aux::session_impl* m_ses;
|
||||
aux::checker_impl* m_chk;
|
||||
sha1_hash m_info_hash;
|
||||
boost::weak_ptr<torrent> m_torrent;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ namespace libtorrent
|
|||
|
||||
void add_node(std::pair<std::string, int> const& node);
|
||||
|
||||
void parse_info_section(entry const& e);
|
||||
bool parse_info_section(entry const& e, std::string& error);
|
||||
|
||||
entry const* extra(char const* key) const
|
||||
{ return m_extra_info.find_key(key); }
|
||||
|
@ -257,7 +257,7 @@ namespace libtorrent
|
|||
|
||||
private:
|
||||
|
||||
void read_torrent_info(const entry& libtorrent);
|
||||
bool read_torrent_info(const entry& libtorrent, std::string& error);
|
||||
|
||||
// the urls to the trackers
|
||||
std::vector<announce_entry> m_urls;
|
||||
|
|
|
@ -71,9 +71,6 @@ namespace libtorrent
|
|||
struct timeout_handler;
|
||||
struct tracker_connection;
|
||||
|
||||
// encodes a string using the base64 scheme
|
||||
TORRENT_EXPORT std::string base64encode(const std::string& s);
|
||||
|
||||
// returns -1 if gzip header is invalid or the header size in bytes
|
||||
TORRENT_EXPORT int gzip_header(const char* buf, int size);
|
||||
|
||||
|
@ -129,7 +126,8 @@ namespace libtorrent
|
|||
, std::vector<peer_entry>& peers
|
||||
, int interval
|
||||
, int complete
|
||||
, int incomplete) = 0;
|
||||
, int incomplete
|
||||
, address const& external_ip) = 0;
|
||||
virtual void tracker_request_timed_out(
|
||||
tracker_request const&) = 0;
|
||||
virtual void tracker_request_error(
|
||||
|
@ -139,24 +137,18 @@ namespace libtorrent
|
|||
|
||||
tcp::endpoint m_tracker_address;
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
virtual void debug_log(const std::string& line) = 0;
|
||||
#endif
|
||||
private:
|
||||
tracker_manager* m_manager;
|
||||
};
|
||||
|
||||
TORRENT_EXPORT bool inflate_gzip(
|
||||
std::vector<char>& buffer
|
||||
, tracker_request const& req
|
||||
, request_callback* requester
|
||||
, int maximum_tracker_response_length);
|
||||
|
||||
struct TORRENT_EXPORT timeout_handler
|
||||
: intrusive_ptr_base<timeout_handler>
|
||||
, boost::noncopyable
|
||||
{
|
||||
timeout_handler(asio::strand& str);
|
||||
timeout_handler(io_service& str);
|
||||
|
||||
void set_timeout(int completion_timeout, int read_timeout);
|
||||
void restart_read_timeout();
|
||||
|
@ -172,7 +164,6 @@ namespace libtorrent
|
|||
boost::intrusive_ptr<timeout_handler> self()
|
||||
{ return boost::intrusive_ptr<timeout_handler>(this); }
|
||||
|
||||
asio::strand& m_strand;
|
||||
// used for timeouts
|
||||
// this is set when the request has been sent
|
||||
ptime m_start_time;
|
||||
|
@ -194,7 +185,7 @@ namespace libtorrent
|
|||
{
|
||||
tracker_connection(tracker_manager& man
|
||||
, tracker_request const& req
|
||||
, asio::strand& str
|
||||
, io_service& ios
|
||||
, address bind_interface
|
||||
, boost::weak_ptr<request_callback> r);
|
||||
|
||||
|
@ -226,7 +217,7 @@ namespace libtorrent
|
|||
, m_abort(false) {}
|
||||
|
||||
void queue_request(
|
||||
asio::strand& str
|
||||
io_service& ios
|
||||
, connection_queue& cc
|
||||
, tracker_request r
|
||||
, std::string const& auth
|
||||
|
|
|
@ -49,7 +49,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/udp_socket.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
|
@ -65,14 +65,14 @@ namespace libtorrent
|
|||
public:
|
||||
|
||||
udp_tracker_connection(
|
||||
asio::strand& str
|
||||
io_service& ios
|
||||
, connection_queue& cc
|
||||
, tracker_manager& man
|
||||
, tracker_request const& req
|
||||
, std::string const& hostname
|
||||
, unsigned short port
|
||||
, address bind_infc
|
||||
, boost::weak_ptr<request_callback> c
|
||||
, session_settings const& stn);
|
||||
, session_settings const& stn
|
||||
, proxy_settings const& ps);
|
||||
|
||||
void close();
|
||||
|
||||
|
@ -92,30 +92,30 @@ namespace libtorrent
|
|||
void name_lookup(asio::error_code const& error, udp::resolver::iterator i);
|
||||
void timeout(asio::error_code const& error);
|
||||
|
||||
void on_receive(asio::error_code const& e, udp::endpoint const& ep
|
||||
, char const* buf, int size);
|
||||
void on_connect_response(char const* buf, int size);
|
||||
void on_announce_response(char const* buf, int size);
|
||||
void on_scrape_response(char const* buf, int size);
|
||||
|
||||
void send_udp_connect();
|
||||
void connect_response(asio::error_code const& error, std::size_t bytes_transferred);
|
||||
|
||||
void send_udp_announce();
|
||||
void announce_response(asio::error_code const& error, std::size_t bytes_transferred);
|
||||
|
||||
void send_udp_scrape();
|
||||
void scrape_response(asio::error_code const& error, std::size_t bytes_transferred);
|
||||
|
||||
virtual void on_timeout();
|
||||
|
||||
tracker_manager& m_man;
|
||||
|
||||
asio::strand& m_strand;
|
||||
udp::resolver m_name_lookup;
|
||||
datagram_socket m_socket;
|
||||
udp_socket m_socket;
|
||||
udp::endpoint m_target;
|
||||
udp::endpoint m_sender;
|
||||
|
||||
int m_transaction_id;
|
||||
boost::int64_t m_connection_id;
|
||||
session_settings const& m_settings;
|
||||
int m_attempts;
|
||||
std::vector<char> m_buffer;
|
||||
|
||||
action_t m_state;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -58,9 +58,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace libtorrent
|
||||
{
|
||||
|
||||
// int: external tcp port
|
||||
// int: external udp port
|
||||
// int: port-mapping index
|
||||
// int: external port
|
||||
// std::string: error message
|
||||
// an empty string as error means success
|
||||
typedef boost::function<void(int, int, std::string const&)> portmap_callback_t;
|
||||
|
||||
class upnp : public intrusive_ptr_base<upnp>
|
||||
|
@ -71,27 +72,35 @@ public:
|
|||
, portmap_callback_t const& cb, bool ignore_nonrouters);
|
||||
~upnp();
|
||||
|
||||
// maps the ports, if a port is set to 0
|
||||
// it will not be mapped
|
||||
void set_mappings(int tcp, int udp);
|
||||
enum protocol_type { none = 0, udp = 1, tcp = 2 };
|
||||
int add_mapping(protocol_type p, int external_port, int local_port);
|
||||
void delete_mapping(int mapping_index);
|
||||
|
||||
void discover_device();
|
||||
void close();
|
||||
|
||||
std::string router_model()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return m_model;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void discover_device_impl();
|
||||
static address_v4 upnp_multicast_address;
|
||||
static udp::endpoint upnp_multicast_endpoint;
|
||||
|
||||
enum { num_mappings = 2 };
|
||||
enum { default_lease_time = 3600 };
|
||||
|
||||
void update_mapping(int i, int port);
|
||||
void resend_request(asio::error_code const& e);
|
||||
void on_reply(udp::endpoint const& from, char* buffer
|
||||
, std::size_t bytes_transferred);
|
||||
|
||||
struct rootdevice;
|
||||
void next(rootdevice& d, int i);
|
||||
void update_map(rootdevice& d, int i);
|
||||
|
||||
|
||||
void on_upnp_xml(asio::error_code const& e
|
||||
, libtorrent::http_parser const& p, rootdevice& d);
|
||||
|
@ -103,28 +112,43 @@ private:
|
|||
, int mapping);
|
||||
void on_expire(asio::error_code const& e);
|
||||
|
||||
void map_port(rootdevice& d, int i);
|
||||
void unmap_port(rootdevice& d, int i);
|
||||
void disable();
|
||||
void disable(char const* msg);
|
||||
void return_error(int mapping, int code);
|
||||
|
||||
void delete_port_mapping(rootdevice& d, int i);
|
||||
void create_port_mapping(http_connection& c, rootdevice& d, int i);
|
||||
void post(upnp::rootdevice const& d, std::string const& soap
|
||||
, std::string const& soap_action);
|
||||
|
||||
int num_mappings() const { return int(m_mappings.size()); }
|
||||
|
||||
struct global_mapping_t
|
||||
{
|
||||
global_mapping_t()
|
||||
: protocol(none)
|
||||
, external_port(0)
|
||||
, local_port(0)
|
||||
{}
|
||||
int protocol;
|
||||
int external_port;
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct mapping_t
|
||||
{
|
||||
enum action_t { action_none, action_add, action_delete };
|
||||
mapping_t()
|
||||
: need_update(false)
|
||||
: action(action_none)
|
||||
, local_port(0)
|
||||
, external_port(0)
|
||||
, protocol(1)
|
||||
, protocol(none)
|
||||
, failcount(0)
|
||||
{}
|
||||
|
||||
// the time the port mapping will expire
|
||||
ptime expires;
|
||||
|
||||
bool need_update;
|
||||
int action;
|
||||
|
||||
// the local port for this mapping. If this is set
|
||||
// to 0, the mapping is not in use
|
||||
|
@ -135,8 +159,11 @@ private:
|
|||
// should announce to others
|
||||
int external_port;
|
||||
|
||||
// 1 = udp, 0 = tcp
|
||||
// 2 = udp, 1 = tcp
|
||||
int protocol;
|
||||
|
||||
// the number of times this mapping has failed
|
||||
int failcount;
|
||||
};
|
||||
|
||||
struct rootdevice
|
||||
|
@ -146,8 +173,6 @@ private:
|
|||
, supports_specific_external(true)
|
||||
, disabled(false)
|
||||
{
|
||||
mapping[0].protocol = 0;
|
||||
mapping[1].protocol = 1;
|
||||
#ifndef NDEBUG
|
||||
magic = 1337;
|
||||
#endif
|
||||
|
@ -160,7 +185,7 @@ private:
|
|||
magic = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// the interface url, through which the list of
|
||||
// supported interfaces are fetched
|
||||
std::string url;
|
||||
|
@ -170,7 +195,7 @@ private:
|
|||
// either the WANIP namespace or the WANPPP namespace
|
||||
char const* service_namespace;
|
||||
|
||||
mapping_t mapping[num_mappings];
|
||||
std::vector<mapping_t> mapping;
|
||||
|
||||
std::string hostname;
|
||||
int port;
|
||||
|
@ -200,8 +225,7 @@ private:
|
|||
{ return url < rhs.url; }
|
||||
};
|
||||
|
||||
int m_udp_local_port;
|
||||
int m_tcp_local_port;
|
||||
std::vector<global_mapping_t> m_mappings;
|
||||
|
||||
std::string const& m_user_agent;
|
||||
|
||||
|
@ -215,8 +239,6 @@ private:
|
|||
|
||||
asio::io_service& m_io_service;
|
||||
|
||||
asio::strand m_strand;
|
||||
|
||||
// the udp socket used to send and receive
|
||||
// multicast messages on the network
|
||||
broadcast_socket m_socket;
|
||||
|
@ -234,6 +256,11 @@ private:
|
|||
|
||||
connection_queue& m_cc;
|
||||
|
||||
typedef boost::mutex mutex_t;
|
||||
mutex_t m_mutex;
|
||||
|
||||
std::string m_model;
|
||||
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
std::ofstream m_log;
|
||||
#endif
|
||||
|
|
|
@ -182,6 +182,20 @@ namespace aux
|
|||
Protocol const& proto;
|
||||
};
|
||||
|
||||
// -------------- is_open -----------
|
||||
|
||||
struct is_open_visitor
|
||||
: boost::static_visitor<bool>
|
||||
{
|
||||
is_open_visitor() {}
|
||||
|
||||
template <class T>
|
||||
bool operator()(T const* p) const
|
||||
{ return p->is_open(); }
|
||||
|
||||
bool operator()(boost::blank) const { return false; }
|
||||
};
|
||||
|
||||
// -------------- close -----------
|
||||
|
||||
struct close_visitor_ec
|
||||
|
@ -221,7 +235,7 @@ namespace aux
|
|||
{}
|
||||
|
||||
template <class T>
|
||||
EndpointType operator()(T* p) const
|
||||
EndpointType operator()(T const* p) const
|
||||
{ return p->remote_endpoint(error_code); }
|
||||
|
||||
EndpointType operator()(boost::blank) const
|
||||
|
@ -235,13 +249,52 @@ namespace aux
|
|||
: boost::static_visitor<EndpointType>
|
||||
{
|
||||
template <class T>
|
||||
EndpointType operator()(T* p) const
|
||||
EndpointType operator()(T const* p) const
|
||||
{ return p->remote_endpoint(); }
|
||||
|
||||
EndpointType operator()(boost::blank) const
|
||||
{ return EndpointType(); }
|
||||
};
|
||||
|
||||
// -------------- set_option -----------
|
||||
|
||||
template <class SettableSocketOption>
|
||||
struct set_option_visitor
|
||||
: boost::static_visitor<>
|
||||
{
|
||||
set_option_visitor(SettableSocketOption const& opt)
|
||||
: opt_(opt)
|
||||
{}
|
||||
|
||||
template <class T>
|
||||
void operator()(T* p) const
|
||||
{ p->set_option(opt_); }
|
||||
|
||||
void operator()(boost::blank) const {}
|
||||
|
||||
SettableSocketOption const& opt_;
|
||||
};
|
||||
|
||||
template <class SettableSocketOption>
|
||||
struct set_option_visitor_ec
|
||||
: boost::static_visitor<asio::error_code>
|
||||
{
|
||||
set_option_visitor_ec(SettableSocketOption const& opt, asio::error_code& ec)
|
||||
: opt_(opt)
|
||||
, ec_(ec)
|
||||
{}
|
||||
|
||||
template <class T>
|
||||
asio::error_code operator()(T* p) const
|
||||
{ return p->set_option(opt_, ec_); }
|
||||
|
||||
asio::error_code operator()(boost::blank) const
|
||||
{ return ec_; }
|
||||
|
||||
SettableSocketOption const& opt_;
|
||||
asio::error_code& ec_;
|
||||
};
|
||||
|
||||
// -------------- local_endpoint -----------
|
||||
|
||||
template <class EndpointType>
|
||||
|
@ -253,7 +306,7 @@ namespace aux
|
|||
{}
|
||||
|
||||
template <class T>
|
||||
EndpointType operator()(T* p) const
|
||||
EndpointType operator()(T const* p) const
|
||||
{
|
||||
return p->local_endpoint(error_code);
|
||||
}
|
||||
|
@ -271,7 +324,7 @@ namespace aux
|
|||
: boost::static_visitor<EndpointType>
|
||||
{
|
||||
template <class T>
|
||||
EndpointType operator()(T* p) const
|
||||
EndpointType operator()(T const* p) const
|
||||
{
|
||||
return p->local_endpoint();
|
||||
}
|
||||
|
@ -379,7 +432,7 @@ namespace aux
|
|||
{}
|
||||
|
||||
template <class T>
|
||||
std::size_t operator()(T* p) const
|
||||
std::size_t operator()(T const* p) const
|
||||
{
|
||||
return p->in_avail(ec);
|
||||
}
|
||||
|
@ -396,7 +449,7 @@ namespace aux
|
|||
: boost::static_visitor<std::size_t>
|
||||
{
|
||||
template <class T>
|
||||
std::size_t operator()(T* p) const
|
||||
std::size_t operator()(T const* p) const
|
||||
{
|
||||
return p->in_avail();
|
||||
}
|
||||
|
@ -414,7 +467,7 @@ namespace aux
|
|||
template <class T>
|
||||
IOService& operator()(T* p) const
|
||||
{
|
||||
return p->io_service();
|
||||
return p->get_io_service();
|
||||
}
|
||||
|
||||
IOService& operator()(boost::blank) const
|
||||
|
@ -471,11 +524,13 @@ public:
|
|||
typedef typename S0::endpoint_type endpoint_type;
|
||||
typedef typename S0::protocol_type protocol_type;
|
||||
|
||||
explicit variant_stream() : m_variant(boost::blank()) {}
|
||||
explicit variant_stream(asio::io_service& ios)
|
||||
: m_io_service(ios), m_variant(boost::blank()) {}
|
||||
|
||||
template <class S>
|
||||
void instantiate(asio::io_service& ios)
|
||||
{
|
||||
TORRENT_ASSERT(&ios == &m_io_service);
|
||||
std::auto_ptr<S> owned(new S(ios));
|
||||
boost::apply_visitor(aux::delete_visitor(), m_variant);
|
||||
m_variant = owned.get();
|
||||
|
@ -594,6 +649,11 @@ public:
|
|||
);
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
{
|
||||
return boost::apply_visitor(aux::is_open_visitor(), m_variant);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (!instantiated()) return;
|
||||
|
@ -608,13 +668,13 @@ public:
|
|||
);
|
||||
}
|
||||
|
||||
std::size_t in_avail()
|
||||
std::size_t in_avail() const
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(aux::in_avail_visitor(), m_variant);
|
||||
}
|
||||
|
||||
std::size_t in_avail(asio::error_code& ec)
|
||||
std::size_t in_avail(asio::error_code& ec) const
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(
|
||||
|
@ -622,13 +682,13 @@ public:
|
|||
);
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint()
|
||||
endpoint_type remote_endpoint() const
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(aux::remote_endpoint_visitor<endpoint_type>(), m_variant);
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint(asio::error_code& ec)
|
||||
endpoint_type remote_endpoint(asio::error_code& ec) const
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(
|
||||
|
@ -636,13 +696,29 @@ public:
|
|||
);
|
||||
}
|
||||
|
||||
endpoint_type local_endpoint()
|
||||
template <class SettableSocketOption>
|
||||
void set_option(SettableSocketOption const& opt)
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
boost::apply_visitor(aux::set_option_visitor<SettableSocketOption>(opt)
|
||||
, m_variant);
|
||||
}
|
||||
|
||||
template <class SettableSocketOption>
|
||||
asio::error_code set_option(SettableSocketOption const& opt, asio::error_code& ec)
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(aux::set_option_visitor_ec<SettableSocketOption>(opt, ec)
|
||||
, m_variant);
|
||||
}
|
||||
|
||||
endpoint_type local_endpoint() const
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(aux::local_endpoint_visitor<endpoint_type>(), m_variant);
|
||||
}
|
||||
|
||||
endpoint_type local_endpoint(asio::error_code& ec)
|
||||
endpoint_type local_endpoint(asio::error_code& ec) const
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(
|
||||
|
@ -650,12 +726,9 @@ public:
|
|||
);
|
||||
}
|
||||
|
||||
asio::io_service& io_service()
|
||||
asio::io_service& get_io_service()
|
||||
{
|
||||
TORRENT_ASSERT(instantiated());
|
||||
return boost::apply_visitor(
|
||||
aux::io_service_visitor<asio::io_service>(), m_variant
|
||||
);
|
||||
return m_io_service;
|
||||
}
|
||||
|
||||
lowest_layer_type& lowest_layer()
|
||||
|
@ -667,6 +740,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
asio::io_service& m_io_service;
|
||||
variant_type m_variant;
|
||||
};
|
||||
|
||||
|
|
|
@ -70,8 +70,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/config.hpp"
|
||||
// parse_url
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
// http_parser
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -123,7 +122,7 @@ namespace libtorrent
|
|||
void write_cancel(peer_request const& r)
|
||||
{ incoming_reject_request(r); }
|
||||
void write_have(int index) {}
|
||||
void write_piece(peer_request const& r, char* buffer) { TORRENT_ASSERT(false); }
|
||||
void write_piece(peer_request const& r, disk_buffer_holder& buffer) { TORRENT_ASSERT(false); }
|
||||
void write_keepalive() {}
|
||||
void on_connected();
|
||||
void write_reject_request(peer_request const&) {}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,6 +30,35 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
*/
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
std::string demangle(char const* name)
|
||||
{
|
||||
// in case this string comes
|
||||
char const* start = strchr(name, '(');
|
||||
if (start != 0) ++start;
|
||||
else start = name;
|
||||
char const* end = strchr(start, '+');
|
||||
|
||||
std::string in;
|
||||
if (end == 0) in.assign(start);
|
||||
else in.assign(start, end);
|
||||
|
||||
size_t len;
|
||||
int status;
|
||||
char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status);
|
||||
if (unmangled == 0) return in;
|
||||
std::string ret(unmangled);
|
||||
free(unmangled);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -58,7 +87,7 @@ void assert_fail(char const* expr, int line, char const* file, char const* funct
|
|||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
fprintf(stderr, "%d: %s\n", i, symbols[i]);
|
||||
fprintf(stderr, "%d: %s\n", i, demangle(symbols[i]).c_str());
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
|
|
|
@ -151,6 +151,11 @@ namespace libtorrent
|
|||
asio::error_code ec;
|
||||
std::vector<ip_interface> interfaces = enum_net_interfaces(ios, ec);
|
||||
|
||||
if (multicast_endpoint.address().is_v4())
|
||||
open_multicast_socket(ios, address_v4::any(), loopback);
|
||||
else
|
||||
open_multicast_socket(ios, address_v6::any(), loopback);
|
||||
|
||||
for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
|
||||
, end(interfaces.end()); i != end; ++i)
|
||||
{
|
||||
|
@ -161,58 +166,70 @@ namespace libtorrent
|
|||
// ignore any loopback interface
|
||||
if (is_loopback(i->interface_address)) continue;
|
||||
|
||||
boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
|
||||
if (i->interface_address.is_v4())
|
||||
{
|
||||
s->open(udp::v4(), ec);
|
||||
if (ec) continue;
|
||||
s->set_option(datagram_socket::reuse_address(true), ec);
|
||||
if (ec) continue;
|
||||
s->bind(udp::endpoint(address_v4::any(), multicast_endpoint.port()), ec);
|
||||
if (ec) continue;
|
||||
s->set_option(join_group(multicast_endpoint.address()), ec);
|
||||
if (ec) continue;
|
||||
s->set_option(outbound_interface(i->interface_address.to_v4()), ec);
|
||||
if (ec) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->open(udp::v6(), ec);
|
||||
if (ec) continue;
|
||||
s->set_option(datagram_socket::reuse_address(true), ec);
|
||||
if (ec) continue;
|
||||
s->bind(udp::endpoint(address_v6::any(), multicast_endpoint.port()), ec);
|
||||
if (ec) continue;
|
||||
s->set_option(join_group(multicast_endpoint.address()), ec);
|
||||
if (ec) continue;
|
||||
// s->set_option(outbound_interface(i->interface_address.to_v6()), ec);
|
||||
// if (ec) continue;
|
||||
}
|
||||
s->set_option(hops(255), ec);
|
||||
if (ec) continue;
|
||||
s->set_option(enable_loopback(loopback), ec);
|
||||
if (ec) continue;
|
||||
m_sockets.push_back(socket_entry(s));
|
||||
socket_entry& se = m_sockets.back();
|
||||
s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
|
||||
, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
|
||||
#ifndef NDEBUG
|
||||
// std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
|
||||
// << " group: " << multicast_endpoint.address() << " ]" << std::endl;
|
||||
#endif
|
||||
open_unicast_socket(ios, i->interface_address);
|
||||
}
|
||||
}
|
||||
|
||||
void broadcast_socket::open_multicast_socket(io_service& ios
|
||||
, address const& addr, bool loopback)
|
||||
{
|
||||
using namespace asio::ip::multicast;
|
||||
|
||||
asio::error_code ec;
|
||||
boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
|
||||
if (addr.is_v4())
|
||||
s->open(udp::v4(), ec);
|
||||
else
|
||||
s->open(udp::v6(), ec);
|
||||
if (ec) return;
|
||||
s->set_option(datagram_socket::reuse_address(true), ec);
|
||||
if (ec) return;
|
||||
s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec);
|
||||
if (ec) return;
|
||||
s->set_option(join_group(m_multicast_endpoint.address()), ec);
|
||||
if (ec) return;
|
||||
s->set_option(hops(255), ec);
|
||||
if (ec) return;
|
||||
s->set_option(enable_loopback(loopback), ec);
|
||||
if (ec) return;
|
||||
m_sockets.push_back(socket_entry(s));
|
||||
socket_entry& se = m_sockets.back();
|
||||
s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
|
||||
, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
|
||||
}
|
||||
|
||||
void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
|
||||
{
|
||||
using namespace asio::ip::multicast;
|
||||
asio::error_code ec;
|
||||
boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
|
||||
s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
|
||||
if (ec) return;
|
||||
s->bind(udp::endpoint(addr, 0), ec);
|
||||
if (ec) return;
|
||||
if (addr.is_v4())
|
||||
s->set_option(outbound_interface(addr.to_v4()), ec);
|
||||
if (ec) return;
|
||||
m_unicast_sockets.push_back(socket_entry(s));
|
||||
socket_entry& se = m_unicast_sockets.back();
|
||||
s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
|
||||
, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
|
||||
}
|
||||
|
||||
void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
|
||||
{
|
||||
for (std::list<socket_entry>::iterator i = m_sockets.begin()
|
||||
, end(m_sockets.end()); i != end; ++i)
|
||||
for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
|
||||
, end(m_unicast_sockets.end()); i != end; ++i)
|
||||
{
|
||||
if (!i->socket) continue;
|
||||
asio::error_code e;
|
||||
i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
|
||||
#ifndef NDEBUG
|
||||
// std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << std::endl;
|
||||
// std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
|
||||
#endif
|
||||
if (e)
|
||||
{
|
||||
|
@ -234,14 +251,10 @@ namespace libtorrent
|
|||
|
||||
void broadcast_socket::close()
|
||||
{
|
||||
m_on_receive.clear();
|
||||
std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
|
||||
std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));
|
||||
|
||||
for (std::list<socket_entry>::iterator i = m_sockets.begin()
|
||||
, end(m_sockets.end()); i != end; ++i)
|
||||
{
|
||||
if (!i->socket) continue;
|
||||
i->socket->close();
|
||||
}
|
||||
m_on_receive.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -163,13 +163,19 @@ namespace libtorrent
|
|||
m_bandwidth_limit[upload_channel].assign(80);
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
m_in_constructor = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void bt_peer_connection::start()
|
||||
{
|
||||
peer_connection::start();
|
||||
|
||||
// start in the state where we are trying to read the
|
||||
// handshake from the other side
|
||||
reset_recv_buffer(20);
|
||||
setup_receive();
|
||||
#ifndef NDEBUG
|
||||
m_in_constructor = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bt_peer_connection::~bt_peer_connection()
|
||||
|
@ -382,6 +388,7 @@ namespace libtorrent
|
|||
#endif
|
||||
|
||||
buffer::interval send_buf = allocate_send_buffer(dh_key_len + pad_size);
|
||||
if (send_buf.begin == 0) return; // out of memory
|
||||
|
||||
std::copy(m_DH_key_exchange->get_local_key(),
|
||||
m_DH_key_exchange->get_local_key() + dh_key_len,
|
||||
|
@ -416,6 +423,7 @@ namespace libtorrent
|
|||
// synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia)
|
||||
buffer::interval send_buf =
|
||||
allocate_send_buffer(20 + 20 + 8 + 4 + 2 + pad_size + 2);
|
||||
if (send_buf.begin == 0) return; // out of memory
|
||||
|
||||
// sync hash (hash('req1',S))
|
||||
h.reset();
|
||||
|
@ -490,6 +498,7 @@ namespace libtorrent
|
|||
|
||||
const int buf_size = 8 + 4 + 2 + pad_size;
|
||||
buffer::interval send_buf = allocate_send_buffer(buf_size);
|
||||
if (send_buf.begin == 0) return; // out of memory
|
||||
write_pe_vc_cryptofield(send_buf, crypto_select, pad_size);
|
||||
|
||||
m_RC4_handler->encrypt(send_buf.end - buf_size, buf_size);
|
||||
|
@ -682,6 +691,7 @@ namespace libtorrent
|
|||
const int string_len = sizeof(version_string)-1;
|
||||
|
||||
buffer::interval i = allocate_send_buffer(1 + string_len + 8 + 20 + 20);
|
||||
if (i.begin == 0) return; // out of memory
|
||||
// length of version string
|
||||
*i.begin = string_len;
|
||||
++i.begin;
|
||||
|
@ -789,7 +799,10 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 1)
|
||||
throw protocol_error("'choke' message size != 1");
|
||||
{
|
||||
disconnect("'choke' message size != 1");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -820,7 +833,10 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 1)
|
||||
throw protocol_error("'unchoke' message size != 1");
|
||||
{
|
||||
disconnect("'unchoke' message size != 1");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -837,7 +853,10 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 1)
|
||||
throw protocol_error("'interested' message size != 1");
|
||||
{
|
||||
disconnect("'interested' message size != 1");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -854,7 +873,10 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 1)
|
||||
throw protocol_error("'not interested' message size != 1");
|
||||
{
|
||||
disconnect("'not interested' message size != 1");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -871,7 +893,10 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 5)
|
||||
throw protocol_error("'have' message size != 5");
|
||||
{
|
||||
disconnect("'have' message size != 5");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -900,7 +925,10 @@ namespace libtorrent
|
|||
// verify the bitfield size
|
||||
if (t->valid_metadata()
|
||||
&& packet_size() - 1 != ((int)get_bitfield().size() + 7) / 8)
|
||||
throw protocol_error("bitfield with invalid size");
|
||||
{
|
||||
disconnect("bitfield with invalid size");
|
||||
return;
|
||||
}
|
||||
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
@ -934,7 +962,10 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 13)
|
||||
throw protocol_error("'request' message size != 13");
|
||||
{
|
||||
disconnect("'request' message size != 13");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -962,6 +993,14 @@ namespace libtorrent
|
|||
buffer::const_interval recv_buffer = receive_buffer();
|
||||
int recv_pos = recv_buffer.end - recv_buffer.begin;
|
||||
|
||||
if (recv_pos == 1)
|
||||
{
|
||||
TORRENT_ASSERT(!has_disk_receive_buffer());
|
||||
if (!allocate_disk_receive_buffer(packet_size() - 9))
|
||||
return;
|
||||
}
|
||||
TORRENT_ASSERT(has_disk_receive_buffer());
|
||||
|
||||
// classify the received data as protocol chatter
|
||||
// or data payload for the statistics
|
||||
if (recv_pos <= 9)
|
||||
|
@ -990,7 +1029,8 @@ namespace libtorrent
|
|||
p.start = detail::read_int32(ptr);
|
||||
p.length = packet_size() - 9;
|
||||
|
||||
incoming_piece(p, recv_buffer.begin + 9);
|
||||
disk_buffer_holder holder(m_ses, release_disk_receive_buffer());
|
||||
incoming_piece(p, holder);
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
|
@ -1003,7 +1043,10 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 13)
|
||||
throw protocol_error("'cancel' message size != 13");
|
||||
{
|
||||
disconnect("'cancel' message size != 13");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -1027,11 +1070,17 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
|
||||
if (!m_supports_dht_port)
|
||||
throw protocol_error("got 'dht_port' message from peer that doesn't support it");
|
||||
{
|
||||
disconnect("got 'dht_port' message from peer that doesn't support it");
|
||||
return;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(received > 0);
|
||||
if (packet_size() != 3)
|
||||
throw protocol_error("'dht_port' message size != 3");
|
||||
{
|
||||
disconnect("'dht_port' message size != 3");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -1048,7 +1097,10 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
|
||||
if (!m_supports_fast)
|
||||
throw protocol_error("got 'suggest_piece' without FAST extension support");
|
||||
{
|
||||
disconnect("got 'suggest_piece' without FAST excension support");
|
||||
return;
|
||||
}
|
||||
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
@ -1065,7 +1117,10 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
|
||||
if (!m_supports_fast)
|
||||
throw protocol_error("got 'have_all' without FAST extension support");
|
||||
{
|
||||
disconnect("got 'have_all' without FAST extension support");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
incoming_have_all();
|
||||
}
|
||||
|
@ -1075,7 +1130,10 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
|
||||
if (!m_supports_fast)
|
||||
throw protocol_error("got 'have_none' without FAST extension support");
|
||||
{
|
||||
disconnect("got 'have_none' without FAST extension support");
|
||||
return;
|
||||
}
|
||||
m_statistics.received_bytes(0, received);
|
||||
incoming_have_none();
|
||||
}
|
||||
|
@ -1085,7 +1143,10 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
|
||||
if (!m_supports_fast)
|
||||
throw protocol_error("got 'reject_request' without FAST extension support");
|
||||
{
|
||||
disconnect("got 'reject_request' without FAST extension support");
|
||||
return;
|
||||
}
|
||||
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
@ -1106,7 +1167,10 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
|
||||
if (!m_supports_fast)
|
||||
throw protocol_error("got 'allowed_fast' without FAST extension support");
|
||||
{
|
||||
disconnect("got 'allowed_fast' without FAST extension support");
|
||||
return;
|
||||
}
|
||||
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (!packet_finished()) return;
|
||||
|
@ -1128,10 +1192,16 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(received > 0);
|
||||
m_statistics.received_bytes(0, received);
|
||||
if (packet_size() < 2)
|
||||
throw protocol_error("'extended' message smaller than 2 bytes");
|
||||
{
|
||||
disconnect("'extended' message smaller than 2 bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
if (associated_torrent().expired())
|
||||
throw protocol_error("'extended' message sent before proper handshake");
|
||||
{
|
||||
disconnect("'extended' message sent before proper handshake");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer::const_interval recv_buffer = receive_buffer();
|
||||
if (recv_buffer.left() < 2) return;
|
||||
|
@ -1157,8 +1227,10 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
throw protocol_error("unknown extended message id: "
|
||||
+ boost::lexical_cast<std::string>(extended_id));
|
||||
std::stringstream msg;
|
||||
msg << "unknown extended message id: " << extended_id;
|
||||
disconnect(msg.str().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
void bt_peer_connection::on_extended_handshake()
|
||||
|
@ -1171,15 +1243,11 @@ namespace libtorrent
|
|||
buffer::const_interval recv_buffer = receive_buffer();
|
||||
|
||||
entry root;
|
||||
try
|
||||
root = bdecode(recv_buffer.begin + 2, recv_buffer.end);
|
||||
if (root.type() == entry::undefined_t)
|
||||
{
|
||||
root = bdecode(recv_buffer.begin + 2, recv_buffer.end);
|
||||
}
|
||||
catch (std::exception& exc)
|
||||
{
|
||||
(void)exc;
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << "invalid extended handshake: " << exc.what() << "\n";
|
||||
(*m_logger) << "invalid extended handshake\n";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -1240,13 +1308,13 @@ namespace libtorrent
|
|||
{
|
||||
address_v4::bytes_type bytes;
|
||||
std::copy(my_ip.begin(), my_ip.end(), bytes.begin());
|
||||
m_ses.m_external_address = address_v4(bytes);
|
||||
m_ses.set_external_address(address_v4(bytes));
|
||||
}
|
||||
else if (my_ip.size() == address_v6::bytes_type::static_size)
|
||||
{
|
||||
address_v6::bytes_type bytes;
|
||||
std::copy(my_ip.begin(), my_ip.end(), bytes.begin());
|
||||
m_ses.m_external_address = address_v6(bytes);
|
||||
m_ses.set_external_address(address_v6(bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1263,6 +1331,7 @@ namespace libtorrent
|
|||
|
||||
buffer::const_interval recv_buffer = receive_buffer();
|
||||
|
||||
TORRENT_ASSERT(recv_buffer.left() >= 1);
|
||||
int packet_type = recv_buffer[0];
|
||||
if (packet_type < 0
|
||||
|| packet_type >= num_supported_messages
|
||||
|
@ -1279,9 +1348,10 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
throw protocol_error("unknown message id: "
|
||||
+ boost::lexical_cast<std::string>(packet_type)
|
||||
+ " size: " + boost::lexical_cast<std::string>(packet_size()));
|
||||
std::stringstream msg;
|
||||
msg << "unkown message id: " << packet_type << " size: " << packet_size();
|
||||
disconnect(msg.str().c_str());
|
||||
return packet_finished();
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(m_message_handler[packet_type] != 0);
|
||||
|
@ -1366,6 +1436,14 @@ namespace libtorrent
|
|||
send_allowed_set();
|
||||
return;
|
||||
}
|
||||
else if (t->num_pieces() == 0)
|
||||
{
|
||||
// don't send a bitfield if we don't have any pieces
|
||||
#ifndef NDEBUG
|
||||
m_sent_bitfield = true;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
int num_pieces = bitfield.size();
|
||||
int lazy_pieces[50];
|
||||
|
@ -1409,6 +1487,7 @@ namespace libtorrent
|
|||
const int packet_size = (num_pieces + 7) / 8 + 5;
|
||||
|
||||
buffer::interval i = allocate_send_buffer(packet_size);
|
||||
if (i.begin == 0) return; // out of memory
|
||||
|
||||
detail::write_int32(packet_size - 4, i.begin);
|
||||
detail::write_uint8(msg_bitfield, i.begin);
|
||||
|
@ -1497,6 +1576,7 @@ namespace libtorrent
|
|||
|
||||
// make room for message
|
||||
buffer::interval i = allocate_send_buffer(6 + msg.size());
|
||||
if (i.begin == 0) return; // out of memory
|
||||
|
||||
// write the length of the message
|
||||
detail::write_int32((int)msg.size() + 2, i.begin);
|
||||
|
@ -1573,7 +1653,7 @@ namespace libtorrent
|
|||
send_buffer(msg, sizeof(msg));
|
||||
}
|
||||
|
||||
void bt_peer_connection::write_piece(peer_request const& r, char* buffer)
|
||||
void bt_peer_connection::write_piece(peer_request const& r, disk_buffer_holder& buffer)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
|
@ -1591,9 +1671,10 @@ namespace libtorrent
|
|||
detail::write_int32(r.start, ptr);
|
||||
send_buffer(msg, sizeof(msg));
|
||||
|
||||
append_send_buffer(buffer, r.length
|
||||
append_send_buffer(buffer.buffer(), r.length
|
||||
, boost::bind(&session_impl::free_disk_buffer
|
||||
, boost::ref(m_ses), _1));
|
||||
buffer.release();
|
||||
|
||||
m_payloads.push_back(range(send_buffer_size() - r.length, r.length));
|
||||
setup_send();
|
||||
|
@ -1641,8 +1722,9 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(in_handshake() || !m_rc4_encrypted || m_encrypted);
|
||||
if (m_rc4_encrypted && m_encrypted)
|
||||
{
|
||||
buffer::interval wr_buf = wr_recv_buffer();
|
||||
m_RC4_handler->decrypt((wr_buf.end - bytes_transferred), bytes_transferred);
|
||||
std::pair<buffer::interval, buffer::interval> wr_buf = wr_recv_buffers(bytes_transferred);
|
||||
m_RC4_handler->decrypt(wr_buf.first.begin, wr_buf.first.left());
|
||||
if (wr_buf.second.left()) m_RC4_handler->decrypt(wr_buf.second.begin, wr_buf.second.left());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1654,10 +1736,10 @@ namespace libtorrent
|
|||
// for outgoing
|
||||
if (m_state == read_pe_dhkey)
|
||||
{
|
||||
assert (!m_encrypted);
|
||||
assert (!m_rc4_encrypted);
|
||||
assert (packet_size() == dh_key_len);
|
||||
assert (recv_buffer == receive_buffer());
|
||||
TORRENT_ASSERT(!m_encrypted);
|
||||
TORRENT_ASSERT(!m_rc4_encrypted);
|
||||
TORRENT_ASSERT(packet_size() == dh_key_len);
|
||||
TORRENT_ASSERT(recv_buffer == receive_buffer());
|
||||
|
||||
if (!packet_finished()) return;
|
||||
|
||||
|
@ -1696,7 +1778,7 @@ namespace libtorrent
|
|||
|
||||
// vc,crypto_select,len(pad),pad, encrypt(handshake)
|
||||
// 8+4+2+0+handshake_len
|
||||
reset_recv_buffer(8+4+2+0+handshake_len);
|
||||
reset_recv_buffer(8+4+2+0+handshake_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1720,10 +1802,7 @@ namespace libtorrent
|
|||
if (recv_buffer.left() < 20)
|
||||
{
|
||||
if (packet_finished())
|
||||
{
|
||||
throw protocol_error ("sync hash not found");
|
||||
}
|
||||
// else
|
||||
disconnect("sync hash not found");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1748,7 +1827,10 @@ namespace libtorrent
|
|||
std::size_t bytes_processed = recv_buffer.left() - 20;
|
||||
m_sync_bytes_read += bytes_processed;
|
||||
if (m_sync_bytes_read >= 512)
|
||||
throw protocol_error("sync hash not found within 532 bytes");
|
||||
{
|
||||
disconnect("sync hash not found within 532 bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
cut_receive_buffer(bytes_processed, (std::min)(packet_size(), (512+20) - m_sync_bytes_read));
|
||||
|
||||
|
@ -1815,6 +1897,8 @@ namespace libtorrent
|
|||
if (!t)
|
||||
{
|
||||
attach_to_torrent(info_hash);
|
||||
if (is_disconnecting()) return;
|
||||
|
||||
t = associated_torrent().lock();
|
||||
TORRENT_ASSERT(t);
|
||||
}
|
||||
|
@ -1828,7 +1912,10 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
if (!m_RC4_handler.get())
|
||||
throw protocol_error("invalid streamkey identifier (info hash) in encrypted handshake");
|
||||
{
|
||||
disconnect("invalid streamkey identifier (info hash) in encrypted handshake");
|
||||
return;
|
||||
}
|
||||
|
||||
// verify constant
|
||||
buffer::interval wr_recv_buf = wr_recv_buffer();
|
||||
|
@ -1838,7 +1925,8 @@ namespace libtorrent
|
|||
const char sh_vc[] = {0,0,0,0, 0,0,0,0};
|
||||
if (!std::equal(sh_vc, sh_vc+8, recv_buffer.begin + 20))
|
||||
{
|
||||
throw protocol_error("unable to verify constant");
|
||||
disconnect("unable to verify constant");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
|
@ -1859,10 +1947,7 @@ namespace libtorrent
|
|||
if (recv_buffer.left() < 8)
|
||||
{
|
||||
if (packet_finished())
|
||||
{
|
||||
throw protocol_error ("sync verification constant not found");
|
||||
}
|
||||
// else
|
||||
disconnect("sync verification constant not found");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1886,7 +1971,10 @@ namespace libtorrent
|
|||
std::size_t bytes_processed = recv_buffer.left() - 8;
|
||||
m_sync_bytes_read += bytes_processed;
|
||||
if (m_sync_bytes_read >= 512)
|
||||
throw protocol_error("sync verification constant not found within 520 bytes");
|
||||
{
|
||||
disconnect("sync verification constant not found within 520 bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
cut_receive_buffer(bytes_processed, (std::min)(packet_size(), (512+8) - m_sync_bytes_read));
|
||||
|
||||
|
@ -1944,22 +2032,23 @@ namespace libtorrent
|
|||
// select a crypto method
|
||||
switch (m_ses.get_pe_settings().allowed_enc_level)
|
||||
{
|
||||
case (pe_settings::plaintext):
|
||||
{
|
||||
case pe_settings::plaintext:
|
||||
if (!(crypto_field & 0x01))
|
||||
throw protocol_error("plaintext not provided");
|
||||
{
|
||||
disconnect("plaintext not provided");
|
||||
return;
|
||||
}
|
||||
crypto_select = 0x01;
|
||||
}
|
||||
break;
|
||||
case (pe_settings::rc4):
|
||||
{
|
||||
break;
|
||||
case pe_settings::rc4:
|
||||
if (!(crypto_field & 0x02))
|
||||
throw protocol_error("rc4 not provided");
|
||||
{
|
||||
disconnect("rc4 not provided");
|
||||
return;
|
||||
}
|
||||
crypto_select = 0x02;
|
||||
}
|
||||
break;
|
||||
case (pe_settings::both):
|
||||
{
|
||||
break;
|
||||
case pe_settings::both:
|
||||
if (m_ses.get_pe_settings().prefer_rc4)
|
||||
{
|
||||
if (crypto_field & 0x02)
|
||||
|
@ -1975,8 +2064,11 @@ namespace libtorrent
|
|||
crypto_select = 0x02;
|
||||
}
|
||||
if (!crypto_select)
|
||||
throw protocol_error("rc4/plaintext not provided");
|
||||
}
|
||||
{
|
||||
disconnect("rc4/plaintext not provided");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} // switch
|
||||
|
||||
// write the pe4 step
|
||||
|
@ -1990,22 +2082,34 @@ namespace libtorrent
|
|||
if (crypto_field == 0x02)
|
||||
{
|
||||
if (allowed_enc_level == pe_settings::plaintext)
|
||||
throw protocol_error("rc4 selected by peer when not provided");
|
||||
{
|
||||
disconnect("rc4 selected by peer when not provided");
|
||||
return;
|
||||
}
|
||||
m_rc4_encrypted = true;
|
||||
}
|
||||
else if (crypto_field == 0x01)
|
||||
{
|
||||
if (allowed_enc_level == pe_settings::rc4)
|
||||
throw protocol_error("plaintext selected by peer when not provided");
|
||||
{
|
||||
disconnect("plaintext selected by peer when not provided");
|
||||
return;
|
||||
}
|
||||
m_rc4_encrypted = false;
|
||||
}
|
||||
else
|
||||
throw protocol_error("unsupported crypto method selected by peer");
|
||||
{
|
||||
disconnect("unsupported crypto method selected by peer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int len_pad = detail::read_int16(recv_buffer.begin);
|
||||
if (len_pad < 0 || len_pad > 512)
|
||||
throw protocol_error("invalid pad length");
|
||||
{
|
||||
disconnect("invalid pad length");
|
||||
return;
|
||||
}
|
||||
|
||||
m_state = read_pe_pad;
|
||||
if (!is_local())
|
||||
|
@ -2039,7 +2143,11 @@ namespace libtorrent
|
|||
recv_buffer.begin += pad_size;
|
||||
int len_ia = detail::read_int16(recv_buffer.begin);
|
||||
|
||||
if (len_ia < 0) throw protocol_error("invalid len_ia in handshake");
|
||||
if (len_ia < 0)
|
||||
{
|
||||
disconnect("invalid len_ia in handshake");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << " len(IA) : " << len_ia << "\n";
|
||||
|
@ -2136,7 +2244,7 @@ namespace libtorrent
|
|||
|
||||
if (m_state == read_protocol_identifier)
|
||||
{
|
||||
assert (packet_size() == 20);
|
||||
TORRENT_ASSERT(packet_size() == 20);
|
||||
|
||||
if (!packet_finished()) return;
|
||||
recv_buffer = receive_buffer();
|
||||
|
@ -2149,7 +2257,10 @@ namespace libtorrent
|
|||
{
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
if (!is_local() && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled)
|
||||
throw protocol_error("encrypted incoming connections disabled");
|
||||
{
|
||||
disconnect("encrypted incoming connections disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't attempt to perform an encrypted handshake
|
||||
// within an encrypted connection
|
||||
|
@ -2164,18 +2275,22 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
|
||||
assert ((!is_local() && m_encrypted) || is_local());
|
||||
TORRENT_ASSERT((!is_local() && m_encrypted) || is_local());
|
||||
#endif // #ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
throw protocol_error("incorrect protocol identifier");
|
||||
disconnect("incorrect protocol identifier");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
assert (m_state != read_pe_dhkey);
|
||||
TORRENT_ASSERT(m_state != read_pe_dhkey);
|
||||
|
||||
if (!is_local() &&
|
||||
(m_ses.get_pe_settings().in_enc_policy == pe_settings::forced) &&
|
||||
!m_encrypted)
|
||||
throw protocol_error("non encrypted incoming connections disabled");
|
||||
{
|
||||
disconnect("non encrypted incoming connections disabled");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
|
@ -2234,6 +2349,7 @@ namespace libtorrent
|
|||
, (char*)info_hash.begin());
|
||||
|
||||
attach_to_torrent(info_hash);
|
||||
if (is_disconnecting()) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2244,7 +2360,8 @@ namespace libtorrent
|
|||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << " received invalid info_hash\n";
|
||||
#endif
|
||||
throw protocol_error("invalid info-hash in handshake");
|
||||
disconnect("invalid info-hash in handshake");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
|
@ -2261,6 +2378,8 @@ namespace libtorrent
|
|||
// if (t->valid_metadata())
|
||||
// write_bitfield(t->pieces());
|
||||
|
||||
if (is_disconnecting()) return;
|
||||
|
||||
TORRENT_ASSERT(t->get_policy().has_connection(this));
|
||||
|
||||
m_state = read_peer_id;
|
||||
|
@ -2318,18 +2437,20 @@ namespace libtorrent
|
|||
// if not, we should close the outgoing one.
|
||||
if (pid < m_ses.get_peer_id() && is_local())
|
||||
{
|
||||
i->second.connection->disconnect();
|
||||
i->second.connection->disconnect("duplicate peer-id, connection closed");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw protocol_error("duplicate peer-id, connection closed");
|
||||
disconnect("duplicate peer-id, connection closed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == m_ses.get_peer_id())
|
||||
{
|
||||
throw protocol_error("closing connection to ourself");
|
||||
disconnect("closing connection to ourself");
|
||||
return;
|
||||
}
|
||||
|
||||
m_client_version = identify_client(pid);
|
||||
|
@ -2343,7 +2464,10 @@ namespace libtorrent
|
|||
// disconnect if the peer has the same peer-id as ourself
|
||||
// since it most likely is ourself then
|
||||
if (pid == m_ses.get_peer_id())
|
||||
throw std::runtime_error("closing connection to ourself");
|
||||
{
|
||||
disconnect("closing connection to ourself");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
for (extension_list_t::iterator i = m_extensions.begin()
|
||||
|
@ -2382,7 +2506,7 @@ namespace libtorrent
|
|||
#endif
|
||||
|
||||
m_state = read_packet_size;
|
||||
reset_recv_buffer(4);
|
||||
reset_recv_buffer(5);
|
||||
if (t->valid_metadata())
|
||||
{
|
||||
write_bitfield(t->pieces());
|
||||
|
@ -2400,11 +2524,12 @@ namespace libtorrent
|
|||
if (m_state == read_packet_size)
|
||||
{
|
||||
// Make sure this is not fallen though into
|
||||
assert (recv_buffer == receive_buffer());
|
||||
TORRENT_ASSERT(recv_buffer == receive_buffer());
|
||||
|
||||
if (!t) return;
|
||||
m_statistics.received_bytes(0, bytes_transferred);
|
||||
if (!packet_finished()) return;
|
||||
|
||||
if (recv_buffer.left() < 4) return;
|
||||
|
||||
const char* ptr = recv_buffer.begin;
|
||||
int packet_size = detail::read_int32(ptr);
|
||||
|
@ -2413,9 +2538,10 @@ namespace libtorrent
|
|||
if (packet_size > 1024*1024 || packet_size < 0)
|
||||
{
|
||||
// packet too large
|
||||
throw std::runtime_error("packet > 1 MB ("
|
||||
+ boost::lexical_cast<std::string>(
|
||||
(unsigned int)packet_size) + " bytes)");
|
||||
std::stringstream msg;
|
||||
msg << "packet > 1 MB (" << (unsigned int)packet_size << " bytes)";
|
||||
disconnect(msg.str().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet_size == 0)
|
||||
|
@ -2423,15 +2549,19 @@ namespace libtorrent
|
|||
incoming_keepalive();
|
||||
// keepalive message
|
||||
m_state = read_packet_size;
|
||||
reset_recv_buffer(4);
|
||||
cut_receive_buffer(4, 4);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (recv_buffer.left() < 5) return;
|
||||
|
||||
m_state = read_packet;
|
||||
reset_recv_buffer(packet_size);
|
||||
cut_receive_buffer(4, packet_size);
|
||||
bytes_transferred = 1;
|
||||
recv_buffer = receive_buffer();
|
||||
TORRENT_ASSERT(recv_buffer.left() == 1);
|
||||
}
|
||||
TORRENT_ASSERT(!packet_finished());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state == read_packet)
|
||||
|
@ -2441,7 +2571,7 @@ namespace libtorrent
|
|||
if (dispatch_message(bytes_transferred))
|
||||
{
|
||||
m_state = read_packet_size;
|
||||
reset_recv_buffer(4);
|
||||
reset_recv_buffer(5);
|
||||
}
|
||||
TORRENT_ASSERT(!packet_finished());
|
||||
return;
|
||||
|
|
|
@ -45,25 +45,49 @@ namespace libtorrent
|
|||
#ifndef NDEBUG
|
||||
, m_in_timeout_function(false)
|
||||
#endif
|
||||
{}
|
||||
{
|
||||
#ifdef TORRENT_CONNECTION_LOGGING
|
||||
m_log.open("connection_queue.log");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool connection_queue::free_slots() const
|
||||
{ return m_num_connecting < m_half_open_limit || m_half_open_limit <= 0; }
|
||||
int connection_queue::free_slots() const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return m_half_open_limit == 0 ? (std::numeric_limits<int>::max)()
|
||||
: m_half_open_limit - m_queue.size();
|
||||
}
|
||||
|
||||
void connection_queue::enqueue(boost::function<void(int)> const& on_connect
|
||||
, boost::function<void()> const& on_timeout
|
||||
, time_duration timeout)
|
||||
, time_duration timeout, int priority)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
INVARIANT_CHECK;
|
||||
|
||||
m_queue.push_back(entry());
|
||||
entry& e = m_queue.back();
|
||||
e.on_connect = on_connect;
|
||||
e.on_timeout = on_timeout;
|
||||
e.ticket = m_next_ticket;
|
||||
e.timeout = timeout;
|
||||
TORRENT_ASSERT(priority >= 0);
|
||||
TORRENT_ASSERT(priority < 2);
|
||||
|
||||
entry* e = 0;
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case 0:
|
||||
m_queue.push_back(entry());
|
||||
e = &m_queue.back();
|
||||
break;
|
||||
case 1:
|
||||
m_queue.push_front(entry());
|
||||
e = &m_queue.front();
|
||||
break;
|
||||
}
|
||||
|
||||
e->priority = priority;
|
||||
e->on_connect = on_connect;
|
||||
e->on_timeout = on_timeout;
|
||||
e->ticket = m_next_ticket;
|
||||
e->timeout = timeout;
|
||||
++m_next_ticket;
|
||||
try_connect();
|
||||
}
|
||||
|
@ -88,11 +112,15 @@ namespace libtorrent
|
|||
|
||||
void connection_queue::close()
|
||||
{
|
||||
m_timer.cancel();
|
||||
asio::error_code ec;
|
||||
m_timer.cancel(ec);
|
||||
}
|
||||
|
||||
void connection_queue::limit(int limit)
|
||||
{ m_half_open_limit = limit; }
|
||||
{
|
||||
TORRENT_ASSERT(limit >= 0);
|
||||
m_half_open_limit = limit;
|
||||
}
|
||||
|
||||
int connection_queue::limit() const
|
||||
{ return m_half_open_limit; }
|
||||
|
@ -116,12 +144,17 @@ namespace libtorrent
|
|||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (!free_slots())
|
||||
return;
|
||||
#ifdef TORRENT_CONNECTION_LOGGING
|
||||
m_log << log_time() << " " << free_slots() << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_num_connecting >= m_half_open_limit
|
||||
&& m_half_open_limit > 0) return;
|
||||
|
||||
if (m_queue.empty())
|
||||
{
|
||||
m_timer.cancel();
|
||||
asio::error_code ec;
|
||||
m_timer.cancel(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -133,7 +166,8 @@ namespace libtorrent
|
|||
ptime expire = time_now() + i->timeout;
|
||||
if (m_num_connecting == 0)
|
||||
{
|
||||
m_timer.expires_at(expire);
|
||||
asio::error_code ec;
|
||||
m_timer.expires_at(expire, ec);
|
||||
m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1));
|
||||
}
|
||||
i->connecting = true;
|
||||
|
@ -144,9 +178,20 @@ namespace libtorrent
|
|||
|
||||
entry& ent = *i;
|
||||
++i;
|
||||
try { ent.on_connect(ent.ticket); } catch (std::exception&) {}
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
ent.on_connect(ent.ticket);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch (std::exception&) {}
|
||||
#endif
|
||||
|
||||
if (!free_slots()) break;
|
||||
#ifdef TORRENT_CONNECTION_LOGGING
|
||||
m_log << log_time() << " " << free_slots() << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_num_connecting >= m_half_open_limit
|
||||
&& m_half_open_limit > 0) break;
|
||||
i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false);
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +251,8 @@ namespace libtorrent
|
|||
|
||||
if (next_expire < max_time())
|
||||
{
|
||||
m_timer.expires_at(next_expire);
|
||||
asio::error_code ec;
|
||||
m_timer.expires_at(next_expire, ec);
|
||||
m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1));
|
||||
}
|
||||
try_connect();
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2008, 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 "libtorrent/disk_buffer_holder.hpp"
|
||||
#include "libtorrent/aux_/session_impl.hpp"
|
||||
#include "libtorrent/disk_io_thread.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
disk_buffer_holder::disk_buffer_holder(aux::session_impl& ses, char* buf)
|
||||
: m_iothread(ses.m_disk_thread), m_buf(buf)
|
||||
{
|
||||
TORRENT_ASSERT(buf == 0 || m_iothread.is_disk_buffer(buf));
|
||||
}
|
||||
|
||||
disk_buffer_holder::disk_buffer_holder(disk_io_thread& iothread, char* buf)
|
||||
: m_iothread(iothread), m_buf(buf)
|
||||
{
|
||||
TORRENT_ASSERT(buf == 0 || m_iothread.is_disk_buffer(buf));
|
||||
}
|
||||
|
||||
char* disk_buffer_holder::release()
|
||||
{
|
||||
char* ret = m_buf;
|
||||
m_buf = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
disk_buffer_holder::~disk_buffer_holder()
|
||||
{
|
||||
if (m_buf) m_iothread.free_buffer(m_buf);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -142,9 +142,38 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
entry::entry()
|
||||
: m_type(undefined_t)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
entry::entry(data_type t)
|
||||
: m_type(undefined_t)
|
||||
{
|
||||
construct(t);
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
entry::entry(const entry& e)
|
||||
: m_type(undefined_t)
|
||||
{
|
||||
copy(e);
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = e.m_type_queried;
|
||||
#endif
|
||||
}
|
||||
|
||||
entry::entry(dictionary_type const& v)
|
||||
: m_type(undefined_t)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
new(data) dictionary_type(v);
|
||||
m_type = dictionary_t;
|
||||
}
|
||||
|
@ -152,6 +181,9 @@ namespace libtorrent
|
|||
entry::entry(string_type const& v)
|
||||
: m_type(undefined_t)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
new(data) string_type(v);
|
||||
m_type = string_t;
|
||||
}
|
||||
|
@ -159,6 +191,9 @@ namespace libtorrent
|
|||
entry::entry(list_type const& v)
|
||||
: m_type(undefined_t)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
new(data) list_type(v);
|
||||
m_type = list_t;
|
||||
}
|
||||
|
@ -166,6 +201,9 @@ namespace libtorrent
|
|||
entry::entry(integer_type const& v)
|
||||
: m_type(undefined_t)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
new(data) integer_type(v);
|
||||
m_type = int_t;
|
||||
}
|
||||
|
@ -175,6 +213,9 @@ namespace libtorrent
|
|||
destruct();
|
||||
new(data) dictionary_type(v);
|
||||
m_type = dictionary_t;
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void entry::operator=(string_type const& v)
|
||||
|
@ -182,6 +223,9 @@ namespace libtorrent
|
|||
destruct();
|
||||
new(data) string_type(v);
|
||||
m_type = string_t;
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void entry::operator=(list_type const& v)
|
||||
|
@ -189,6 +233,9 @@ namespace libtorrent
|
|||
destruct();
|
||||
new(data) list_type(v);
|
||||
m_type = list_t;
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void entry::operator=(integer_type const& v)
|
||||
|
@ -196,6 +243,9 @@ namespace libtorrent
|
|||
destruct();
|
||||
new(data) integer_type(v);
|
||||
m_type = int_t;
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool entry::operator==(entry const& e) const
|
||||
|
@ -235,16 +285,17 @@ namespace libtorrent
|
|||
new (data) dictionary_type;
|
||||
break;
|
||||
default:
|
||||
TORRENT_ASSERT(m_type == undefined_t);
|
||||
m_type = undefined_t;
|
||||
return;
|
||||
TORRENT_ASSERT(t == undefined_t);
|
||||
}
|
||||
m_type = t;
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void entry::copy(entry const& e)
|
||||
{
|
||||
switch(e.m_type)
|
||||
switch (e.type())
|
||||
{
|
||||
case int_t:
|
||||
new(data) integer_type(e.integer());
|
||||
|
@ -259,10 +310,12 @@ namespace libtorrent
|
|||
new (data) dictionary_type(e.dict());
|
||||
break;
|
||||
default:
|
||||
m_type = undefined_t;
|
||||
return;
|
||||
TORRENT_ASSERT(e.type() == undefined_t);
|
||||
}
|
||||
m_type = e.m_type;
|
||||
m_type = e.type();
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void entry::destruct()
|
||||
|
@ -286,6 +339,9 @@ namespace libtorrent
|
|||
break;
|
||||
}
|
||||
m_type = undefined_t;
|
||||
#ifndef NDEBUG
|
||||
m_type_queried = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void entry::swap(entry& e)
|
||||
|
|
|
@ -31,12 +31,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
*/
|
||||
|
||||
#include "libtorrent/config.hpp"
|
||||
|
||||
|
||||
#if defined TORRENT_BSD || defined TORRENT_LINUX
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <net/route.h>
|
||||
#elif defined TORRENT_WINDOWS
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
@ -46,8 +47,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#endif
|
||||
|
||||
#include "libtorrent/enum_net.hpp"
|
||||
// for is_loopback and is_any
|
||||
#include "libtorrent/broadcast_socket.hpp"
|
||||
#include <asio/ip/host_name.hpp>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -71,6 +72,17 @@ namespace libtorrent
|
|||
}
|
||||
return address();
|
||||
}
|
||||
|
||||
#ifdef TORRENT_BSD
|
||||
bool verify_sockaddr(sockaddr_in* sin)
|
||||
{
|
||||
return (sin->sin_len == sizeof(sockaddr_in)
|
||||
&& sin->sin_family == AF_INET)
|
||||
|| (sin->sin_len == sizeof(sockaddr_in6)
|
||||
&& sin->sin_family == AF_INET6);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool in_subnet(address const& addr, ip_interface const& iface)
|
||||
|
@ -101,7 +113,7 @@ namespace libtorrent
|
|||
std::vector<ip_interface> enum_net_interfaces(asio::io_service& ios, asio::error_code& ec)
|
||||
{
|
||||
std::vector<ip_interface> ret;
|
||||
|
||||
// covers linux, MacOS X and BSD distributions
|
||||
#if defined TORRENT_LINUX || defined TORRENT_BSD
|
||||
int s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s < 0)
|
||||
|
@ -201,7 +213,7 @@ namespace libtorrent
|
|||
#warning THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK
|
||||
// make a best guess of the interface we're using and its IP
|
||||
udp::resolver r(ios);
|
||||
udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(ec), "0"));
|
||||
udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(ec), "0"), ec);
|
||||
if (ec) return ret;
|
||||
ip_interface iface;
|
||||
for (;i != udp::resolver_iterator(); ++i)
|
||||
|
@ -215,15 +227,116 @@ namespace libtorrent
|
|||
return ret;
|
||||
}
|
||||
|
||||
address router_for_interface(address const interface, asio::error_code& ec)
|
||||
address get_default_gateway(asio::io_service& ios, address const& interface, asio::error_code& ec)
|
||||
{
|
||||
#ifdef TORRENT_WINDOWS
|
||||
|
||||
#if defined TORRENT_BSD
|
||||
|
||||
struct rt_msg
|
||||
{
|
||||
rt_msghdr m_rtm;
|
||||
char buf[512];
|
||||
};
|
||||
|
||||
rt_msg m;
|
||||
int len = sizeof(rt_msg);
|
||||
bzero(&m, len);
|
||||
m.m_rtm.rtm_type = RTM_GET;
|
||||
m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
|
||||
m.m_rtm.rtm_version = RTM_VERSION;
|
||||
m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY;
|
||||
m.m_rtm.rtm_seq = 0;
|
||||
m.m_rtm.rtm_msglen = len;
|
||||
|
||||
int s = socket(PF_ROUTE, SOCK_RAW, AF_INET);
|
||||
if (s == -1)
|
||||
{
|
||||
ec = asio::error_code(errno, asio::error::system_category);
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
int n = write(s, &m, len);
|
||||
if (n == -1)
|
||||
{
|
||||
ec = asio::error_code(errno, asio::error::system_category);
|
||||
close(s);
|
||||
return address_v4::any();
|
||||
}
|
||||
else if (n != len)
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
close(s);
|
||||
return address_v4::any();
|
||||
}
|
||||
bzero(&m, len);
|
||||
|
||||
n = read(s, &m, len);
|
||||
if (n == -1)
|
||||
{
|
||||
ec = asio::error_code(errno, asio::error::system_category);
|
||||
close(s);
|
||||
return address_v4::any();
|
||||
}
|
||||
close(s);
|
||||
|
||||
TORRENT_ASSERT(m.m_rtm.rtm_seq == 0);
|
||||
TORRENT_ASSERT(m.m_rtm.rtm_pid == getpid());
|
||||
if (m.m_rtm.rtm_errno)
|
||||
{
|
||||
ec = asio::error_code(m.m_rtm.rtm_errno, asio::error::system_category);
|
||||
return address_v4::any();
|
||||
}
|
||||
if (m.m_rtm.rtm_flags & RTF_UP == 0
|
||||
|| m.m_rtm.rtm_flags & RTF_GATEWAY == 0)
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
if (m.m_rtm.rtm_addrs & RTA_DST == 0
|
||||
|| m.m_rtm.rtm_addrs & RTA_GATEWAY == 0)
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
if (m.m_rtm.rtm_msglen > len)
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
int min_len = sizeof(rt_msghdr) + 2 * sizeof(struct sockaddr_in);
|
||||
if (m.m_rtm.rtm_msglen < min_len)
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
// default route
|
||||
char* p = m.buf;
|
||||
sockaddr_in* sin = (sockaddr_in*)p;
|
||||
if (!verify_sockaddr(sin))
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
// default gateway
|
||||
p += sin->sin_len;
|
||||
sin = (sockaddr_in*)p;
|
||||
if (!verify_sockaddr(sin))
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
return sockaddr_to_address((sockaddr*)sin);
|
||||
|
||||
#elif defined TORRENT_WINDOWS
|
||||
|
||||
// Load Iphlpapi library
|
||||
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
|
||||
if (!iphlp)
|
||||
{
|
||||
ec = asio::error::fault;
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
|
@ -233,7 +346,7 @@ namespace libtorrent
|
|||
if (!GetAdaptersInfo)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::fault;
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
|
@ -242,7 +355,7 @@ namespace libtorrent
|
|||
if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::fault;
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
|
@ -250,14 +363,13 @@ namespace libtorrent
|
|||
if (!adapter_info)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::fault;
|
||||
ec = asio::error::no_memory;
|
||||
return address_v4::any();
|
||||
}
|
||||
|
||||
address ret;
|
||||
if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR)
|
||||
{
|
||||
|
||||
for (PIP_ADAPTER_INFO adapter = adapter_info;
|
||||
adapter != 0; adapter = adapter->Next)
|
||||
{
|
||||
|
@ -282,11 +394,12 @@ namespace libtorrent
|
|||
|
||||
return ret;
|
||||
|
||||
//#elif defined TORRENT_LINUX
|
||||
// No linux implementation yet
|
||||
#else
|
||||
// TODO: temporary implementation
|
||||
if (!interface.is_v4())
|
||||
{
|
||||
ec = asio::error::fault;
|
||||
ec = asio::error::operation_not_supported;
|
||||
return address_v4::any();
|
||||
}
|
||||
return address_v4((interface.to_v4().to_ulong() & 0xffffff00) | 1);
|
||||
|
|
|
@ -38,6 +38,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <iomanip>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
||||
|
@ -60,23 +63,41 @@ namespace libtorrent
|
|||
{
|
||||
++i;
|
||||
if (i == s.end())
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return ret;
|
||||
#else
|
||||
throw std::runtime_error("invalid escaped string");
|
||||
#endif
|
||||
|
||||
int high;
|
||||
if(*i >= '0' && *i <= '9') high = *i - '0';
|
||||
else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A';
|
||||
else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a';
|
||||
else throw std::runtime_error("invalid escaped string");
|
||||
else
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return ret;
|
||||
#else
|
||||
throw std::runtime_error("invalid escaped string");
|
||||
#endif
|
||||
|
||||
++i;
|
||||
if (i == s.end())
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return ret;
|
||||
#else
|
||||
throw std::runtime_error("invalid escaped string");
|
||||
#endif
|
||||
|
||||
int low;
|
||||
if(*i >= '0' && *i <= '9') low = *i - '0';
|
||||
else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A';
|
||||
else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a';
|
||||
else throw std::runtime_error("invalid escaped string");
|
||||
else
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return ret;
|
||||
#else
|
||||
throw std::runtime_error("invalid escaped string");
|
||||
#endif
|
||||
|
||||
ret += char(high * 16 + low);
|
||||
}
|
||||
|
@ -84,7 +105,6 @@ namespace libtorrent
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::string escape_string(const char* str, int len)
|
||||
{
|
||||
TORRENT_ASSERT(str != 0);
|
||||
|
@ -148,4 +168,189 @@ namespace libtorrent
|
|||
}
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
std::string base64encode(const std::string& s)
|
||||
{
|
||||
static const char base64_table[] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
unsigned char inbuf[3];
|
||||
unsigned char outbuf[4];
|
||||
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = s.begin(); i != s.end();)
|
||||
{
|
||||
// available input is 1,2 or 3 bytes
|
||||
// since we read 3 bytes at a time at most
|
||||
int available_input = (std::min)(3, (int)std::distance(i, s.end()));
|
||||
|
||||
// clear input buffer
|
||||
std::fill(inbuf, inbuf+3, 0);
|
||||
|
||||
// read a chunk of input into inbuf
|
||||
std::copy(i, i + available_input, inbuf);
|
||||
i += available_input;
|
||||
|
||||
// encode inbuf to outbuf
|
||||
outbuf[0] = (inbuf[0] & 0xfc) >> 2;
|
||||
outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
|
||||
outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
|
||||
outbuf[3] = inbuf[2] & 0x3f;
|
||||
|
||||
// write output
|
||||
for (int j = 0; j < available_input+1; ++j)
|
||||
{
|
||||
ret += base64_table[outbuf[j]];
|
||||
}
|
||||
|
||||
// write pad
|
||||
for (int j = 0; j < 3 - available_input; ++j)
|
||||
{
|
||||
ret += '=';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string base32encode(std::string const& s)
|
||||
{
|
||||
static const char base32_table[] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7'
|
||||
};
|
||||
|
||||
int input_output_mapping[] = {0, 2, 4, 5, 7, 8};
|
||||
|
||||
unsigned char inbuf[5];
|
||||
unsigned char outbuf[8];
|
||||
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = s.begin(); i != s.end();)
|
||||
{
|
||||
int available_input = (std::min)(5, (int)std::distance(i, s.end()));
|
||||
|
||||
// clear input buffer
|
||||
std::fill(inbuf, inbuf+5, 0);
|
||||
|
||||
// read a chunk of input into inbuf
|
||||
std::copy(i, i + available_input, inbuf);
|
||||
i += available_input;
|
||||
|
||||
// encode inbuf to outbuf
|
||||
outbuf[0] = (inbuf[0] & 0xf8) >> 3;
|
||||
outbuf[1] = ((inbuf[0] & 0x07) << 2) | ((inbuf[1] & 0xc0) >> 6);
|
||||
outbuf[2] = ((inbuf[1] & 0x3e) >> 1);
|
||||
outbuf[3] = ((inbuf[1] & 0x01) << 4) | ((inbuf[2] & 0xf0) >> 4);
|
||||
outbuf[4] = ((inbuf[2] & 0x0f) << 1) | ((inbuf[3] & 0x80) >> 7);
|
||||
outbuf[5] = ((inbuf[3] & 0x7c) >> 2);
|
||||
outbuf[6] = ((inbuf[3] & 0x03) << 3) | ((inbuf[4] & 0xe0) >> 5);
|
||||
outbuf[7] = inbuf[4] & 0x1f;
|
||||
|
||||
// write output
|
||||
int num_out = input_output_mapping[available_input];
|
||||
for (int j = 0; j < num_out; ++j)
|
||||
{
|
||||
ret += base32_table[outbuf[j]];
|
||||
}
|
||||
|
||||
// write pad
|
||||
for (int j = 0; j < 8 - num_out; ++j)
|
||||
{
|
||||
ret += '=';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string base32decode(std::string const& s)
|
||||
{
|
||||
unsigned char inbuf[8];
|
||||
unsigned char outbuf[5];
|
||||
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = s.begin(); i != s.end();)
|
||||
{
|
||||
int available_input = (std::min)(8, (int)std::distance(i, s.end()));
|
||||
|
||||
int pad_start = 0;
|
||||
if (available_input < 8) pad_start = available_input;
|
||||
|
||||
// clear input buffer
|
||||
std::fill(inbuf, inbuf+8, 0);
|
||||
for (int j = 0; j < available_input; ++j)
|
||||
{
|
||||
char in = std::toupper(*i++);
|
||||
if (in >= 'A' && in <= 'Z')
|
||||
inbuf[j] = in - 'A';
|
||||
else if (in >= '2' && in <= '7')
|
||||
inbuf[j] = in - '2' + ('Z' - 'A') + 1;
|
||||
else if (in == '=')
|
||||
{
|
||||
inbuf[j] = 0;
|
||||
if (pad_start == 0) pad_start = j;
|
||||
}
|
||||
else if (in == '1')
|
||||
inbuf[j] = 'I' - 'A';
|
||||
else
|
||||
return std::string();
|
||||
TORRENT_ASSERT(inbuf[j] == (inbuf[j] & 0x1f));
|
||||
}
|
||||
|
||||
// decode inbuf to outbuf
|
||||
outbuf[0] = inbuf[0] << 3;
|
||||
outbuf[0] |= inbuf[1] >> 2;
|
||||
outbuf[1] = (inbuf[1] & 0x3) << 6;
|
||||
outbuf[1] |= inbuf[2] << 1;
|
||||
outbuf[1] |= (inbuf[3] & 0x10) >> 4;
|
||||
outbuf[2] = (inbuf[3] & 0x0f) << 4;
|
||||
outbuf[2] |= (inbuf[4] & 0x1e) >> 1;
|
||||
outbuf[3] = (inbuf[4] & 0x01) << 7;
|
||||
outbuf[3] |= (inbuf[5] & 0x1f) << 2;
|
||||
outbuf[3] |= (inbuf[6] & 0x18) >> 3;
|
||||
outbuf[4] = (inbuf[6] & 0x07) << 5;
|
||||
outbuf[4] |= inbuf[7];
|
||||
|
||||
int input_output_mapping[] = {5, 1, 1, 2, 2, 3, 4, 4, 5};
|
||||
int num_out = input_output_mapping[pad_start];
|
||||
|
||||
// write output
|
||||
std::copy(outbuf, outbuf + num_out, std::back_inserter(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
boost::optional<std::string> url_has_argument(
|
||||
std::string const& url, std::string argument)
|
||||
{
|
||||
size_t i = url.find('?');
|
||||
if (i == std::string::npos) return boost::optional<std::string>();
|
||||
++i;
|
||||
|
||||
argument += '=';
|
||||
|
||||
if (url.compare(i, argument.size(), argument) == 0)
|
||||
{
|
||||
size_t pos = i + argument.size();
|
||||
return url.substr(pos, url.find('&', pos) - pos);
|
||||
}
|
||||
argument.insert(0, "&");
|
||||
i = url.find(argument, i);
|
||||
if (i == std::string::npos) return boost::optional<std::string>();
|
||||
size_t pos = i + argument.size();
|
||||
return url.substr(pos, url.find('&', pos) - pos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "libtorrent/pch.hpp"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#ifdef _WIN32
|
||||
// windows part
|
||||
#include "libtorrent/utf8.hpp"
|
||||
|
@ -157,7 +158,7 @@ namespace libtorrent
|
|||
close();
|
||||
}
|
||||
|
||||
void open(fs::path const& path, int mode)
|
||||
bool open(fs::path const& path, int mode)
|
||||
{
|
||||
TORRENT_ASSERT(path.is_complete());
|
||||
close();
|
||||
|
@ -186,9 +187,12 @@ namespace libtorrent
|
|||
std::stringstream msg;
|
||||
msg << "open failed: '" << path.native_file_string() << "'. "
|
||||
<< std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
return false;
|
||||
}
|
||||
m_open_mode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
void close()
|
||||
|
@ -218,7 +222,8 @@ namespace libtorrent
|
|||
{
|
||||
std::stringstream msg;
|
||||
msg << "read failed: " << std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -242,12 +247,13 @@ namespace libtorrent
|
|||
{
|
||||
std::stringstream msg;
|
||||
msg << "write failed: " << std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_size(size_type s)
|
||||
bool set_size(size_type s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#error file.cpp is for posix systems only. use file_win.cpp on windows
|
||||
|
@ -256,8 +262,11 @@ namespace libtorrent
|
|||
{
|
||||
std::stringstream msg;
|
||||
msg << "ftruncate failed: '" << std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -283,7 +292,9 @@ namespace libtorrent
|
|||
<< "' fd: " << m_fd
|
||||
<< " offset: " << offset
|
||||
<< " seekdir: " << seekdir;
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -300,8 +311,15 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string const& error() const
|
||||
{
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
return *m_error;
|
||||
}
|
||||
|
||||
int m_fd;
|
||||
int m_open_mode;
|
||||
mutable boost::scoped_ptr<std::string> m_error;
|
||||
};
|
||||
|
||||
// pimpl forwardings
|
||||
|
@ -314,9 +332,9 @@ namespace libtorrent
|
|||
|
||||
file::~file() {}
|
||||
|
||||
void file::open(fs::path const& p, file::open_mode m)
|
||||
bool file::open(fs::path const& p, file::open_mode m)
|
||||
{
|
||||
m_impl->open(p, m.m_mask);
|
||||
return m_impl->open(p, m.m_mask);
|
||||
}
|
||||
|
||||
void file::close()
|
||||
|
@ -334,9 +352,9 @@ namespace libtorrent
|
|||
return m_impl->read(buf, num_bytes);
|
||||
}
|
||||
|
||||
void file::set_size(size_type s)
|
||||
bool file::set_size(size_type s)
|
||||
{
|
||||
m_impl->set_size(s);
|
||||
return m_impl->set_size(s);
|
||||
}
|
||||
|
||||
size_type file::seek(size_type pos, file::seek_mode m)
|
||||
|
@ -349,4 +367,9 @@ namespace libtorrent
|
|||
return m_impl->tell();
|
||||
}
|
||||
|
||||
std::string const& file::error() const
|
||||
{
|
||||
return m_impl->error();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ namespace libtorrent
|
|||
using boost::multi_index::nth_index;
|
||||
using boost::multi_index::get;
|
||||
|
||||
boost::shared_ptr<file> file_pool::open_file(void* st, fs::path const& p, file::open_mode m)
|
||||
boost::shared_ptr<file> file_pool::open_file(void* st, fs::path const& p
|
||||
, file::open_mode m, std::string& error)
|
||||
{
|
||||
TORRENT_ASSERT(st != 0);
|
||||
TORRENT_ASSERT(p.is_complete());
|
||||
|
@ -59,8 +60,9 @@ namespace libtorrent
|
|||
{
|
||||
// this means that another instance of the storage
|
||||
// is using the exact same file.
|
||||
throw file_error("torrent uses the same file as another torrent "
|
||||
"(" + p.string() + ")");
|
||||
error = "torrent uses the same file as another torrent "
|
||||
"(" + p.string() + ")";
|
||||
return boost::shared_ptr<file>();
|
||||
}
|
||||
|
||||
e.key = st;
|
||||
|
@ -70,8 +72,13 @@ namespace libtorrent
|
|||
// the new read/write privilages
|
||||
i->file_ptr.reset();
|
||||
TORRENT_ASSERT(e.file_ptr.unique());
|
||||
e.file_ptr.reset();
|
||||
e.file_ptr.reset(new file(p, m));
|
||||
e.file_ptr->close();
|
||||
if (!e.file_ptr->open(p, m))
|
||||
{
|
||||
error = e.file_ptr->error();
|
||||
m_files.erase(i);
|
||||
return boost::shared_ptr<file>();
|
||||
}
|
||||
e.mode = m;
|
||||
}
|
||||
pt.replace(i, e);
|
||||
|
@ -89,7 +96,18 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(lt.size() == 1 || (i->last_use <= boost::next(i)->last_use));
|
||||
lt.erase(i);
|
||||
}
|
||||
lru_file_entry e(boost::shared_ptr<file>(new file(p, m)));
|
||||
lru_file_entry e;
|
||||
e.file_ptr.reset(new file);
|
||||
if (!e.file_ptr)
|
||||
{
|
||||
error = "no memory";
|
||||
return e.file_ptr;
|
||||
}
|
||||
if (!e.file_ptr->open(p, m))
|
||||
{
|
||||
error = e.file_ptr->error();
|
||||
return boost::shared_ptr<file>();
|
||||
}
|
||||
e.mode = m;
|
||||
e.key = st;
|
||||
e.file_path = p;
|
||||
|
|
|
@ -41,6 +41,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <sstream>
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -81,36 +82,7 @@ namespace
|
|||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
void throw_exception(const char* thrower)
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
|
||||
#ifdef UNICODE
|
||||
wchar_t *wbuffer = 0;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM
|
||||
|FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
, 0, err, 0, (LPWSTR)&wbuffer, 0, 0);
|
||||
auto_localfree auto_free(wbuffer);
|
||||
std::string tmp_utf8;
|
||||
libtorrent::wchar_utf8(wbuffer, tmp_utf8);
|
||||
char const* buffer = tmp_utf8.c_str();
|
||||
#else
|
||||
char* buffer = 0;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM
|
||||
|FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
, 0, err, 0, (LPSTR)&buffer, 0, 0);
|
||||
auto_localfree auto_free(buffer);
|
||||
#endif
|
||||
|
||||
std::stringstream s;
|
||||
s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");
|
||||
|
||||
throw libtorrent::file_error(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -130,12 +102,42 @@ namespace libtorrent
|
|||
seek_end = FILE_END
|
||||
};
|
||||
|
||||
void set_error(const char* thrower)
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
|
||||
#ifdef UNICODE
|
||||
wchar_t *wbuffer = 0;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM
|
||||
|FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
, 0, err, 0, (LPWSTR)&wbuffer, 0, 0);
|
||||
auto_localfree auto_free(wbuffer);
|
||||
std::string tmp_utf8;
|
||||
libtorrent::wchar_utf8(wbuffer, tmp_utf8);
|
||||
char const* buffer = tmp_utf8.c_str();
|
||||
#else
|
||||
char* buffer = 0;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM
|
||||
|FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
, 0, err, 0, (LPSTR)&buffer, 0, 0);
|
||||
auto_localfree auto_free(buffer);
|
||||
#endif
|
||||
|
||||
std::stringstream s;
|
||||
s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL");
|
||||
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = s.str();
|
||||
}
|
||||
|
||||
impl()
|
||||
{
|
||||
m_file_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
void open(const char *file_name, open_flags flags)
|
||||
bool open(const char *file_name, open_flags flags)
|
||||
{
|
||||
TORRENT_ASSERT(file_name);
|
||||
TORRENT_ASSERT(flags & (read_flag | write_flag));
|
||||
|
@ -170,7 +172,10 @@ namespace libtorrent
|
|||
#endif
|
||||
|
||||
if (new_handle == INVALID_HANDLE_VALUE)
|
||||
throw_exception(file_name);
|
||||
{
|
||||
set_error(file_name);
|
||||
return false;
|
||||
}
|
||||
// try to make the file sparse if supported
|
||||
if (access_mask & GENERIC_WRITE)
|
||||
{
|
||||
|
@ -181,6 +186,7 @@ namespace libtorrent
|
|||
// will only close old file if the open succeeded
|
||||
close();
|
||||
m_file_handle = new_handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
void close()
|
||||
|
@ -211,7 +217,8 @@ namespace libtorrent
|
|||
, &bytes_written
|
||||
, 0))
|
||||
{
|
||||
throw_exception("file::write");
|
||||
set_error("file::write");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return bytes_written;
|
||||
|
@ -233,20 +240,23 @@ namespace libtorrent
|
|||
, &bytes_read
|
||||
, 0))
|
||||
{
|
||||
throw_exception("file::read");
|
||||
set_error("file::set_size");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
void set_size(size_type s)
|
||||
bool set_size(size_type s)
|
||||
{
|
||||
size_type pos = tell();
|
||||
seek(s, seek_begin);
|
||||
if (FALSE == ::SetEndOfFile(m_file_handle))
|
||||
throw_exception("file::set_size");
|
||||
|
||||
seek(pos, seek_begin);
|
||||
{
|
||||
set_error("file::set_size");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_type seek(size_type pos, seek_mode from_where)
|
||||
|
@ -261,7 +271,8 @@ namespace libtorrent
|
|||
, &offs
|
||||
, from_where))
|
||||
{
|
||||
throw_exception("file::seek");
|
||||
set_error("file::seek");
|
||||
return -1;
|
||||
}
|
||||
return offs.QuadPart;
|
||||
}
|
||||
|
@ -278,7 +289,8 @@ namespace libtorrent
|
|||
, &offs
|
||||
, FILE_CURRENT))
|
||||
{
|
||||
throw_exception("file::tell");
|
||||
set_error("file::tell");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_type pos = offs.QuadPart;
|
||||
|
@ -299,9 +311,17 @@ namespace libtorrent
|
|||
return size;
|
||||
}
|
||||
*/
|
||||
|
||||
std::string const& error() const
|
||||
{
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
return *m_error;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
HANDLE m_file_handle;
|
||||
mutable boost::scoped_ptr<std::string> m_error;
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -329,10 +349,10 @@ namespace libtorrent
|
|||
{
|
||||
}
|
||||
|
||||
void file::open(boost::filesystem::path const& p, open_mode m)
|
||||
bool file::open(boost::filesystem::path const& p, open_mode m)
|
||||
{
|
||||
TORRENT_ASSERT(p.is_complete());
|
||||
m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
|
||||
return m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask));
|
||||
}
|
||||
|
||||
void file::close()
|
||||
|
@ -350,9 +370,9 @@ namespace libtorrent
|
|||
return m_impl->read(buffer, num_bytes);
|
||||
}
|
||||
|
||||
void file::set_size(size_type s)
|
||||
bool file::set_size(size_type s)
|
||||
{
|
||||
m_impl->set_size(s);
|
||||
return m_impl->set_size(s);
|
||||
}
|
||||
|
||||
size_type file::seek(size_type pos, seek_mode m)
|
||||
|
@ -364,4 +384,9 @@ namespace libtorrent
|
|||
{
|
||||
return m_impl->tell();
|
||||
}
|
||||
|
||||
std::string const& file::error() const
|
||||
{
|
||||
return m_impl->error();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2007, 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 "libtorrent/assert.hpp"
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
FTEXT = 0x01,
|
||||
FHCRC = 0x02,
|
||||
FEXTRA = 0x04,
|
||||
FNAME = 0x08,
|
||||
FCOMMENT = 0x10,
|
||||
FRESERVED = 0xe0,
|
||||
|
||||
GZIP_MAGIC0 = 0x1f,
|
||||
GZIP_MAGIC1 = 0x8b
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
// returns -1 if gzip header is invalid or the header size in bytes
|
||||
int gzip_header(const char* buf, int size)
|
||||
{
|
||||
TORRENT_ASSERT(buf != 0);
|
||||
TORRENT_ASSERT(size > 0);
|
||||
|
||||
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
|
||||
const int total_size = size;
|
||||
|
||||
// The zip header cannot be shorter than 10 bytes
|
||||
if (size < 10) return -1;
|
||||
|
||||
// check the magic header of gzip
|
||||
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
|
||||
|
||||
int method = buffer[2];
|
||||
int flags = buffer[3];
|
||||
|
||||
// check for reserved flag and make sure it's compressed with the correct metod
|
||||
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
|
||||
|
||||
// skip time, xflags, OS code
|
||||
size -= 10;
|
||||
buffer += 10;
|
||||
|
||||
if (flags & FEXTRA)
|
||||
{
|
||||
int extra_len;
|
||||
|
||||
if (size < 2) return -1;
|
||||
|
||||
extra_len = (buffer[1] << 8) | buffer[0];
|
||||
|
||||
if (size < (extra_len+2)) return -1;
|
||||
size -= (extra_len + 2);
|
||||
buffer += (extra_len + 2);
|
||||
}
|
||||
|
||||
if (flags & FNAME)
|
||||
{
|
||||
while (size && *buffer)
|
||||
{
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
if (!size || *buffer) return -1;
|
||||
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
|
||||
if (flags & FCOMMENT)
|
||||
{
|
||||
while (size && *buffer)
|
||||
{
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
if (!size || *buffer) return -1;
|
||||
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
|
||||
if (flags & FHCRC)
|
||||
{
|
||||
if (size < 2) return -1;
|
||||
|
||||
size -= 2;
|
||||
buffer += 2;
|
||||
}
|
||||
|
||||
return total_size - size;
|
||||
}
|
||||
|
||||
bool inflate_gzip(
|
||||
char const* in
|
||||
, int size
|
||||
, std::vector<char>& buffer
|
||||
, int maximum_size
|
||||
, std::string& error)
|
||||
{
|
||||
TORRENT_ASSERT(maximum_size > 0);
|
||||
|
||||
int header_len = gzip_header(in, size);
|
||||
if (header_len < 0)
|
||||
{
|
||||
error = "invalid gzip header in tracker response";
|
||||
return true;
|
||||
}
|
||||
|
||||
// start off with one kilobyte and grow
|
||||
// if needed
|
||||
buffer.resize(1024);
|
||||
|
||||
// initialize the zlib-stream
|
||||
z_stream str;
|
||||
|
||||
// subtract 8 from the end of the buffer since that's CRC32 and input size
|
||||
// and those belong to the gzip file
|
||||
str.avail_in = (int)size - header_len - 8;
|
||||
str.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(in + header_len));
|
||||
str.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
|
||||
str.avail_out = (int)buffer.size();
|
||||
str.zalloc = Z_NULL;
|
||||
str.zfree = Z_NULL;
|
||||
str.opaque = 0;
|
||||
// -15 is really important. It will make inflate() not look for a zlib header
|
||||
// and just deflate the buffer
|
||||
if (inflateInit2(&str, -15) != Z_OK)
|
||||
{
|
||||
error = "gzip out of memory";
|
||||
return true;
|
||||
}
|
||||
|
||||
// inflate and grow inflate_buffer as needed
|
||||
int ret = inflate(&str, Z_SYNC_FLUSH);
|
||||
while (ret == Z_OK)
|
||||
{
|
||||
if (str.avail_out == 0)
|
||||
{
|
||||
if (buffer.size() >= (unsigned)maximum_size)
|
||||
{
|
||||
inflateEnd(&str);
|
||||
error = "response too large";
|
||||
return true;
|
||||
}
|
||||
int new_size = (int)buffer.size() * 2;
|
||||
if (new_size > maximum_size)
|
||||
new_size = maximum_size;
|
||||
int old_size = (int)buffer.size();
|
||||
|
||||
buffer.resize(new_size);
|
||||
str.next_out = reinterpret_cast<Bytef*>(&buffer[old_size]);
|
||||
str.avail_out = new_size - old_size;
|
||||
}
|
||||
|
||||
ret = inflate(&str, Z_SYNC_FLUSH);
|
||||
}
|
||||
|
||||
buffer.resize(buffer.size() - str.avail_out);
|
||||
inflateEnd(&str);
|
||||
|
||||
if (ret != Z_STREAM_END)
|
||||
{
|
||||
error = "gzip error";
|
||||
return true;
|
||||
}
|
||||
|
||||
// commit the resulting buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -31,6 +31,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
*/
|
||||
|
||||
#include "libtorrent/http_connection.hpp"
|
||||
#include "libtorrent/escape_string.hpp"
|
||||
#include "libtorrent/instantiate_connection.hpp"
|
||||
#include "libtorrent/gzip.hpp"
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
@ -39,13 +43,14 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
using boost::bind;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
namespace libtorrent {
|
||||
|
||||
enum { max_bottled_buffer = 1024 * 1024 };
|
||||
enum { max_bottled_buffer = 1024 * 1024 };
|
||||
|
||||
void http_connection::get(std::string const& url, time_duration timeout
|
||||
, int handle_redirects)
|
||||
|
||||
void http_connection::get(std::string const& url, time_duration timeout, int prio
|
||||
, proxy_settings const* ps, int handle_redirects, std::string const& user_agent
|
||||
, address const& bind_addr)
|
||||
{
|
||||
std::string protocol;
|
||||
std::string auth;
|
||||
|
@ -53,37 +58,125 @@ void http_connection::get(std::string const& url, time_duration timeout
|
|||
std::string path;
|
||||
int port;
|
||||
boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url);
|
||||
|
||||
TORRENT_ASSERT(prio >= 0 && prio < 2);
|
||||
|
||||
bool ssl = false;
|
||||
if (protocol == "https") ssl = true;
|
||||
#ifndef TORRENT_USE_OPENSSL
|
||||
if (ssl)
|
||||
{
|
||||
callback(asio::error::socket_type_not_supported);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::stringstream headers;
|
||||
headers << "GET " << path << " HTTP/1.0\r\n"
|
||||
"Host:" << hostname <<
|
||||
"\r\nConnection: close\r\n";
|
||||
if (ps && (ps->type == proxy_settings::http
|
||||
|| ps->type == proxy_settings::http_pw)
|
||||
&& !ssl)
|
||||
{
|
||||
// if we're using an http proxy and not an ssl
|
||||
// connection, just do a regular http proxy request
|
||||
headers << "GET " << url << " HTTP/1.0\r\n";
|
||||
if (ps->type == proxy_settings::http_pw)
|
||||
headers << "Proxy-Authorization: Basic " << base64encode(
|
||||
ps->username + ":" + ps->password) << "\r\n";
|
||||
hostname = ps->hostname;
|
||||
port = ps->port;
|
||||
ps = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
headers << "GET " << path << " HTTP/1.0\r\n"
|
||||
"Host:" << hostname << "\r\n";
|
||||
}
|
||||
|
||||
if (!auth.empty())
|
||||
headers << "Authorization: Basic " << base64encode(auth) << "\r\n";
|
||||
headers << "\r\n";
|
||||
|
||||
if (!user_agent.empty())
|
||||
headers << "User-Agent: " << user_agent << "\r\n";
|
||||
|
||||
headers <<
|
||||
"Connection: close\r\n"
|
||||
"Accept-Encoding: gzip\r\n"
|
||||
"\r\n";
|
||||
|
||||
sendbuffer = headers.str();
|
||||
start(hostname, boost::lexical_cast<std::string>(port), timeout, handle_redirects);
|
||||
start(hostname, boost::lexical_cast<std::string>(port), timeout, prio
|
||||
, ps, ssl, handle_redirects, bind_addr);
|
||||
}
|
||||
|
||||
void http_connection::start(std::string const& hostname, std::string const& port
|
||||
, time_duration timeout, int handle_redirects)
|
||||
, time_duration timeout, int prio, proxy_settings const* ps, bool ssl, int handle_redirects
|
||||
, address const& bind_addr)
|
||||
{
|
||||
m_redirects = handle_redirects;
|
||||
TORRENT_ASSERT(prio >= 0 && prio < 2);
|
||||
|
||||
m_redirects = handle_redirects;
|
||||
if (ps) m_proxy = *ps;
|
||||
|
||||
m_timeout = timeout;
|
||||
m_timer.expires_from_now(m_timeout);
|
||||
asio::error_code ec;
|
||||
m_timer.expires_from_now(m_timeout, ec);
|
||||
m_timer.async_wait(bind(&http_connection::on_timeout
|
||||
, boost::weak_ptr<http_connection>(shared_from_this()), _1));
|
||||
m_called = false;
|
||||
m_parser.reset();
|
||||
m_recvbuffer.clear();
|
||||
m_read_pos = 0;
|
||||
if (m_sock.is_open() && m_hostname == hostname && m_port == port)
|
||||
m_parser.reset();
|
||||
m_recvbuffer.clear();
|
||||
m_read_pos = 0;
|
||||
m_priority = prio;
|
||||
|
||||
if (ec)
|
||||
{
|
||||
callback(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_sock.is_open() && m_hostname == hostname && m_port == port
|
||||
&& m_ssl == ssl && m_bind_addr == bind_addr)
|
||||
{
|
||||
asio::async_write(m_sock, asio::buffer(sendbuffer)
|
||||
, bind(&http_connection::on_write, shared_from_this(), _1));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sock.close();
|
||||
m_ssl = ssl;
|
||||
m_bind_addr = bind_addr;
|
||||
asio::error_code ec;
|
||||
m_sock.close(ec);
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (m_ssl)
|
||||
{
|
||||
m_sock.instantiate<ssl_stream<socket_type> >(m_resolver.get_io_service());
|
||||
ssl_stream<socket_type>& s = m_sock.get<ssl_stream<socket_type> >();
|
||||
bool ret = instantiate_connection(m_resolver.get_io_service(), m_proxy, s.next_layer());
|
||||
TORRENT_ASSERT(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sock.instantiate<socket_type>(m_resolver.get_io_service());
|
||||
bool ret = instantiate_connection(m_resolver.get_io_service()
|
||||
, m_proxy, m_sock.get<socket_type>());
|
||||
TORRENT_ASSERT(ret);
|
||||
}
|
||||
#else
|
||||
bool ret = instantiate_connection(m_resolver.get_io_service(), m_proxy, m_sock);
|
||||
TORRENT_ASSERT(ret);
|
||||
#endif
|
||||
if (m_bind_addr != address_v4::any())
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_sock.bind(tcp::endpoint(m_bind_addr, 0), ec);
|
||||
if (ec)
|
||||
{
|
||||
callback(ec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tcp::resolver::query query(hostname, port);
|
||||
m_resolver.async_resolve(query, bind(&http_connection::on_resolve
|
||||
, shared_from_this(), _1, _2));
|
||||
|
@ -119,16 +212,17 @@ void http_connection::on_timeout(boost::weak_ptr<http_connection> p
|
|||
}
|
||||
|
||||
if (!c->m_sock.is_open()) return;
|
||||
|
||||
c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
|
||||
asio::error_code ec;
|
||||
c->m_timer.expires_at(c->m_last_receive + c->m_timeout, ec);
|
||||
c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
|
||||
}
|
||||
|
||||
void http_connection::close()
|
||||
{
|
||||
m_timer.cancel();
|
||||
m_limiter_timer.cancel();
|
||||
m_sock.close();
|
||||
asio::error_code ec;
|
||||
m_timer.cancel(ec);
|
||||
m_limiter_timer.cancel(ec);
|
||||
m_sock.close(ec);
|
||||
m_hostname.clear();
|
||||
m_port.clear();
|
||||
|
||||
|
@ -139,7 +233,7 @@ void http_connection::close()
|
|||
}
|
||||
|
||||
void http_connection::on_resolve(asio::error_code const& e
|
||||
, tcp::resolver::iterator i)
|
||||
, tcp::resolver::iterator i)
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
|
@ -148,9 +242,24 @@ void http_connection::on_resolve(asio::error_code const& e
|
|||
return;
|
||||
}
|
||||
TORRENT_ASSERT(i != tcp::resolver::iterator());
|
||||
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
|
||||
|
||||
// look for an address that has the same kind as the one
|
||||
// we're binding to. To make sure a tracker get our
|
||||
// correct listening address.
|
||||
tcp::resolver::iterator target = i;
|
||||
tcp::resolver::iterator end;
|
||||
tcp::endpoint target_address = *i;
|
||||
for (; target != end && target->endpoint().address().is_v4()
|
||||
!= m_bind_addr.is_v4(); ++target);
|
||||
|
||||
if (target != end)
|
||||
{
|
||||
target_address = *target;
|
||||
}
|
||||
|
||||
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, target_address)
|
||||
, bind(&http_connection::on_connect_timeout, shared_from_this())
|
||||
, m_timeout);
|
||||
, m_timeout, m_priority);
|
||||
}
|
||||
|
||||
void http_connection::connect(int ticket, tcp::endpoint target_address)
|
||||
|
@ -176,7 +285,7 @@ void http_connection::on_connect(asio::error_code const& e
|
|||
m_sock.close();
|
||||
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
|
||||
, bind(&http_connection::on_connect_timeout, shared_from_this())
|
||||
, m_timeout);
|
||||
, m_timeout, m_priority);
|
||||
}
|
||||
*/ else
|
||||
{
|
||||
|
@ -189,7 +298,25 @@ void http_connection::callback(asio::error_code const& e, char const* data, int
|
|||
{
|
||||
if (!m_bottled || !m_called)
|
||||
{
|
||||
std::vector<char> buf;
|
||||
if (m_bottled && m_parser.finished())
|
||||
{
|
||||
std::string const& encoding = m_parser.header("content-encoding");
|
||||
if (encoding == "gzip" || encoding == "x-gzip")
|
||||
{
|
||||
std::string error;
|
||||
if (inflate_gzip(data, size, buf, max_bottled_buffer, error))
|
||||
{
|
||||
callback(asio::error::fault, data, size);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
data = &buf[0];
|
||||
size = int(buf.size());
|
||||
}
|
||||
}
|
||||
m_called = true;
|
||||
m_timer.cancel();
|
||||
if (m_handler) m_handler(e, m_parser, data, size);
|
||||
}
|
||||
}
|
||||
|
@ -262,15 +389,13 @@ void http_connection::on_read(asio::error_code const& e
|
|||
{
|
||||
libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
|
||||
, &m_recvbuffer[0] + m_read_pos);
|
||||
try
|
||||
bool error = false;
|
||||
m_parser.incoming(rcv_buf, error);
|
||||
if (error)
|
||||
{
|
||||
m_parser.incoming(rcv_buf);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
m_timer.cancel();
|
||||
m_handler(asio::error::fault, m_parser, 0, 0);
|
||||
m_handler.clear();
|
||||
// HTTP parse error
|
||||
asio::error_code ec = asio::error::fault;
|
||||
callback(ec, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -292,7 +417,7 @@ void http_connection::on_read(asio::error_code const& e
|
|||
|
||||
asio::error_code ec;
|
||||
m_sock.close(ec);
|
||||
get(url, m_timeout, m_redirects - 1);
|
||||
get(url, m_timeout, m_priority, &m_proxy, m_redirects - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -309,7 +434,8 @@ void http_connection::on_read(asio::error_code const& e
|
|||
}
|
||||
else if (m_bottled && m_parser.finished())
|
||||
{
|
||||
m_timer.cancel();
|
||||
asio::error_code ec;
|
||||
m_timer.cancel(ec);
|
||||
callback(e, m_parser.get_body().begin, m_parser.get_body().left());
|
||||
}
|
||||
}
|
||||
|
@ -373,8 +499,9 @@ void http_connection::on_assign_bandwidth(asio::error_code const& e)
|
|||
, bind(&http_connection::on_read
|
||||
, shared_from_this(), _1, _2));
|
||||
|
||||
asio::error_code ec;
|
||||
m_limiter_timer_active = true;
|
||||
m_limiter_timer.expires_from_now(milliseconds(250));
|
||||
m_limiter_timer.expires_from_now(milliseconds(250), ec);
|
||||
m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
|
||||
, shared_from_this(), _1));
|
||||
}
|
||||
|
@ -385,8 +512,9 @@ void http_connection::rate_limit(int limit)
|
|||
|
||||
if (!m_limiter_timer_active)
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_limiter_timer_active = true;
|
||||
m_limiter_timer.expires_from_now(milliseconds(250));
|
||||
m_limiter_timer.expires_from_now(milliseconds(250), ec);
|
||||
m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
|
||||
, shared_from_this(), _1));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2008, 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 "libtorrent/pch.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
namespace
|
||||
{
|
||||
char to_lower(char c) { return std::tolower(c); }
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
http_parser::http_parser()
|
||||
: m_recv_pos(0)
|
||||
, m_status_code(-1)
|
||||
, m_content_length(-1)
|
||||
, m_state(read_status)
|
||||
, m_recv_buffer(0, 0)
|
||||
, m_body_start_pos(0)
|
||||
, m_finished(false)
|
||||
{}
|
||||
|
||||
boost::tuple<int, int> http_parser::incoming(
|
||||
buffer::const_interval recv_buffer, bool& error)
|
||||
{
|
||||
TORRENT_ASSERT(recv_buffer.left() >= m_recv_buffer.left());
|
||||
boost::tuple<int, int> ret(0, 0);
|
||||
|
||||
// early exit if there's nothing new in the receive buffer
|
||||
if (recv_buffer.left() == m_recv_buffer.left()) return ret;
|
||||
m_recv_buffer = recv_buffer;
|
||||
|
||||
if (m_state == error_state)
|
||||
{
|
||||
error = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char const* pos = recv_buffer.begin + m_recv_pos;
|
||||
if (m_state == read_status)
|
||||
{
|
||||
TORRENT_ASSERT(!m_finished);
|
||||
char const* newline = std::find(pos, recv_buffer.end, '\n');
|
||||
// if we don't have a full line yet, wait.
|
||||
if (newline == recv_buffer.end) return ret;
|
||||
|
||||
if (newline == pos)
|
||||
{
|
||||
m_state = error_state;
|
||||
error = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char const* line_end = newline;
|
||||
if (pos != line_end && *(line_end - 1) == '\r') --line_end;
|
||||
|
||||
std::istringstream line(std::string(pos, line_end));
|
||||
++newline;
|
||||
int incoming = (int)std::distance(pos, newline);
|
||||
m_recv_pos += incoming;
|
||||
boost::get<1>(ret) += incoming;
|
||||
pos = newline;
|
||||
|
||||
line >> m_protocol;
|
||||
if (m_protocol.substr(0, 5) == "HTTP/")
|
||||
{
|
||||
line >> m_status_code;
|
||||
std::getline(line, m_server_message);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_method = m_protocol;
|
||||
std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower);
|
||||
m_protocol.clear();
|
||||
line >> m_path >> m_protocol;
|
||||
m_status_code = 0;
|
||||
}
|
||||
m_state = read_header;
|
||||
}
|
||||
|
||||
if (m_state == read_header)
|
||||
{
|
||||
TORRENT_ASSERT(!m_finished);
|
||||
char const* newline = std::find(pos, recv_buffer.end, '\n');
|
||||
std::string line;
|
||||
|
||||
while (newline != recv_buffer.end && m_state == read_header)
|
||||
{
|
||||
// if the LF character is preceeded by a CR
|
||||
// charachter, don't copy it into the line string.
|
||||
char const* line_end = newline;
|
||||
if (pos != line_end && *(line_end - 1) == '\r') --line_end;
|
||||
line.assign(pos, line_end);
|
||||
++newline;
|
||||
m_recv_pos += newline - pos;
|
||||
boost::get<1>(ret) += newline - pos;
|
||||
pos = newline;
|
||||
|
||||
std::string::size_type separator = line.find(':');
|
||||
if (separator == std::string::npos)
|
||||
{
|
||||
// this means we got a blank line,
|
||||
// the header is finished and the body
|
||||
// starts.
|
||||
m_state = read_body;
|
||||
m_body_start_pos = m_recv_pos;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string name = line.substr(0, separator);
|
||||
std::transform(name.begin(), name.end(), name.begin(), &to_lower);
|
||||
++separator;
|
||||
// skip whitespace
|
||||
while (separator < line.size()
|
||||
&& (line[separator] == ' ' || line[separator] == '\t'))
|
||||
++separator;
|
||||
std::string value = line.substr(separator, std::string::npos);
|
||||
m_header.insert(std::make_pair(name, value));
|
||||
|
||||
if (name == "content-length")
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_content_length = _atoi64(value.c_str());
|
||||
#else
|
||||
m_content_length = atoll(value.c_str());
|
||||
#endif
|
||||
}
|
||||
else if (name == "content-range")
|
||||
{
|
||||
std::stringstream range_str(value);
|
||||
char dummy;
|
||||
std::string bytes;
|
||||
size_type range_start, range_end;
|
||||
// apparently some web servers do not send the "bytes"
|
||||
// in their content-range
|
||||
if (value.find(' ') != std::string::npos)
|
||||
range_str >> bytes;
|
||||
range_str >> range_start >> dummy >> range_end;
|
||||
if (!range_str || range_end < range_start)
|
||||
{
|
||||
m_state = error_state;
|
||||
error = true;
|
||||
return ret;
|
||||
}
|
||||
// the http range is inclusive
|
||||
m_content_length = range_end - range_start + 1;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(m_recv_pos <= (int)recv_buffer.left());
|
||||
newline = std::find(pos, recv_buffer.end, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state == read_body)
|
||||
{
|
||||
int incoming = recv_buffer.end - pos;
|
||||
if (m_recv_pos - m_body_start_pos + incoming > m_content_length
|
||||
&& m_content_length >= 0)
|
||||
incoming = m_content_length - m_recv_pos + m_body_start_pos;
|
||||
|
||||
TORRENT_ASSERT(incoming >= 0);
|
||||
m_recv_pos += incoming;
|
||||
boost::get<0>(ret) += incoming;
|
||||
|
||||
if (m_content_length >= 0
|
||||
&& m_recv_pos - m_body_start_pos >= m_content_length)
|
||||
{
|
||||
m_finished = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
buffer::const_interval http_parser::get_body() const
|
||||
{
|
||||
TORRENT_ASSERT(m_state == read_body);
|
||||
if (m_content_length >= 0)
|
||||
return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
|
||||
, m_recv_buffer.begin + (std::min)(size_type(m_recv_pos)
|
||||
, m_body_start_pos + m_content_length));
|
||||
else
|
||||
return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
|
||||
, m_recv_buffer.begin + m_recv_pos);
|
||||
}
|
||||
|
||||
void http_parser::reset()
|
||||
{
|
||||
m_recv_pos = 0;
|
||||
m_body_start_pos = 0;
|
||||
m_status_code = -1;
|
||||
m_content_length = -1;
|
||||
m_finished = false;
|
||||
m_state = read_status;
|
||||
m_recv_buffer.begin = 0;
|
||||
m_recv_buffer.end = 0;
|
||||
m_header.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/pch.hpp"
|
||||
|
||||
#include "libtorrent/http_stream.hpp"
|
||||
#include "libtorrent/tracker_manager.hpp" // for base64encode
|
||||
#include "libtorrent/escape_string.hpp" // for base64encode
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -44,7 +44,8 @@ namespace libtorrent
|
|||
if (e || i == tcp::resolver::iterator())
|
||||
{
|
||||
(*h)(e);
|
||||
close();
|
||||
asio::error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,7 +58,8 @@ namespace libtorrent
|
|||
if (e)
|
||||
{
|
||||
(*h)(e);
|
||||
close();
|
||||
asio::error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -89,7 +91,8 @@ namespace libtorrent
|
|||
if (e)
|
||||
{
|
||||
(*h)(e);
|
||||
close();
|
||||
asio::error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -104,7 +107,8 @@ namespace libtorrent
|
|||
if (e)
|
||||
{
|
||||
(*h)(e);
|
||||
close();
|
||||
asio::error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -134,7 +138,8 @@ namespace libtorrent
|
|||
if (status == 0)
|
||||
{
|
||||
(*h)(asio::error::operation_not_supported);
|
||||
close();
|
||||
asio::error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -143,7 +148,8 @@ namespace libtorrent
|
|||
if (code != 200)
|
||||
{
|
||||
(*h)(asio::error::operation_not_supported);
|
||||
close();
|
||||
asio::error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -100,6 +100,7 @@ closest_nodes::closest_nodes(
|
|||
|
||||
void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
|
||||
{
|
||||
TORRENT_ASSERT(m_rpc.allocation_size() >= sizeof(closest_nodes_observer));
|
||||
observer_ptr o(new (m_rpc.allocator().malloc()) closest_nodes_observer(this, id, m_target));
|
||||
#ifndef NDEBUG
|
||||
o->m_in_constructor = false;
|
||||
|
|
|
@ -58,7 +58,6 @@ using libtorrent::dht::node_impl;
|
|||
using libtorrent::dht::node_id;
|
||||
using libtorrent::dht::packet_t;
|
||||
using libtorrent::dht::msg;
|
||||
using libtorrent::dht::packet_iterator;
|
||||
namespace messages = libtorrent::dht::messages;
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
|
@ -145,27 +144,23 @@ namespace libtorrent { namespace dht
|
|||
|
||||
// class that puts the networking and the kademlia node in a single
|
||||
// unit and connecting them together.
|
||||
dht_tracker::dht_tracker(asio::io_service& ios, dht_settings const& settings
|
||||
, asio::ip::address listen_interface, entry const& bootstrap)
|
||||
: m_strand(ios)
|
||||
, m_socket(ios, udp::endpoint(listen_interface, settings.service_port))
|
||||
, m_dht(bind(&dht_tracker::send_packet, this, _1), settings
|
||||
dht_tracker::dht_tracker(udp_socket& sock, dht_settings const& settings
|
||||
, entry const& bootstrap)
|
||||
: m_dht(bind(&dht_tracker::send_packet, this, _1), settings
|
||||
, read_id(bootstrap))
|
||||
, m_buffer(0)
|
||||
, m_sock(sock)
|
||||
, m_last_new_key(time_now() - minutes(key_refresh))
|
||||
, m_timer(ios)
|
||||
, m_connection_timer(ios)
|
||||
, m_refresh_timer(ios)
|
||||
, m_timer(sock.get_io_service())
|
||||
, m_connection_timer(sock.get_io_service())
|
||||
, m_refresh_timer(sock.get_io_service())
|
||||
, m_settings(settings)
|
||||
, m_refresh_bucket(160)
|
||||
, m_abort(false)
|
||||
, m_host_resolver(ios)
|
||||
, m_host_resolver(sock.get_io_service())
|
||||
, m_refs(0)
|
||||
{
|
||||
using boost::bind;
|
||||
|
||||
m_in_buf[0].resize(1000);
|
||||
m_in_buf[1].resize(1000);
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
m_counter = 0;
|
||||
std::fill_n(m_replies_bytes_sent, 5, 0);
|
||||
|
@ -203,18 +198,15 @@ namespace libtorrent { namespace dht
|
|||
} catch (std::exception&) {}
|
||||
}
|
||||
|
||||
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
|
||||
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
|
||||
, m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2)));
|
||||
m_timer.expires_from_now(seconds(1));
|
||||
m_timer.async_wait(m_strand.wrap(bind(&dht_tracker::tick, self(), _1)));
|
||||
m_timer.async_wait(bind(&dht_tracker::tick, self(), _1));
|
||||
|
||||
m_connection_timer.expires_from_now(seconds(10));
|
||||
m_connection_timer.async_wait(m_strand.wrap(
|
||||
bind(&dht_tracker::connection_timeout, self(), _1)));
|
||||
m_connection_timer.async_wait(
|
||||
bind(&dht_tracker::connection_timeout, self(), _1));
|
||||
|
||||
m_refresh_timer.expires_from_now(seconds(5));
|
||||
m_refresh_timer.async_wait(m_strand.wrap(bind(&dht_tracker::refresh_timeout, self(), _1)));
|
||||
m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, self(), _1));
|
||||
|
||||
m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, self()));
|
||||
}
|
||||
|
@ -226,7 +218,6 @@ namespace libtorrent { namespace dht
|
|||
m_timer.cancel();
|
||||
m_connection_timer.cancel();
|
||||
m_refresh_timer.cancel();
|
||||
m_socket.close();
|
||||
m_host_resolver.cancel();
|
||||
}
|
||||
|
||||
|
@ -243,10 +234,9 @@ namespace libtorrent { namespace dht
|
|||
mutex_t::scoped_lock l(m_mutex);
|
||||
if (e || m_abort) return;
|
||||
|
||||
if (!m_socket.is_open()) return;
|
||||
time_duration d = m_dht.connection_timeout();
|
||||
m_connection_timer.expires_from_now(d);
|
||||
m_connection_timer.async_wait(m_strand.wrap(bind(&dht_tracker::connection_timeout, self(), _1)));
|
||||
m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, self(), _1));
|
||||
}
|
||||
catch (std::exception& exc)
|
||||
{
|
||||
|
@ -263,37 +253,24 @@ namespace libtorrent { namespace dht
|
|||
mutex_t::scoped_lock l(m_mutex);
|
||||
if (e || m_abort) return;
|
||||
|
||||
if (!m_socket.is_open()) return;
|
||||
time_duration d = m_dht.refresh_timeout();
|
||||
m_refresh_timer.expires_from_now(d);
|
||||
m_refresh_timer.async_wait(m_strand.wrap(
|
||||
bind(&dht_tracker::refresh_timeout, self(), _1)));
|
||||
m_refresh_timer.async_wait(
|
||||
bind(&dht_tracker::refresh_timeout, self(), _1));
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
TORRENT_ASSERT(false);
|
||||
};
|
||||
|
||||
void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port)
|
||||
{
|
||||
m_socket.close();
|
||||
udp::endpoint ep(listen_interface, listen_port);
|
||||
m_socket.open(ep.protocol());
|
||||
m_socket.bind(ep);
|
||||
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
|
||||
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
|
||||
, m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2)));
|
||||
}
|
||||
|
||||
void dht_tracker::tick(asio::error_code const& e)
|
||||
try
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
if (e || m_abort) return;
|
||||
|
||||
if (!m_socket.is_open()) return;
|
||||
m_timer.expires_from_now(minutes(tick_period));
|
||||
m_timer.async_wait(m_strand.wrap(bind(&dht_tracker::tick, self(), _1)));
|
||||
m_timer.async_wait(bind(&dht_tracker::tick, self(), _1));
|
||||
|
||||
ptime now = time_now();
|
||||
if (now - m_last_new_key > minutes(key_refresh))
|
||||
|
@ -400,26 +377,15 @@ namespace libtorrent { namespace dht
|
|||
|
||||
// translate bittorrent kademlia message into the generice kademlia message
|
||||
// used by the library
|
||||
void dht_tracker::on_receive(asio::error_code const& error, size_t bytes_transferred)
|
||||
void dht_tracker::on_receive(udp::endpoint const& ep, char const* buf, int bytes_transferred)
|
||||
try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket.is_open()) return;
|
||||
|
||||
int current_buffer = m_buffer;
|
||||
m_buffer = (m_buffer + 1) & 1;
|
||||
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
|
||||
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
|
||||
, m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2)));
|
||||
|
||||
if (error) return;
|
||||
|
||||
node_ban_entry* match = 0;
|
||||
node_ban_entry* min = m_ban_nodes;
|
||||
ptime now = time_now();
|
||||
for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i)
|
||||
{
|
||||
if (i->src == m_remote_endpoint[current_buffer])
|
||||
if (i->src == ep)
|
||||
{
|
||||
match = i;
|
||||
break;
|
||||
|
@ -438,8 +404,7 @@ namespace libtorrent { namespace dht
|
|||
if (match->count == 20)
|
||||
{
|
||||
TORRENT_LOG(dht_tracker) << time_now_string() << " BANNING PEER [ ip: "
|
||||
<< m_remote_endpoint[current_buffer] << " | "
|
||||
"time: " << total_seconds((now - match->limit) + seconds(5))
|
||||
<< ep << " | time: " << total_milliseconds((now - match->limit) + seconds(5)) / 1000.f
|
||||
<< " | count: " << match->count << " ]";
|
||||
}
|
||||
#endif
|
||||
|
@ -459,7 +424,7 @@ namespace libtorrent { namespace dht
|
|||
{
|
||||
min->count = 1;
|
||||
min->limit = now + seconds(5);
|
||||
min->src = m_remote_endpoint[current_buffer];
|
||||
min->src = ep;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
|
@ -474,17 +439,16 @@ namespace libtorrent { namespace dht
|
|||
|
||||
TORRENT_ASSERT(bytes_transferred > 0);
|
||||
|
||||
entry e = bdecode(m_in_buf[current_buffer].begin()
|
||||
, m_in_buf[current_buffer].end());
|
||||
entry e = bdecode(buf, buf + bytes_transferred);
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(dht_tracker) << time_now_string() << " RECEIVED ["
|
||||
<< m_remote_endpoint[current_buffer] << "]:";
|
||||
<< ep << "]:";
|
||||
#endif
|
||||
|
||||
libtorrent::dht::msg m;
|
||||
m.message_id = 0;
|
||||
m.addr = m_remote_endpoint[current_buffer];
|
||||
m.addr = ep;
|
||||
m.transaction_id = e["t"].string();
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
|
@ -721,9 +685,7 @@ namespace libtorrent { namespace dht
|
|||
catch (std::exception& e)
|
||||
{
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
int current_buffer = (m_buffer + 1) & 1;
|
||||
std::string msg(m_in_buf[current_buffer].begin()
|
||||
, m_in_buf[current_buffer].begin() + bytes_transferred);
|
||||
std::string msg(buf, buf + bytes_transferred);
|
||||
TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
|
||||
<< e.what() << "\n" << msg << "\n";
|
||||
#endif
|
||||
|
@ -773,15 +735,14 @@ namespace libtorrent { namespace dht
|
|||
void dht_tracker::add_node(std::pair<std::string, int> const& node)
|
||||
{
|
||||
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
|
||||
m_host_resolver.async_resolve(q, m_strand.wrap(
|
||||
bind(&dht_tracker::on_name_lookup, self(), _1, _2)));
|
||||
m_host_resolver.async_resolve(q,
|
||||
bind(&dht_tracker::on_name_lookup, self(), _1, _2));
|
||||
}
|
||||
|
||||
void dht_tracker::on_name_lookup(asio::error_code const& e
|
||||
, udp::resolver::iterator host) try
|
||||
{
|
||||
if (e || host == udp::resolver::iterator()) return;
|
||||
if (!m_socket.is_open()) return;
|
||||
add_node(host->endpoint());
|
||||
}
|
||||
catch (std::exception&)
|
||||
|
@ -792,15 +753,14 @@ namespace libtorrent { namespace dht
|
|||
void dht_tracker::add_router_node(std::pair<std::string, int> const& node)
|
||||
{
|
||||
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
|
||||
m_host_resolver.async_resolve(q, m_strand.wrap(
|
||||
bind(&dht_tracker::on_router_name_lookup, self(), _1, _2)));
|
||||
m_host_resolver.async_resolve(q,
|
||||
bind(&dht_tracker::on_router_name_lookup, self(), _1, _2));
|
||||
}
|
||||
|
||||
void dht_tracker::on_router_name_lookup(asio::error_code const& e
|
||||
, udp::resolver::iterator host) try
|
||||
{
|
||||
if (e || host == udp::resolver::iterator()) return;
|
||||
if (!m_socket.is_open()) return;
|
||||
m_dht.add_router_node(host->endpoint());
|
||||
}
|
||||
catch (std::exception&)
|
||||
|
@ -998,9 +958,7 @@ namespace libtorrent { namespace dht
|
|||
m_send_buf.clear();
|
||||
bencode(std::back_inserter(m_send_buf), e);
|
||||
asio::error_code ec;
|
||||
m_socket.send_to(asio::buffer(&m_send_buf[0]
|
||||
, (int)m_send_buf.size()), m.addr, 0, ec);
|
||||
if (ec) return;
|
||||
m_sock.send(m.addr, &m_send_buf[0], (int)m_send_buf.size(), ec);
|
||||
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
m_total_out_bytes += m_send_buf.size();
|
||||
|
|
|
@ -109,6 +109,7 @@ void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr)
|
|||
return;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(m_rpc.allocation_size() >= sizeof(find_data_observer));
|
||||
observer_ptr o(new (m_rpc.allocator().malloc()) find_data_observer(this, id, m_target));
|
||||
#ifndef NDEBUG
|
||||
o->m_in_constructor = false;
|
||||
|
|
|
@ -43,7 +43,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/random_sample.hpp"
|
||||
#include "libtorrent/kademlia/node_id.hpp"
|
||||
#include "libtorrent/kademlia/rpc_manager.hpp"
|
||||
#include "libtorrent/kademlia/packet_iterator.hpp"
|
||||
#include "libtorrent/kademlia/routing_table.hpp"
|
||||
#include "libtorrent/kademlia/node.hpp"
|
||||
|
||||
|
|
|
@ -103,6 +103,7 @@ void ping_observer::timeout()
|
|||
|
||||
void refresh::invoke(node_id const& nid, udp::endpoint addr)
|
||||
{
|
||||
TORRENT_ASSERT(m_rpc.allocation_size() >= sizeof(refresh_observer));
|
||||
observer_ptr o(new (m_rpc.allocator().malloc()) refresh_observer(
|
||||
this, nid, m_target));
|
||||
#ifndef NDEBUG
|
||||
|
@ -157,6 +158,7 @@ void refresh::invoke_pings_or_finish(bool prevent_request)
|
|||
|
||||
try
|
||||
{
|
||||
TORRENT_ASSERT(m_rpc.allocation_size() >= sizeof(ping_observer));
|
||||
observer_ptr o(new (m_rpc.allocator().malloc()) ping_observer(
|
||||
this, node.id));
|
||||
#ifndef NDEBUG
|
||||
|
|
|
@ -355,6 +355,19 @@ bool routing_table::need_bootstrap() const
|
|||
return true;
|
||||
}
|
||||
|
||||
template <class SrcIter, class DstIter, class Pred>
|
||||
DstIter copy_if_n(SrcIter begin, SrcIter end, DstIter target, size_t n, Pred p)
|
||||
{
|
||||
for (; n > 0 && begin != end; ++begin)
|
||||
{
|
||||
if (!p(*begin)) continue;
|
||||
*target = *begin;
|
||||
--n;
|
||||
++target;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
// fills the vector with the k nodes from our buckets that
|
||||
// are nearest to the given id.
|
||||
void routing_table::find_node(node_id const& target
|
||||
|
@ -369,8 +382,8 @@ void routing_table::find_node(node_id const& target
|
|||
|
||||
// copy all nodes that hasn't failed into the target
|
||||
// vector.
|
||||
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
|
||||
, bind(&node_entry::fail_count, _1));
|
||||
copy_if_n(b.begin(), b.end(), std::back_inserter(l)
|
||||
, (std::min)(size_t(count), b.size()), bind(&node_entry::fail_count, _1) == 0);
|
||||
TORRENT_ASSERT((int)l.size() <= count);
|
||||
|
||||
if ((int)l.size() == count)
|
||||
|
@ -393,19 +406,23 @@ void routing_table::find_node(node_id const& target
|
|||
, bind(&node_entry::fail_count, _1));
|
||||
}
|
||||
|
||||
std::random_shuffle(tmpb.begin(), tmpb.end());
|
||||
size_t to_copy = (std::min)(m_bucket_size - l.size()
|
||||
, tmpb.size());
|
||||
std::copy(tmpb.begin(), tmpb.begin() + to_copy
|
||||
, std::back_inserter(l));
|
||||
if (count - l.size() < tmpb.size())
|
||||
{
|
||||
std::random_shuffle(tmpb.begin(), tmpb.end());
|
||||
size_t to_copy = count - l.size();
|
||||
std::copy(tmpb.begin(), tmpb.begin() + to_copy, std::back_inserter(l));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(tmpb.begin(), tmpb.end(), std::back_inserter(l));
|
||||
}
|
||||
|
||||
TORRENT_ASSERT((int)l.size() <= m_bucket_size);
|
||||
TORRENT_ASSERT((int)l.size() <= count);
|
||||
|
||||
// return if we have enough nodes or if the bucket index
|
||||
// is the biggest index available (there are no more buckets)
|
||||
// to look in.
|
||||
if ((int)l.size() == count
|
||||
|| bucket_index == (int)m_buckets.size() - 1)
|
||||
if ((int)l.size() == count)
|
||||
{
|
||||
TORRENT_ASSERT(std::count_if(l.begin(), l.end()
|
||||
, boost::bind(&node_entry::fail_count, _1) != 0) == 0);
|
||||
|
@ -416,18 +433,17 @@ void routing_table::find_node(node_id const& target
|
|||
{
|
||||
bucket_t& b = m_buckets[i].first;
|
||||
|
||||
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
|
||||
, bind(&node_entry::fail_count, _1));
|
||||
if ((int)l.size() >= count)
|
||||
size_t to_copy = (std::min)(count - l.size(), b.size());
|
||||
copy_if_n(b.begin(), b.end(), std::back_inserter(l)
|
||||
, to_copy, bind(&node_entry::fail_count, _1) == 0);
|
||||
TORRENT_ASSERT((int)l.size() <= count);
|
||||
if ((int)l.size() == count)
|
||||
{
|
||||
l.erase(l.begin() + count, l.end());
|
||||
TORRENT_ASSERT(std::count_if(l.begin(), l.end()
|
||||
, boost::bind(&node_entry::fail_count, _1) != 0) == 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
TORRENT_ASSERT((int)l.size() == count
|
||||
|| std::distance(l.begin(), l.end()) < m_bucket_size);
|
||||
TORRENT_ASSERT((int)l.size() <= count);
|
||||
|
||||
TORRENT_ASSERT(std::count_if(l.begin(), l.end()
|
||||
|
|
|
@ -121,6 +121,7 @@ rpc_manager::rpc_manager(fun const& f, node_id const& our_id
|
|||
|
||||
rpc_manager::~rpc_manager()
|
||||
{
|
||||
TORRENT_ASSERT(!m_destructing);
|
||||
m_destructing = true;
|
||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||
TORRENT_LOG(rpc) << "Destructing";
|
||||
|
@ -136,6 +137,12 @@ rpc_manager::~rpc_manager()
|
|||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t rpc_manager::allocation_size() const
|
||||
{
|
||||
size_t s = sizeof(mpl::deref<max_observer_type_iter::base>::type);
|
||||
return s;
|
||||
}
|
||||
|
||||
void rpc_manager::check_invariant() const
|
||||
{
|
||||
TORRENT_ASSERT(m_oldest_transaction_id >= 0);
|
||||
|
@ -302,7 +309,7 @@ time_duration rpc_manager::tick()
|
|||
// clear the aborted transactions, will likely
|
||||
// generate new requests. We need to swap, since the
|
||||
// destrutors may add more observers to the m_aborted_transactions
|
||||
std::vector<observer_ptr >().swap(m_aborted_transactions);
|
||||
std::vector<observer_ptr>().swap(m_aborted_transactions);
|
||||
return milliseconds(timeout_ms);
|
||||
}
|
||||
|
||||
|
@ -429,6 +436,7 @@ void rpc_manager::reply_with_ping(msg& m)
|
|||
std::back_insert_iterator<std::string> out(m.ping_transaction_id);
|
||||
io::write_uint16(m_next_transaction_id, out);
|
||||
|
||||
TORRENT_ASSERT(allocation_size() >= sizeof(null_observer));
|
||||
observer_ptr o(new (allocator().malloc()) null_observer(allocator()));
|
||||
#ifndef NDEBUG
|
||||
o->m_in_constructor = false;
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2008, 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 "libtorrent/lazy_entry.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
int fail_bdecode() { return -1; }
|
||||
|
||||
// fills in 'val' with what the string between start and the
|
||||
// first occurance of the delimiter is interpreted as an int.
|
||||
// return the pointer to the delimiter, or 0 if there is a
|
||||
// parse error. val should be initialized to zero
|
||||
char const* parse_int(char const* start, char const* end, char delimiter, boost::int64_t& val)
|
||||
{
|
||||
while (start < end && *start != delimiter)
|
||||
{
|
||||
using namespace std;
|
||||
if (!isdigit(*start)) { fail_bdecode(); return 0; }
|
||||
val *= 10;
|
||||
val += *start - '0';
|
||||
++start;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
char const* find_char(char const* start, char const* end, char delimiter)
|
||||
{
|
||||
while (start < end && *start != delimiter) ++start;
|
||||
return start;
|
||||
}
|
||||
|
||||
// return 0 = success
|
||||
int lazy_bdecode(char const* start, char const* end, lazy_entry& ret, int depth_limit)
|
||||
{
|
||||
ret.clear();
|
||||
if (start == end) return 0;
|
||||
|
||||
std::vector<lazy_entry*> stack;
|
||||
|
||||
stack.push_back(&ret);
|
||||
while (start < end)
|
||||
{
|
||||
if (stack.empty()) break; // done!
|
||||
|
||||
lazy_entry* top = stack.back();
|
||||
|
||||
if (int(stack.size()) > depth_limit) return fail_bdecode();
|
||||
if (start == end) return fail_bdecode();
|
||||
char t = *start;
|
||||
++start;
|
||||
if (start == end && t != 'e') return fail_bdecode();
|
||||
|
||||
switch (top->type())
|
||||
{
|
||||
case lazy_entry::dict_t:
|
||||
{
|
||||
if (t == 'e')
|
||||
{
|
||||
top->set_end(start);
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
boost::int64_t len = t - '0';
|
||||
start = parse_int(start, end, ':', len);
|
||||
if (start == 0 || start + len + 3 > end || *start != ':') return fail_bdecode();
|
||||
++start;
|
||||
lazy_entry* ent = top->dict_append(start);
|
||||
start += len;
|
||||
stack.push_back(ent);
|
||||
t = *start;
|
||||
++start;
|
||||
break;
|
||||
}
|
||||
case lazy_entry::list_t:
|
||||
{
|
||||
if (t == 'e')
|
||||
{
|
||||
top->set_end(start);
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
lazy_entry* ent = top->list_append();
|
||||
stack.push_back(ent);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
top = stack.back();
|
||||
switch (t)
|
||||
{
|
||||
case 'd':
|
||||
top->construct_dict(start - 1);
|
||||
continue;
|
||||
case 'l':
|
||||
top->construct_list(start - 1);
|
||||
continue;
|
||||
case 'i':
|
||||
{
|
||||
char const* int_start = start;
|
||||
start = find_char(start, end, 'e');
|
||||
top->construct_int(int_start, start - int_start);
|
||||
if (start == end) return fail_bdecode();
|
||||
TORRENT_ASSERT(*start == 'e');
|
||||
++start;
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
{
|
||||
using namespace std;
|
||||
if (!isdigit(t)) return fail_bdecode();
|
||||
|
||||
boost::int64_t len = t - '0';
|
||||
start = parse_int(start, end, ':', len);
|
||||
if (start == 0 || start + len + 1 > end || *start != ':') return fail_bdecode();
|
||||
++start;
|
||||
top->construct_string(start, int(len));
|
||||
stack.pop_back();
|
||||
start += len;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
boost::int64_t lazy_entry::int_value() const
|
||||
{
|
||||
TORRENT_ASSERT(m_type == int_t);
|
||||
boost::int64_t val = 0;
|
||||
bool negative = false;
|
||||
if (*m_data.start == '-') negative = true;
|
||||
parse_int(negative?m_data.start+1:m_data.start, m_data.start + m_size, 'e', val);
|
||||
if (negative) val = -val;
|
||||
return val;
|
||||
}
|
||||
|
||||
lazy_entry* lazy_entry::dict_append(char const* name)
|
||||
{
|
||||
TORRENT_ASSERT(m_type == dict_t);
|
||||
TORRENT_ASSERT(m_size <= m_capacity);
|
||||
if (m_capacity == 0)
|
||||
{
|
||||
int capacity = 10;
|
||||
m_data.dict = new (std::nothrow) std::pair<char const*, lazy_entry>[capacity];
|
||||
if (m_data.dict == 0) return 0;
|
||||
m_capacity = capacity;
|
||||
}
|
||||
else if (m_size == m_capacity)
|
||||
{
|
||||
int capacity = m_capacity * 2;
|
||||
std::pair<char const*, lazy_entry>* tmp = new (std::nothrow) std::pair<char const*, lazy_entry>[capacity];
|
||||
if (tmp == 0) return 0;
|
||||
std::memcpy(tmp, m_data.dict, sizeof(std::pair<char const*, lazy_entry>) * m_size);
|
||||
delete[] m_data.dict;
|
||||
m_data.dict = tmp;
|
||||
m_capacity = capacity;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(m_size < m_capacity);
|
||||
std::pair<char const*, lazy_entry>& ret = m_data.dict[m_size++];
|
||||
ret.first = name;
|
||||
return &ret.second;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// the number of decimal digits needed
|
||||
// to represent the given value
|
||||
int num_digits(int val)
|
||||
{
|
||||
int ret = 1;
|
||||
while (val > 10)
|
||||
{
|
||||
++ret;
|
||||
val /= 10;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void lazy_entry::construct_string(char const* start, int length)
|
||||
{
|
||||
TORRENT_ASSERT(m_type == none_t);
|
||||
m_type = string_t;
|
||||
m_data.start = start;
|
||||
m_size = length;
|
||||
m_begin = start - 1 - num_digits(length);
|
||||
m_end = start + length;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// str1 is null-terminated
|
||||
// str2 is not, str2 is len2 chars
|
||||
bool string_equal(char const* str1, char const* str2, int len2)
|
||||
{
|
||||
while (len2 > 0)
|
||||
{
|
||||
if (*str1 != *str2) return false;
|
||||
if (*str1 == 0) return false;
|
||||
++str1;
|
||||
++str2;
|
||||
--len2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
lazy_entry* lazy_entry::dict_find(char const* name)
|
||||
{
|
||||
TORRENT_ASSERT(m_type == dict_t);
|
||||
for (int i = 0; i < m_size; ++i)
|
||||
{
|
||||
std::pair<char const*, lazy_entry> const& e = m_data.dict[i];
|
||||
if (string_equal(name, e.first, e.second.m_begin - e.first))
|
||||
return &m_data.dict[i].second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
lazy_entry* lazy_entry::list_append()
|
||||
{
|
||||
TORRENT_ASSERT(m_type == list_t);
|
||||
TORRENT_ASSERT(m_size <= m_capacity);
|
||||
if (m_capacity == 0)
|
||||
{
|
||||
int capacity = 10;
|
||||
m_data.list = new (std::nothrow) lazy_entry[capacity];
|
||||
if (m_data.list == 0) return 0;
|
||||
m_capacity = capacity;
|
||||
}
|
||||
else if (m_size == m_capacity)
|
||||
{
|
||||
int capacity = m_capacity * 2;
|
||||
lazy_entry* tmp = new (std::nothrow) lazy_entry[capacity];
|
||||
if (tmp == 0) return 0;
|
||||
std::memcpy(tmp, m_data.list, sizeof(lazy_entry) * m_size);
|
||||
delete[] m_data.list;
|
||||
m_data.list = tmp;
|
||||
m_capacity = capacity;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(m_size < m_capacity);
|
||||
return m_data.list + (m_size++);
|
||||
}
|
||||
|
||||
void lazy_entry::clear()
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case list_t: delete[] m_data.list; break;
|
||||
case dict_t: delete[] m_data.dict; break;
|
||||
default: break;
|
||||
}
|
||||
m_size = 0;
|
||||
m_capacity = 0;
|
||||
m_type = none_t;
|
||||
}
|
||||
|
||||
std::pair<char const*, int> lazy_entry::data_section()
|
||||
{
|
||||
typedef std::pair<char const*, int> return_t;
|
||||
return return_t(m_begin, m_end - m_begin);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, lazy_entry const& e)
|
||||
{
|
||||
switch (e.type())
|
||||
{
|
||||
case lazy_entry::none_t: return os << "none";
|
||||
case lazy_entry::int_t: return os << e.int_value();
|
||||
case lazy_entry::string_t:
|
||||
{
|
||||
bool printable = true;
|
||||
char const* str = e.string_ptr();
|
||||
for (int i = 0; i < e.string_length(); ++i)
|
||||
{
|
||||
using namespace std;
|
||||
if (isprint(str[i])) continue;
|
||||
printable = false;
|
||||
break;
|
||||
}
|
||||
if (printable) return os << e.string_value();
|
||||
for (int i = 0; i < e.string_length(); ++i)
|
||||
os << std::hex << int((unsigned char)(str[i]));
|
||||
return os;
|
||||
}
|
||||
case lazy_entry::list_t:
|
||||
{
|
||||
os << "[";
|
||||
bool one_liner = (e.list_size() == 0
|
||||
|| e.list_at(0)->type() == lazy_entry::int_t
|
||||
|| (e.list_at(0)->type() == lazy_entry::string_t
|
||||
&& e.list_at(0)->string_length() < 5))
|
||||
&& e.list_size() < 5;
|
||||
if (!one_liner) os << "\n";
|
||||
for (int i = 0; i < e.list_size(); ++i)
|
||||
{
|
||||
if (i == 0 && one_liner) os << " ";
|
||||
os << *e.list_at(i);
|
||||
if (i < e.list_size() - 1) os << (one_liner?", ":",\n");
|
||||
else os << (one_liner?" ":"\n");
|
||||
}
|
||||
return os << "]";
|
||||
}
|
||||
case lazy_entry::dict_t:
|
||||
{
|
||||
os << "{";
|
||||
bool one_liner = (e.dict_size() == 0
|
||||
|| e.dict_at(0).second->type() == lazy_entry::int_t
|
||||
|| (e.dict_at(0).second->type() == lazy_entry::string_t
|
||||
&& e.dict_at(0).second->string_length() < 4)
|
||||
|| strlen(e.dict_at(0).first) < 10)
|
||||
&& e.dict_size() < 5;
|
||||
|
||||
if (!one_liner) os << "\n";
|
||||
for (int i = 0; i < e.dict_size(); ++i)
|
||||
{
|
||||
if (i == 0 && one_liner) os << " ";
|
||||
std::pair<char const*, lazy_entry const*> ent = e.dict_at(i);
|
||||
os << "'" << ent.first << "': " << *ent.second;
|
||||
if (i < e.dict_size() - 1) os << (one_liner?", ":",\n");
|
||||
else os << (one_liner?" ":"\n");
|
||||
}
|
||||
return os << "}";
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -35,6 +35,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/lsd.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/buffer.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
|
@ -95,11 +97,11 @@ void lsd::announce(sha1_hash const& ih, int listen_port)
|
|||
<< " ==> announce: ih: " << ih << " port: " << listen_port << std::endl;
|
||||
#endif
|
||||
|
||||
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
|
||||
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
|
||||
m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
|
||||
}
|
||||
|
||||
void lsd::resend_announce(asio::error_code const& e, std::string msg) try
|
||||
void lsd::resend_announce(asio::error_code const& e, std::string msg)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
|
@ -110,11 +112,9 @@ void lsd::resend_announce(asio::error_code const& e, std::string msg) try
|
|||
if (m_retry_count >= 5)
|
||||
return;
|
||||
|
||||
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
|
||||
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
|
||||
m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
|
||||
}
|
||||
catch (std::exception&)
|
||||
{}
|
||||
|
||||
void lsd::on_announce(udp::endpoint const& from, char* buffer
|
||||
, std::size_t bytes_transferred)
|
||||
|
@ -123,9 +123,11 @@ void lsd::on_announce(udp::endpoint const& from, char* buffer
|
|||
|
||||
http_parser p;
|
||||
|
||||
p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred));
|
||||
bool error = false;
|
||||
p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred)
|
||||
, error);
|
||||
|
||||
if (!p.header_finished())
|
||||
if (!p.header_finished() || error)
|
||||
{
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string()
|
||||
|
@ -176,15 +178,22 @@ void lsd::on_announce(udp::endpoint const& from, char* buffer
|
|||
<< ":" << port << " ih: " << ih << std::endl;
|
||||
#endif
|
||||
// we got an announce, pass it on through the callback
|
||||
try { m_callback(tcp::endpoint(from.address(), port), ih); }
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
m_callback(tcp::endpoint(from.address(), port), ih);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
}
|
||||
catch (std::exception&) {}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void lsd::close()
|
||||
{
|
||||
m_socket.close();
|
||||
m_broadcast_timer.cancel();
|
||||
asio::error_code ec;
|
||||
m_broadcast_timer.cancel(ec);
|
||||
m_disabled = true;
|
||||
m_callback.clear();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2007, 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 "libtorrent/magnet_uri.hpp"
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/torrent_handle.hpp"
|
||||
#include "libtorrent/escape_string.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
std::string make_magnet_uri(torrent_handle const& handle)
|
||||
{
|
||||
std::stringstream ret;
|
||||
if (!handle.is_valid()) return ret.str();
|
||||
|
||||
std::string name = handle.name();
|
||||
|
||||
ret << "magnet:?xt=urn:btih:" << base32encode(
|
||||
std::string((char*)handle.info_hash().begin(), 20));
|
||||
if (!name.empty())
|
||||
ret << "&dn=" << escape_string(name.c_str(), name.length());
|
||||
torrent_status st = handle.status();
|
||||
if (!st.current_tracker.empty())
|
||||
{
|
||||
ret << "&tr=" << escape_string(st.current_tracker.c_str()
|
||||
, st.current_tracker.length());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<announce_entry> const& tr = handle.trackers();
|
||||
if (!tr.empty())
|
||||
{
|
||||
ret << "&tr=" << escape_string(tr[0].url.c_str()
|
||||
, tr[0].url.length());
|
||||
}
|
||||
}
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
torrent_handle add_magnet_uri(session& ses, std::string const& uri
|
||||
, fs::path const& save_path
|
||||
, storage_mode_t storage_mode
|
||||
, bool paused
|
||||
, storage_constructor_type sc
|
||||
, void* userdata)
|
||||
{
|
||||
std::string name;
|
||||
std::string tracker;
|
||||
|
||||
boost::optional<std::string> display_name = url_has_argument(uri, "dn");
|
||||
if (display_name) name = unescape_string(display_name->c_str());
|
||||
boost::optional<std::string> tracker_string = url_has_argument(uri, "tr");
|
||||
if (tracker_string) tracker = unescape_string(tracker_string->c_str());
|
||||
|
||||
boost::optional<std::string> btih = url_has_argument(uri, "xt");
|
||||
if (!btih) return torrent_handle();
|
||||
|
||||
if (btih->compare(0, 9, "urn:btih:") != 0) return torrent_handle();
|
||||
|
||||
sha1_hash info_hash(base32decode(btih->substr(9)));
|
||||
|
||||
return ses.add_torrent(tracker.empty() ? 0 : tracker.c_str(), info_hash
|
||||
, name.empty() ? 0 : name.c_str(), save_path, entry()
|
||||
, storage_mode, paused, sc, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,812 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2007, Arvid Norberg, Daniel Wallin
|
||||
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 "libtorrent/pch.hpp"
|
||||
|
||||
#include "libtorrent/storage.hpp"
|
||||
#include "libtorrent/size_type.hpp"
|
||||
#include "libtorrent/file.hpp"
|
||||
#include <set>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
using boost::iostreams::mapped_file;
|
||||
using boost::iostreams::mapped_file_params;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
struct mapped_file_pool
|
||||
{
|
||||
mapped_file_pool(int size = 40): m_size(size) {}
|
||||
|
||||
private:
|
||||
|
||||
enum { view_size = 100 * 1024 * 1024 };
|
||||
int m_size;
|
||||
|
||||
struct file_entry
|
||||
{
|
||||
file_entry() : key(0), references(0) {}
|
||||
bool open(fs::path const& path, std::ios::openmode openmode
|
||||
, size_type start, size_type size, void* key_, size_type file_size = 0)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (file_size > 0)
|
||||
{
|
||||
fs::system_error_type ec;
|
||||
fs::file_status st = fs::status(path, ec);
|
||||
TORRENT_ASSERT(!fs::exists(st));
|
||||
}
|
||||
#endif
|
||||
key = key_;
|
||||
last_use = time_now();
|
||||
params.path = path.string();
|
||||
params.mode = openmode;
|
||||
params.offset = start;
|
||||
params.length = size;
|
||||
params.new_file_size = file_size;
|
||||
file.open(params);
|
||||
return file.is_open();
|
||||
}
|
||||
mapped_file_params params;
|
||||
mapped_file file;
|
||||
void* key;
|
||||
ptime last_use;
|
||||
int references;
|
||||
};
|
||||
|
||||
typedef std::list<file_entry> files_t;
|
||||
files_t m_files;
|
||||
|
||||
public:
|
||||
|
||||
struct file_view
|
||||
{
|
||||
explicit file_view(file_entry* e): m_entry(e) { ++m_entry->references; }
|
||||
file_view(): m_entry(0) {}
|
||||
file_view(file_view const& f): m_entry(f.m_entry)
|
||||
{ if (m_entry) ++m_entry->references; }
|
||||
~file_view()
|
||||
{
|
||||
TORRENT_ASSERT(m_entry == 0 || m_entry->references > 0);
|
||||
if (m_entry) --m_entry->references;
|
||||
}
|
||||
file_view& operator=(file_view const& v)
|
||||
{
|
||||
TORRENT_ASSERT(m_entry == 0 || m_entry->references > 0);
|
||||
if (m_entry) --m_entry->references;
|
||||
m_entry = v.m_entry;
|
||||
if (m_entry) ++m_entry->references;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool valid() const { return m_entry && m_entry->file.const_data(); }
|
||||
|
||||
char* addr() const
|
||||
{
|
||||
TORRENT_ASSERT(m_entry);
|
||||
return m_entry->file.data();
|
||||
}
|
||||
|
||||
char const* const_addr() const
|
||||
{
|
||||
TORRENT_ASSERT(m_entry);
|
||||
return m_entry->file.const_data();
|
||||
}
|
||||
|
||||
size_type offset() const
|
||||
{
|
||||
TORRENT_ASSERT(m_entry);
|
||||
return m_entry->params.offset;
|
||||
}
|
||||
|
||||
size_type size() const
|
||||
{
|
||||
TORRENT_ASSERT(m_entry);
|
||||
return m_entry->params.length;
|
||||
}
|
||||
|
||||
private:
|
||||
file_entry* m_entry;
|
||||
};
|
||||
|
||||
file_view open_file(fs::path const& p, std::ios::openmode mode
|
||||
, size_type offset, size_type length, void* key
|
||||
, size_type file_size)
|
||||
{
|
||||
TORRENT_ASSERT(file_size > 0);
|
||||
files_t::iterator min = m_files.end();
|
||||
for (std::list<file_entry>::iterator i = m_files.begin()
|
||||
, end(m_files.end()); i != end; ++i)
|
||||
{
|
||||
if (i->params.path == p.string()
|
||||
&& i->params.offset <= offset
|
||||
&& i->params.offset + i->params.length >= offset + length)
|
||||
{
|
||||
if (i->key != key) return file_view();
|
||||
if ((mode & std::ios::out) && (i->params.mode & std::ios::out) == 0)
|
||||
{
|
||||
TORRENT_ASSERT(i->references == 0);
|
||||
i->file.close();
|
||||
m_files.erase(i);
|
||||
min = m_files.end();
|
||||
break;
|
||||
}
|
||||
i->last_use = time_now();
|
||||
return file_view(&(*i));
|
||||
}
|
||||
if ((min == m_files.end() || i->last_use < min->last_use)
|
||||
&& i->references == 0)
|
||||
{
|
||||
min = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (int(m_files.size()) >= m_size && min != m_files.end())
|
||||
{
|
||||
TORRENT_ASSERT(min->references == 0);
|
||||
min->file.close();
|
||||
m_files.erase(min);
|
||||
}
|
||||
|
||||
size_type start = (offset / view_size) * view_size;
|
||||
TORRENT_ASSERT(start + view_size >= offset + length);
|
||||
|
||||
fs::system_error_type ec;
|
||||
fs::file_status st = fs::status(p, ec);
|
||||
|
||||
m_files.push_back(file_entry());
|
||||
bool ret = false;
|
||||
if (!exists(st))
|
||||
{
|
||||
ret = m_files.back().open(p, mode | std::ios::out, start, view_size, key, file_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_directory(st)) return file_view();
|
||||
size_type s = fs::file_size(p);
|
||||
#ifdef WIN32
|
||||
// TODO: SetFileSize()
|
||||
if (s < file_size) {}
|
||||
#else
|
||||
if (s < file_size) truncate(p.string().c_str(), file_size);
|
||||
#endif
|
||||
ret = m_files.back().open(p, mode, start, view_size, key);
|
||||
}
|
||||
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
m_files.erase(boost::prior(m_files.end()));
|
||||
return file_view();
|
||||
}
|
||||
return file_view(&m_files.back());
|
||||
}
|
||||
|
||||
void release(void* key)
|
||||
{
|
||||
for (std::list<file_entry>::iterator i = m_files.begin();
|
||||
!m_files.empty() && i != m_files.end();)
|
||||
{
|
||||
if (i->key == key)
|
||||
{
|
||||
TORRENT_ASSERT(i->references == 0);
|
||||
i->file.close();
|
||||
m_files.erase(i++);
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct mapped_storage: storage_interface
|
||||
{
|
||||
mapped_storage(boost::intrusive_ptr<torrent_info const> const& info, fs::path save_path)
|
||||
: m_info(info)
|
||||
, m_save_path(save_path)
|
||||
{}
|
||||
|
||||
bool initialize(bool allocate_files) { return false; }
|
||||
|
||||
int read(char* buf, int slot, int offset, int size)
|
||||
{
|
||||
TORRENT_ASSERT(buf != 0);
|
||||
TORRENT_ASSERT(slot >= 0 && slot < m_info->num_pieces());
|
||||
TORRENT_ASSERT(offset >= 0);
|
||||
TORRENT_ASSERT(offset < m_info->piece_size(slot));
|
||||
TORRENT_ASSERT(size > 0);
|
||||
|
||||
size_type result = -1;
|
||||
try
|
||||
{
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::vector<file_slice> slices
|
||||
= m_info->map_block(slot, offset, size, true);
|
||||
TORRENT_ASSERT(!slices.empty());
|
||||
#endif
|
||||
size_type start = slot * (size_type)m_info->piece_length() + offset;
|
||||
TORRENT_ASSERT(start + size <= m_info->total_size());
|
||||
|
||||
// find the file iterator and file offset
|
||||
size_type file_offset = start;
|
||||
std::vector<file_entry>::const_iterator file_iter;
|
||||
|
||||
for (file_iter = m_info->begin_files(true);;)
|
||||
{
|
||||
if (file_offset < file_iter->size)
|
||||
break;
|
||||
|
||||
file_offset -= file_iter->size;
|
||||
++file_iter;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(file_iter->size > 0);
|
||||
mapped_file_pool::file_view view = m_pool.open_file(
|
||||
m_save_path / file_iter->path, std::ios::in
|
||||
, file_offset + file_iter->file_base, size, this
|
||||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
{
|
||||
set_error((m_save_path / file_iter->path).string(), "failed to open file for reading");
|
||||
return -1;
|
||||
}
|
||||
TORRENT_ASSERT(view.const_addr() != 0);
|
||||
|
||||
int left_to_read = size;
|
||||
int buf_pos = 0;
|
||||
result = left_to_read;
|
||||
#ifndef NDEBUG
|
||||
int counter = 0;
|
||||
#endif
|
||||
while (left_to_read > 0)
|
||||
{
|
||||
int read_bytes = left_to_read;
|
||||
if (file_offset + read_bytes > file_iter->size)
|
||||
read_bytes = static_cast<int>(file_iter->size - file_offset);
|
||||
|
||||
if (read_bytes > 0)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
TORRENT_ASSERT(int(slices.size()) > counter);
|
||||
size_type slice_size = slices[counter].size;
|
||||
TORRENT_ASSERT(slice_size == read_bytes);
|
||||
TORRENT_ASSERT(m_info->file_at(slices[counter].file_index, true).path
|
||||
== file_iter->path);
|
||||
#endif
|
||||
|
||||
TORRENT_ASSERT(file_offset + file_iter->file_base >= view.offset());
|
||||
TORRENT_ASSERT(view.const_addr() != 0);
|
||||
std::memcpy(buf + buf_pos
|
||||
, view.const_addr() + (file_offset + file_iter->file_base - view.offset())
|
||||
, read_bytes);
|
||||
|
||||
left_to_read -= read_bytes;
|
||||
buf_pos += read_bytes;
|
||||
TORRENT_ASSERT(buf_pos >= 0);
|
||||
file_offset += read_bytes;
|
||||
}
|
||||
|
||||
if (left_to_read > 0)
|
||||
{
|
||||
++file_iter;
|
||||
// skip empty files
|
||||
while (file_iter != m_info->end_files(true) && file_iter->size == 0)
|
||||
++file_iter;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// empty files are not returned by map_block, so if
|
||||
// this file was empty, don't increment the slice counter
|
||||
if (read_bytes > 0) ++counter;
|
||||
#endif
|
||||
fs::path path = m_save_path / file_iter->path;
|
||||
|
||||
file_offset = 0;
|
||||
|
||||
view = m_pool.open_file(path, std::ios::in, file_offset + file_iter->file_base
|
||||
, left_to_read, this
|
||||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
{
|
||||
set_error((m_save_path / file_iter->path).string(), "failed to open for reading");
|
||||
return -1;
|
||||
}
|
||||
TORRENT_ASSERT(view.const_addr() != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
set_error("", e.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int write(const char* buf, int slot, int offset, int size)
|
||||
{
|
||||
TORRENT_ASSERT(buf != 0);
|
||||
TORRENT_ASSERT(slot >= 0 && slot < m_info->num_pieces());
|
||||
TORRENT_ASSERT(offset >= 0);
|
||||
TORRENT_ASSERT(offset < m_info->piece_size(slot));
|
||||
TORRENT_ASSERT(size > 0);
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::vector<file_slice> slices
|
||||
= m_info->map_block(slot, offset, size, true);
|
||||
TORRENT_ASSERT(!slices.empty());
|
||||
#endif
|
||||
size_type start = slot * (size_type)m_info->piece_length() + offset;
|
||||
TORRENT_ASSERT(start + size <= m_info->total_size());
|
||||
|
||||
// find the file iterator and file offset
|
||||
size_type file_offset = start;
|
||||
std::vector<file_entry>::const_iterator file_iter;
|
||||
|
||||
for (file_iter = m_info->begin_files(true);;)
|
||||
{
|
||||
if (file_offset < file_iter->size)
|
||||
break;
|
||||
|
||||
file_offset -= file_iter->size;
|
||||
++file_iter;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(file_iter->size > 0);
|
||||
try
|
||||
{
|
||||
|
||||
mapped_file_pool::file_view view = m_pool.open_file(
|
||||
m_save_path / file_iter->path, std::ios::in | std::ios::out
|
||||
, file_offset + file_iter->file_base, size, this
|
||||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
{
|
||||
set_error((m_save_path / file_iter->path).string(), "failed to open file for writing");
|
||||
return -1;
|
||||
}
|
||||
TORRENT_ASSERT(view.addr() != 0);
|
||||
|
||||
int left_to_write = size;
|
||||
int buf_pos = 0;
|
||||
#ifndef NDEBUG
|
||||
int counter = 0;
|
||||
#endif
|
||||
while (left_to_write > 0)
|
||||
{
|
||||
int write_bytes = left_to_write;
|
||||
if (file_offset + write_bytes > file_iter->size)
|
||||
write_bytes = static_cast<int>(file_iter->size - file_offset);
|
||||
|
||||
if (write_bytes > 0)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
TORRENT_ASSERT(int(slices.size()) > counter);
|
||||
size_type slice_size = slices[counter].size;
|
||||
TORRENT_ASSERT(slice_size == write_bytes);
|
||||
TORRENT_ASSERT(m_info->file_at(slices[counter].file_index, true).path
|
||||
== file_iter->path);
|
||||
#endif
|
||||
|
||||
TORRENT_ASSERT(file_offset + file_iter->file_base >= view.offset());
|
||||
TORRENT_ASSERT(view.addr() != 0);
|
||||
std::memcpy(view.addr() + (file_offset + file_iter->file_base - view.offset())
|
||||
, buf + buf_pos
|
||||
, write_bytes);
|
||||
|
||||
left_to_write -= write_bytes;
|
||||
buf_pos += write_bytes;
|
||||
TORRENT_ASSERT(buf_pos >= 0);
|
||||
file_offset += write_bytes;
|
||||
}
|
||||
|
||||
if (left_to_write > 0)
|
||||
{
|
||||
++file_iter;
|
||||
while (file_iter != m_info->end_files(true) && file_iter->size == 0)
|
||||
++file_iter;
|
||||
#ifndef NDEBUG
|
||||
// empty files are not returned by map_block, so if
|
||||
// this file was empty, don't increment the slice counter
|
||||
if (write_bytes > 0) ++counter;
|
||||
#endif
|
||||
fs::path path = m_save_path / file_iter->path;
|
||||
|
||||
file_offset = 0;
|
||||
view = m_pool.open_file(path, std::ios::in | std::ios::out
|
||||
, file_offset + file_iter->file_base, left_to_write, this
|
||||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
{
|
||||
set_error((m_save_path / file_iter->path).string(), "failed to open file for reading");
|
||||
return -1;
|
||||
}
|
||||
TORRENT_ASSERT(view.addr() != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
set_error((m_save_path / file_iter->path).string(), e.what());
|
||||
return -1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool move_storage(fs::path save_path)
|
||||
{
|
||||
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400
|
||||
fs::wpath old_path;
|
||||
fs::wpath new_path;
|
||||
#else
|
||||
fs::path old_path;
|
||||
fs::path new_path;
|
||||
#endif
|
||||
|
||||
save_path = complete(save_path);
|
||||
|
||||
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
|
||||
std::wstring wsave_path(safe_convert(save_path.native_file_string()));
|
||||
if (!exists_win(save_path))
|
||||
CreateDirectory(wsave_path.c_str(), 0);
|
||||
else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
||||
return false;
|
||||
#elif defined(_WIN32) && defined(UNICODE)
|
||||
fs::wpath wp = safe_convert(save_path.string());
|
||||
if (!exists(wp))
|
||||
create_directory(wp);
|
||||
else if (!is_directory(wp))
|
||||
return false;
|
||||
#else
|
||||
if (!exists(save_path))
|
||||
create_directory(save_path);
|
||||
else if (!is_directory(save_path))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
m_pool.release(this);
|
||||
|
||||
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION >= 103400
|
||||
old_path = safe_convert((m_save_path / m_info->name()).string());
|
||||
new_path = safe_convert((save_path / m_info->name()).string());
|
||||
#else
|
||||
old_path = m_save_path / m_info->name();
|
||||
new_path = save_path / m_info->name();
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
|
||||
rename_win(old_path, new_path);
|
||||
rename(old_path, new_path);
|
||||
#else
|
||||
rename(old_path, new_path);
|
||||
#endif
|
||||
m_save_path = save_path;
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool verify_resume_data(entry const& rd, std::string& error)
|
||||
{
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "invalid fastresume file";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes;
|
||||
entry const* file_sizes_ent = rd.find_key("file sizes");
|
||||
if (file_sizes_ent == 0 || file_sizes_ent->type() != entry::list_t)
|
||||
{
|
||||
error = "missing or invalid 'file sizes' entry in resume data";
|
||||
return false;
|
||||
}
|
||||
|
||||
entry::list_type const& l = file_sizes_ent->list();
|
||||
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
if (i->type() != entry::list_t) break;
|
||||
entry::list_type const& pair = i->list();
|
||||
if (pair.size() != 2 || pair.front().type() != entry::int_t
|
||||
|| pair.back().type() != entry::int_t)
|
||||
break;
|
||||
file_sizes.push_back(std::pair<size_type, std::time_t>(
|
||||
pair.front().integer(), pair.back().integer()));
|
||||
}
|
||||
|
||||
if (file_sizes.empty())
|
||||
{
|
||||
error = "the number of files in resume data is 0";
|
||||
return false;
|
||||
}
|
||||
|
||||
entry const* slots_ent = rd.find_key("slots");
|
||||
if (slots_ent == 0 || slots_ent->type() != entry::list_t)
|
||||
{
|
||||
error = "missing or invalid 'slots' entry in resume data";
|
||||
return false;
|
||||
}
|
||||
|
||||
entry::list_type const& slots = slots_ent->list();
|
||||
bool seed = int(slots.size()) == m_info->num_pieces()
|
||||
&& std::find_if(slots.begin(), slots.end()
|
||||
, boost::bind<bool>(std::less<int>()
|
||||
, boost::bind((size_type const& (entry::*)() const)
|
||||
&entry::integer, _1), 0)) == slots.end();
|
||||
|
||||
bool full_allocation_mode = false;
|
||||
entry const* allocation_mode = rd.find_key("allocation");
|
||||
if (allocation_mode && allocation_mode->type() == entry::string_t)
|
||||
full_allocation_mode = allocation_mode->string() == "full";
|
||||
|
||||
if (seed)
|
||||
{
|
||||
if (m_info->num_files(true) != (int)file_sizes.size())
|
||||
{
|
||||
error = "the number of files does not match the torrent (num: "
|
||||
+ boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
|
||||
+ boost::lexical_cast<std::string>(m_info->num_files(true)) + ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_type, std::time_t> >::iterator
|
||||
fs = file_sizes.begin();
|
||||
// the resume data says we have the entire torrent
|
||||
// make sure the file sizes are the right ones
|
||||
for (torrent_info::file_iterator i = m_info->begin_files(true)
|
||||
, end(m_info->end_files(true)); i != end; ++i, ++fs)
|
||||
{
|
||||
if (i->size != fs->first)
|
||||
{
|
||||
error = "file size for '" + i->path.native_file_string()
|
||||
+ "' was expected to be "
|
||||
+ boost::lexical_cast<std::string>(i->size) + " bytes";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return match_filesizes(*m_info, m_save_path, file_sizes
|
||||
, !full_allocation_mode, &error);
|
||||
}
|
||||
|
||||
bool write_resume_data(entry& rd) const
|
||||
{
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
set_error("", "invalid fastresume file");
|
||||
return true;
|
||||
}
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes
|
||||
= get_filesizes(*m_info, m_save_path);
|
||||
|
||||
entry::list_type& fl = rd["file sizes"].list();
|
||||
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
|
||||
= file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
|
||||
{
|
||||
entry::list_type p;
|
||||
p.push_back(entry(i->first));
|
||||
p.push_back(entry(i->second));
|
||||
fl.push_back(entry(p));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool move_slot(int src_slot, int dst_slot)
|
||||
{
|
||||
// TODO: this can be optimized by mapping both slots and do a straight memcpy
|
||||
int piece_size = m_info->piece_size(dst_slot);
|
||||
m_scratch_buffer.resize(piece_size);
|
||||
size_type ret1 = read(&m_scratch_buffer[0], src_slot, 0, piece_size);
|
||||
size_type ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
|
||||
return ret1 != piece_size || ret2 != piece_size;
|
||||
}
|
||||
|
||||
bool swap_slots(int slot1, int slot2)
|
||||
{
|
||||
// TODO: this can be optimized by mapping both slots and do a straight memcpy
|
||||
// the size of the target slot is the size of the piece
|
||||
int piece_size = m_info->piece_length();
|
||||
int piece1_size = m_info->piece_size(slot2);
|
||||
int piece2_size = m_info->piece_size(slot1);
|
||||
m_scratch_buffer.resize(piece_size * 2);
|
||||
size_type ret1 = read(&m_scratch_buffer[0], slot1, 0, piece1_size);
|
||||
size_type ret2 = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
|
||||
size_type ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
size_type ret4 = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
|
||||
return ret1 != piece1_size || ret2 != piece2_size
|
||||
|| ret3 != piece1_size || ret4 != piece2_size;
|
||||
}
|
||||
|
||||
bool swap_slots3(int slot1, int slot2, int slot3)
|
||||
{
|
||||
// TODO: this can be optimized by mapping both slots and do a straight memcpy
|
||||
// the size of the target slot is the size of the piece
|
||||
int piece_size = m_info->piece_length();
|
||||
int piece1_size = m_info->piece_size(slot2);
|
||||
int piece2_size = m_info->piece_size(slot3);
|
||||
int piece3_size = m_info->piece_size(slot1);
|
||||
m_scratch_buffer.resize(piece_size * 2);
|
||||
size_type ret1 = read(&m_scratch_buffer[0], slot1, 0, piece1_size);
|
||||
size_type ret2 = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
|
||||
size_type ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
size_type ret4 = read(&m_scratch_buffer[0], slot3, 0, piece3_size);
|
||||
size_type ret5 = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
|
||||
size_type ret6 = write(&m_scratch_buffer[0], slot1, 0, piece3_size);
|
||||
return ret1 != piece1_size || ret2 != piece2_size
|
||||
|| ret3 != piece1_size || ret4 != piece3_size
|
||||
|| ret5 != piece2_size || ret6 != piece3_size;
|
||||
}
|
||||
|
||||
sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
hasher partial;
|
||||
hasher whole;
|
||||
int slot_size1 = piece_size;
|
||||
m_scratch_buffer.resize(slot_size1);
|
||||
read(&m_scratch_buffer[0], slot, 0, slot_size1);
|
||||
if (ph.offset > 0)
|
||||
partial.update(&m_scratch_buffer[0], ph.offset);
|
||||
whole.update(&m_scratch_buffer[0], slot_size1);
|
||||
hasher partial_copy = ph.h;
|
||||
TORRENT_ASSERT(ph.offset == 0 || partial_copy.final() == partial.final());
|
||||
#endif
|
||||
int slot_size = piece_size - ph.offset;
|
||||
if (slot_size > 0)
|
||||
{
|
||||
m_scratch_buffer.resize(slot_size);
|
||||
read(&m_scratch_buffer[0], slot, ph.offset, slot_size);
|
||||
ph.h.update(&m_scratch_buffer[0], slot_size);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
sha1_hash ret = ph.h.final();
|
||||
TORRENT_ASSERT(ret == whole.final());
|
||||
return ret;
|
||||
#else
|
||||
return ph.h.final();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool release_files()
|
||||
{
|
||||
m_pool.release(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool delete_files()
|
||||
{
|
||||
// make sure we don't have the files open
|
||||
m_pool.release(this);
|
||||
buffer().swap(m_scratch_buffer);
|
||||
|
||||
int result = 0;
|
||||
std::string error;
|
||||
std::string error_file;
|
||||
|
||||
// delete the files from disk
|
||||
std::set<std::string> directories;
|
||||
typedef std::set<std::string>::iterator iter_t;
|
||||
for (torrent_info::file_iterator i = m_info->begin_files(true)
|
||||
, end(m_info->end_files(true)); i != end; ++i)
|
||||
{
|
||||
std::string p = (m_save_path / i->path).string();
|
||||
fs::path bp = i->path.branch_path();
|
||||
std::pair<iter_t, bool> ret;
|
||||
ret.second = true;
|
||||
while (ret.second && !bp.empty())
|
||||
{
|
||||
std::pair<iter_t, bool> ret = directories.insert((m_save_path / bp).string());
|
||||
bp = bp.branch_path();
|
||||
}
|
||||
if (std::remove(p.c_str()) != 0 && errno != ENOENT)
|
||||
{
|
||||
error = std::strerror(errno);
|
||||
error_file = p;
|
||||
result = errno;
|
||||
}
|
||||
}
|
||||
|
||||
// remove the directories. Reverse order to delete
|
||||
// subdirectories first
|
||||
|
||||
for (std::set<std::string>::reverse_iterator i = directories.rbegin()
|
||||
, end(directories.rend()); i != end; ++i)
|
||||
{
|
||||
if (std::remove(i->c_str()) != 0 && errno != ENOENT)
|
||||
{
|
||||
error = std::strerror(errno);
|
||||
error_file = *i;
|
||||
result = errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error.empty()) set_error(error_file, error);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
boost::intrusive_ptr<torrent_info const> m_info;
|
||||
fs::path m_save_path;
|
||||
|
||||
// temporary storage for moving pieces
|
||||
buffer m_scratch_buffer;
|
||||
|
||||
static mapped_file_pool m_pool;
|
||||
};
|
||||
|
||||
storage_interface* mapped_storage_constructor(boost::intrusive_ptr<torrent_info const> ti
|
||||
, fs::path const& path, file_pool& fp)
|
||||
{
|
||||
return new mapped_storage(ti, path);
|
||||
}
|
||||
|
||||
mapped_file_pool mapped_storage::m_pool;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2007, 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.
|
||||
|
||||
*/
|
||||
|
||||
#if defined __linux__ && defined __GNUC__
|
||||
#include <execinfo.h>
|
||||
|
||||
// Prototypes for __malloc_hook, __free_hook
|
||||
#include <malloc.h>
|
||||
#include <boost/array.hpp>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include "libtorrent/time.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
||||
using boost::multi_index_container;
|
||||
using namespace boost::multi_index;
|
||||
using libtorrent::time_now;
|
||||
|
||||
struct memdebug
|
||||
{
|
||||
memdebug()
|
||||
{
|
||||
malloc_log.open("memory.log");
|
||||
malloc_index_log.open("memory_index.log");
|
||||
|
||||
assert(old_malloc_hook == 0);
|
||||
assert(old_free_hook == 0);
|
||||
old_malloc_hook = __malloc_hook;
|
||||
old_free_hook = __free_hook;
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
}
|
||||
|
||||
static void my_free_hook(void *ptr, const void *caller);
|
||||
static void* my_malloc_hook(size_t size, const void *caller);
|
||||
|
||||
static boost::mutex mutex;
|
||||
static std::ofstream malloc_log;
|
||||
static std::ofstream malloc_index_log;
|
||||
|
||||
// the original library functions
|
||||
static void* (*old_malloc_hook)(size_t, const void *);
|
||||
static void (*old_free_hook)(void*, const void *);
|
||||
|
||||
struct allocation_point_t
|
||||
{
|
||||
allocation_point_t()
|
||||
: allocated(0)
|
||||
, peak_allocated(0)
|
||||
, spacetime(0)
|
||||
, last_update(time_now()) {}
|
||||
|
||||
int index;
|
||||
// total number of bytes allocated from this point
|
||||
int allocated;
|
||||
// the maximum total number of bytes allocated
|
||||
// from this point
|
||||
int peak_allocated;
|
||||
// total number of bytes allocated times the number of
|
||||
// milliseconds they were allocated from this point
|
||||
boost::int64_t spacetime;
|
||||
// the last malloc or free operation on
|
||||
// this allocation point. The spacetime
|
||||
// should be updated from this point to
|
||||
// the current operation
|
||||
libtorrent::ptime last_update;
|
||||
};
|
||||
|
||||
typedef boost::array<void*, 15> stacktrace_t;
|
||||
typedef std::map<stacktrace_t, allocation_point_t> allocation_map_t;
|
||||
static allocation_map_t allocation_points;
|
||||
static std::map<void*, std::pair<allocation_map_t::iterator, int> > allocations;
|
||||
static int allocation_point_index;
|
||||
static libtorrent::ptime start_time;
|
||||
};
|
||||
|
||||
boost::mutex memdebug::mutex;
|
||||
int memdebug::allocation_point_index = 0;
|
||||
std::ofstream memdebug::malloc_log;
|
||||
std::ofstream memdebug::malloc_index_log;
|
||||
void* (*memdebug::old_malloc_hook)(size_t, const void *) = 0;
|
||||
void (*memdebug::old_free_hook)(void*, const void *) = 0;
|
||||
memdebug::allocation_map_t memdebug::allocation_points;
|
||||
std::map<void*, std::pair<memdebug::allocation_map_t::iterator, int> > memdebug::allocations;
|
||||
libtorrent::ptime memdebug::start_time = time_now();
|
||||
|
||||
void* memdebug::my_malloc_hook(size_t size, const void *caller)
|
||||
{
|
||||
boost::mutex::scoped_lock l(mutex);
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
/* Call recursively */
|
||||
void* result = malloc(size);
|
||||
/* Save underlying hooks */
|
||||
old_malloc_hook = __malloc_hook;
|
||||
old_free_hook = __free_hook;
|
||||
|
||||
stacktrace_t stack;
|
||||
int stacksize = backtrace(&stack[0], stack.size());
|
||||
libtorrent::ptime now = time_now();
|
||||
|
||||
allocation_map_t::iterator i = allocation_points.lower_bound(stack);
|
||||
if (i == allocation_points.end() || i->first != stack)
|
||||
{
|
||||
i = allocation_points.insert(i, std::make_pair(stack, allocation_point_t()));
|
||||
i->second.index = allocation_point_index++;
|
||||
i->second.allocated = size;
|
||||
|
||||
malloc_index_log << i->second.index << "#";
|
||||
char** symbols = backtrace_symbols(&stack[0], stacksize);
|
||||
for (int j = 2; j < stacksize; ++j)
|
||||
malloc_index_log << demangle(symbols[j]) << "#";
|
||||
malloc_index_log << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
allocation_point_t& ap = i->second;
|
||||
ap.spacetime += libtorrent::total_milliseconds(now - ap.last_update) * ap.allocated;
|
||||
ap.allocated += size;
|
||||
if (ap.allocated > ap.peak_allocated) ap.peak_allocated = ap.allocated;
|
||||
ap.last_update = now;
|
||||
}
|
||||
allocation_point_t& ap = i->second;
|
||||
|
||||
allocations[result] = std::make_pair(i, size);
|
||||
malloc_log << "#" << ap.index << " "
|
||||
<< libtorrent::total_milliseconds(time_now() - start_time) << " A "
|
||||
<< result << " " << size << " " << ap.allocated << " " << ap.spacetime
|
||||
<< " " << ap.peak_allocated << std::endl;
|
||||
|
||||
/* Restore our own hooks */
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
return result;
|
||||
}
|
||||
|
||||
void memdebug::my_free_hook(void *ptr, const void *caller)
|
||||
{
|
||||
boost::mutex::scoped_lock l(mutex);
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
/* Call recursively */
|
||||
free(ptr);
|
||||
/* Save underlying hooks */
|
||||
old_malloc_hook = __malloc_hook;
|
||||
old_free_hook = __free_hook;
|
||||
|
||||
std::map<void*, std::pair<allocation_map_t::iterator, int> >::iterator i
|
||||
= allocations.find(ptr);
|
||||
|
||||
if (i != allocations.end())
|
||||
{
|
||||
allocation_point_t& ap = i->second.first->second;
|
||||
int size = i->second.second;
|
||||
ap.allocated -= size;
|
||||
malloc_log << "#" << ap.index << " "
|
||||
<< libtorrent::total_milliseconds(time_now() - start_time) << " F "
|
||||
<< ptr << " " << size << " " << ap.allocated << " " << ap.spacetime
|
||||
<< " " << ap.peak_allocated << std::endl;
|
||||
|
||||
allocations.erase(i);
|
||||
}
|
||||
|
||||
/* Restore our own hooks */
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
}
|
||||
|
||||
static int ref_count = 0;
|
||||
|
||||
void start_malloc_debug()
|
||||
{
|
||||
boost::mutex::scoped_lock l(memdebug::mutex);
|
||||
static memdebug mi;
|
||||
++ref_count;
|
||||
}
|
||||
|
||||
void stop_malloc_debug()
|
||||
{
|
||||
boost::mutex::scoped_lock l(memdebug::mutex);
|
||||
if (--ref_count == 0)
|
||||
{
|
||||
__malloc_hook = memdebug::old_malloc_hook;
|
||||
__free_hook = memdebug::old_free_hook;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -185,7 +185,16 @@ namespace libtorrent { namespace
|
|||
}
|
||||
|
||||
entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
|
||||
m_torrent.set_metadata(metadata);
|
||||
std::string error;
|
||||
if (!m_torrent.set_metadata(metadata, error))
|
||||
{
|
||||
// this means the metadata is correct, since we
|
||||
// verified it against the info-hash, but we
|
||||
// failed to parse it. Pause the torrent
|
||||
// TODO: Post an alert!
|
||||
m_torrent.pause();
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear the storage for the bitfield
|
||||
std::vector<bool>().swap(m_have_metadata);
|
||||
|
@ -272,17 +281,14 @@ namespace libtorrent { namespace
|
|||
// called when the extension handshake from the other end is received
|
||||
virtual bool on_extension_handshake(entry const& h)
|
||||
{
|
||||
entry const& messages = h["m"];
|
||||
if (entry const* index = messages.find_key("LT_metadata"))
|
||||
{
|
||||
m_message_index = int(index->integer());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_message_index = 0;
|
||||
return false;
|
||||
}
|
||||
m_message_index = 0;
|
||||
entry const* messages = h.find_key("m");
|
||||
if (!messages || messages->type() != entry::dictionary_t) return false;
|
||||
|
||||
entry const* index = messages->find_key("LT_metadata");
|
||||
if (!index || index->type() != entry::int_t) return false;
|
||||
m_message_index = int(index->integer());
|
||||
return true;
|
||||
}
|
||||
|
||||
void write_metadata_request(std::pair<int, int> req)
|
||||
|
@ -367,7 +373,10 @@ namespace libtorrent { namespace
|
|||
if (m_message_index == 0) return false;
|
||||
|
||||
if (length > 500 * 1024)
|
||||
throw protocol_error("LT_metadata message larger than 500 kB");
|
||||
{
|
||||
m_pc.disconnect("LT_metadata message larger than 500 kB");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (body.left() < 1) return true;
|
||||
int type = detail::read_uint8(body.begin);
|
||||
|
@ -383,7 +392,8 @@ namespace libtorrent { namespace
|
|||
if (length != 3)
|
||||
{
|
||||
// invalid metadata request
|
||||
throw protocol_error("invalid metadata request");
|
||||
m_pc.disconnect("invalid metadata request");
|
||||
return true;
|
||||
}
|
||||
|
||||
write_metadata(std::make_pair(start, size));
|
||||
|
@ -398,13 +408,25 @@ namespace libtorrent { namespace
|
|||
int data_size = length - 9;
|
||||
|
||||
if (total_size > 500 * 1024)
|
||||
throw protocol_error("metadata size larger than 500 kB");
|
||||
{
|
||||
m_pc.disconnect("metadata size larger than 500 kB");
|
||||
return true;
|
||||
}
|
||||
if (total_size <= 0)
|
||||
throw protocol_error("invalid metadata size");
|
||||
{
|
||||
m_pc.disconnect("invalid metadata size");
|
||||
return true;
|
||||
}
|
||||
if (offset > total_size || offset < 0)
|
||||
throw protocol_error("invalid metadata offset");
|
||||
{
|
||||
m_pc.disconnect("invalid metadata offset");
|
||||
return true;
|
||||
}
|
||||
if (offset + data_size > total_size)
|
||||
throw protocol_error("invalid metadata message");
|
||||
{
|
||||
m_pc.disconnect("invalid metadata message");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_tp.metadata_progress(total_size
|
||||
, body.left() - m_metadata_progress);
|
||||
|
@ -425,8 +447,11 @@ namespace libtorrent { namespace
|
|||
m_waiting_metadata_request = false;
|
||||
break;
|
||||
default:
|
||||
throw protocol_error("unknown metadata extension message: "
|
||||
+ boost::lexical_cast<std::string>(type));
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "unknown metadata extension message: " << type;
|
||||
m_pc.disconnect(msg.str().c_str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -43,15 +43,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
using boost::bind;
|
||||
using namespace libtorrent;
|
||||
|
||||
enum { num_mappings = 2 };
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
// defined in upnp.cpp
|
||||
bool is_local(address const& a);
|
||||
address guess_local_address(asio::io_service&);
|
||||
}
|
||||
|
||||
natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb)
|
||||
: m_callback(cb)
|
||||
, m_currently_mapping(-1)
|
||||
|
@ -59,99 +50,180 @@ natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callbac
|
|||
, m_socket(ios)
|
||||
, m_send_timer(ios)
|
||||
, m_refresh_timer(ios)
|
||||
, m_next_refresh(-1)
|
||||
, m_disabled(false)
|
||||
{
|
||||
m_mappings[0].protocol = 2; // tcp
|
||||
m_mappings[1].protocol = 1; // udp
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log.open("natpmp.log", std::ios::in | std::ios::out | std::ios::trunc);
|
||||
#endif
|
||||
rebind(listen_interface);
|
||||
}
|
||||
|
||||
void natpmp::rebind(address const& listen_interface) try
|
||||
void natpmp::rebind(address const& listen_interface)
|
||||
{
|
||||
address local = address_v4::any();
|
||||
if (listen_interface != address_v4::any())
|
||||
{
|
||||
local = listen_interface;
|
||||
}
|
||||
else
|
||||
{
|
||||
local = guess_local_address(m_socket.io_service());
|
||||
|
||||
if (local == address_v4::any())
|
||||
{
|
||||
throw std::runtime_error("local host is probably not on a NATed "
|
||||
"network. disabling NAT-PMP");
|
||||
}
|
||||
}
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
asio::error_code ec;
|
||||
address gateway = get_default_gateway(m_socket.get_io_service(), listen_interface, ec);
|
||||
if (ec)
|
||||
{
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string()
|
||||
<< " local ip: " << local.to_string() << std::endl;
|
||||
m_log << time_now_string() << " failed to find default router: "
|
||||
<< ec.message() << std::endl;
|
||||
#endif
|
||||
|
||||
if (!is_local(local))
|
||||
{
|
||||
// the local address seems to be an external
|
||||
// internet address. Assume it is not behind a NAT
|
||||
throw std::runtime_error("local IP is not on a local network");
|
||||
disable("failed to find default router");
|
||||
return;
|
||||
}
|
||||
|
||||
m_disabled = false;
|
||||
|
||||
asio::error_code ec;
|
||||
udp::endpoint nat_endpoint(router_for_interface(local, ec), 5351);
|
||||
if (ec)
|
||||
throw std::runtime_error("cannot retrieve router address");
|
||||
|
||||
udp::endpoint nat_endpoint(gateway, 5351);
|
||||
if (nat_endpoint == m_nat_endpoint) return;
|
||||
m_nat_endpoint = nat_endpoint;
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl;
|
||||
m_log << time_now_string() << " found router at: "
|
||||
<< m_nat_endpoint.address() << std::endl;
|
||||
#endif
|
||||
|
||||
m_socket.open(udp::v4());
|
||||
m_socket.bind(udp::endpoint(address_v4::any(), 0));
|
||||
|
||||
for (int i = 0; i < num_mappings; ++i)
|
||||
m_socket.open(udp::v4(), ec);
|
||||
if (ec)
|
||||
{
|
||||
if (m_mappings[i].local_port == 0)
|
||||
disable(ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
m_socket.bind(udp::endpoint(address_v4::any(), 0), ec);
|
||||
if (ec)
|
||||
{
|
||||
disable(ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::vector<mapping_t>::iterator i = m_mappings.begin()
|
||||
, end(m_mappings.end()); i != end; ++i)
|
||||
{
|
||||
if (i->protocol != none
|
||||
|| i->action != mapping_t::action_none)
|
||||
continue;
|
||||
refresh_mapping(i);
|
||||
i->action = mapping_t::action_add;
|
||||
update_mapping(i - m_mappings.begin());
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
||||
void natpmp::disable(char const* message)
|
||||
{
|
||||
m_disabled = true;
|
||||
std::stringstream msg;
|
||||
msg << "NAT-PMP disabled: " << e.what();
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << msg.str() << std::endl;
|
||||
#endif
|
||||
m_callback(0, 0, msg.str());
|
||||
};
|
||||
|
||||
void natpmp::set_mappings(int tcp, int udp)
|
||||
for (std::vector<mapping_t>::iterator i = m_mappings.begin()
|
||||
, end(m_mappings.end()); i != end; ++i)
|
||||
{
|
||||
if (i->protocol == none) continue;
|
||||
i->protocol = none;
|
||||
m_callback(i - m_mappings.begin(), 0, message);
|
||||
}
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string() << " NAT-PMP disabled: " << message << std::endl;
|
||||
#endif
|
||||
close();
|
||||
}
|
||||
void natpmp::delete_mapping(int index)
|
||||
{
|
||||
if (m_disabled) return;
|
||||
update_mapping(0, tcp);
|
||||
update_mapping(1, udp);
|
||||
TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0);
|
||||
if (index >= int(m_mappings.size()) || index < 0) return;
|
||||
mapping_t& m = m_mappings[index];
|
||||
|
||||
if (m.protocol == none) return;
|
||||
|
||||
m.action = mapping_t::action_delete;
|
||||
update_mapping(index);
|
||||
}
|
||||
|
||||
void natpmp::update_mapping(int i, int port)
|
||||
int natpmp::add_mapping(protocol_type p, int external_port, int local_port)
|
||||
{
|
||||
natpmp::mapping& m = m_mappings[i];
|
||||
if (port <= 0) return;
|
||||
if (m.local_port != port)
|
||||
m.need_update = true;
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
m.local_port = port;
|
||||
// prefer the same external port as the local port
|
||||
if (m.external_port == 0) m.external_port = port;
|
||||
if (m_disabled) return -1;
|
||||
|
||||
std::vector<mapping_t>::iterator i = std::find_if(m_mappings.begin()
|
||||
, m_mappings.end(), boost::bind(&mapping_t::protocol, _1) == int(none));
|
||||
if (i == m_mappings.end())
|
||||
{
|
||||
m_mappings.push_back(mapping_t());
|
||||
i = m_mappings.end() - 1;
|
||||
}
|
||||
i->protocol = p;
|
||||
i->external_port = external_port;
|
||||
i->local_port = local_port;
|
||||
i->action = mapping_t::action_add;
|
||||
|
||||
int mapping_index = i - m_mappings.begin();
|
||||
|
||||
update_mapping(mapping_index);
|
||||
return mapping_index;
|
||||
}
|
||||
|
||||
void natpmp::try_next_mapping(int i)
|
||||
{
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string() << " try_next_mapping [ " << i << " ]" << std::endl;
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
ptime now = time_now();
|
||||
for (std::vector<mapping_t>::iterator i = m_mappings.begin()
|
||||
, end(m_mappings.end()); i != end; ++i)
|
||||
{
|
||||
m_log << " " << (i - m_mappings.begin()) << " [ "
|
||||
"proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp")
|
||||
<< " port: " << i->external_port
|
||||
<< " local-port: " << i->local_port
|
||||
<< " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete")
|
||||
<< " ttl: " << total_seconds(i->expires - now)
|
||||
<< " ]" << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i < int(m_mappings.size()) - 1)
|
||||
{
|
||||
update_mapping(i + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<mapping_t>::iterator m = std::find_if(
|
||||
m_mappings.begin(), m_mappings.end()
|
||||
, boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none));
|
||||
|
||||
if (m == m_mappings.end())
|
||||
{
|
||||
if (m_abort)
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_send_timer.cancel(ec);
|
||||
m_socket.close(ec);
|
||||
}
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << " done" << (m_abort?" shutting down":"") << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << " updating " << (m - m_mappings.begin()) << std::endl;
|
||||
#endif
|
||||
|
||||
update_mapping(m - m_mappings.begin());
|
||||
}
|
||||
|
||||
void natpmp::update_mapping(int i)
|
||||
{
|
||||
natpmp::mapping_t& m = m_mappings[i];
|
||||
if (m.action == mapping_t::action_none
|
||||
|| m.protocol == none)
|
||||
{
|
||||
try_next_mapping(i);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_currently_mapping == -1)
|
||||
{
|
||||
|
@ -164,14 +236,15 @@ void natpmp::update_mapping(int i, int port)
|
|||
}
|
||||
}
|
||||
|
||||
void natpmp::send_map_request(int i) try
|
||||
void natpmp::send_map_request(int i)
|
||||
{
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
TORRENT_ASSERT(m_currently_mapping == -1
|
||||
|| m_currently_mapping == i);
|
||||
m_currently_mapping = i;
|
||||
mapping& m = m_mappings[i];
|
||||
mapping_t& m = m_mappings[i];
|
||||
TORRENT_ASSERT(m.action != mapping_t::action_none);
|
||||
char buf[12];
|
||||
char* out = buf;
|
||||
write_uint8(0, out); // NAT-PMP version
|
||||
|
@ -179,36 +252,39 @@ void natpmp::send_map_request(int i) try
|
|||
write_uint16(0, out); // reserved
|
||||
write_uint16(m.local_port, out); // private port
|
||||
write_uint16(m.external_port, out); // requested public port
|
||||
int ttl = m.external_port == 0 ? 0 : 3600;
|
||||
int ttl = m.action == mapping_t::action_add ? 3600 : 0;
|
||||
write_uint32(ttl, out); // port mapping lifetime
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string()
|
||||
<< " ==> port map request: " << (m.protocol == 1 ? "udp" : "tcp")
|
||||
<< " ==> port map ["
|
||||
<< " action: " << (m.action == mapping_t::action_add ? "add" : "delete") << " "
|
||||
<< " proto: " << (m.protocol == udp ? "udp" : "tcp")
|
||||
<< " local: " << m.local_port << " external: " << m.external_port
|
||||
<< " ttl: " << ttl << std::endl;
|
||||
<< " ttl: " << ttl << " ]" << std::endl;
|
||||
#endif
|
||||
|
||||
m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint);
|
||||
asio::error_code ec;
|
||||
m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint, 0, ec);
|
||||
// linear back-off instead of exponential
|
||||
++m_retry_count;
|
||||
m_send_timer.expires_from_now(milliseconds(250 * m_retry_count));
|
||||
m_send_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
|
||||
m_send_timer.async_wait(bind(&natpmp::resend_request, self(), i, _1));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::string err = e.what();
|
||||
};
|
||||
|
||||
void natpmp::resend_request(int i, asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
if (m_currently_mapping != i) return;
|
||||
if (m_retry_count >= 9)
|
||||
{
|
||||
m_mappings[i].need_update = false;
|
||||
m_currently_mapping = -1;
|
||||
m_mappings[i].action = mapping_t::action_none;
|
||||
// try again in two hours
|
||||
m_mappings[i].expires = time_now() + hours(2);
|
||||
try_next_mapping(i);
|
||||
return;
|
||||
}
|
||||
send_map_request(i);
|
||||
|
@ -220,179 +296,195 @@ void natpmp::on_reply(asio::error_code const& e
|
|||
using namespace libtorrent::detail;
|
||||
if (e) return;
|
||||
|
||||
try
|
||||
if (m_remote != m_nat_endpoint)
|
||||
{
|
||||
|
||||
if (m_remote != m_nat_endpoint)
|
||||
{
|
||||
m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
|
||||
, m_remote, bind(&natpmp::on_reply, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
m_send_timer.cancel();
|
||||
|
||||
TORRENT_ASSERT(m_currently_mapping >= 0);
|
||||
int i = m_currently_mapping;
|
||||
mapping& m = m_mappings[i];
|
||||
|
||||
char* in = m_response_buffer;
|
||||
int version = read_uint8(in);
|
||||
int cmd = read_uint8(in);
|
||||
int result = read_uint16(in);
|
||||
int time = read_uint32(in);
|
||||
int private_port = read_uint16(in);
|
||||
int public_port = read_uint16(in);
|
||||
int lifetime = read_uint32(in);
|
||||
|
||||
(void)time; // to remove warning
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string()
|
||||
<< " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp")
|
||||
<< " local: " << private_port << " external: " << public_port
|
||||
<< " ttl: " << lifetime << std::endl;
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (version != 0)
|
||||
{
|
||||
m_log << "*** unexpected version: " << version << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (private_port != m.local_port)
|
||||
{
|
||||
m_log << "*** unexpected local port: " << private_port << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (cmd != 128 + m.protocol)
|
||||
{
|
||||
m_log << "*** unexpected protocol: " << (cmd - 128) << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (public_port == 0 || lifetime == 0)
|
||||
{
|
||||
// this means the mapping was
|
||||
// successfully closed
|
||||
m.local_port = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m.expires = time_now() + seconds(int(lifetime * 0.7f));
|
||||
m.external_port = public_port;
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "*** ERROR: " << result << std::endl;
|
||||
#endif
|
||||
std::stringstream errmsg;
|
||||
errmsg << "NAT router reports error (" << result << ") ";
|
||||
switch (result)
|
||||
{
|
||||
case 1: errmsg << "Unsupported protocol version"; break;
|
||||
case 2: errmsg << "Not authorized to create port map (enable NAT-PMP on your router)"; break;
|
||||
case 3: errmsg << "Network failure"; break;
|
||||
case 4: errmsg << "Out of resources"; break;
|
||||
case 5: errmsg << "Unsupported opcode"; break;
|
||||
}
|
||||
throw std::runtime_error(errmsg.str());
|
||||
}
|
||||
|
||||
// don't report when we remove mappings
|
||||
if (m.local_port != 0)
|
||||
{
|
||||
int tcp_port = 0;
|
||||
int udp_port = 0;
|
||||
if (m.protocol == 1) udp_port = m.external_port;
|
||||
else tcp_port = public_port;
|
||||
m_callback(tcp_port, udp_port, "");
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// try again in two hours
|
||||
m_mappings[m_currently_mapping].expires = time_now() + hours(2);
|
||||
m_callback(0, 0, e.what());
|
||||
m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
|
||||
, m_remote, bind(&natpmp::on_reply, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
asio::error_code ec;
|
||||
m_send_timer.cancel(ec);
|
||||
|
||||
TORRENT_ASSERT(m_currently_mapping >= 0);
|
||||
int i = m_currently_mapping;
|
||||
mapping_t& m = m_mappings[i];
|
||||
|
||||
char* in = m_response_buffer;
|
||||
int version = read_uint8(in);
|
||||
int cmd = read_uint8(in);
|
||||
int result = read_uint16(in);
|
||||
int time = read_uint32(in);
|
||||
int private_port = read_uint16(in);
|
||||
int public_port = read_uint16(in);
|
||||
int lifetime = read_uint32(in);
|
||||
|
||||
(void)time; // to remove warning
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string()
|
||||
<< " <== port map ["
|
||||
<< " protocol: " << (cmd - 128 == 1 ? "udp" : "tcp")
|
||||
<< " local: " << private_port << " external: " << public_port
|
||||
<< " ttl: " << lifetime << " ]" << std::endl;
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (version != 0)
|
||||
{
|
||||
m_log << "*** unexpected version: " << version << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (private_port != m.local_port)
|
||||
{
|
||||
m_log << "*** unexpected local port: " << private_port << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (cmd != 128 + m.protocol)
|
||||
{
|
||||
m_log << "*** unexpected protocol: " << (cmd - 128) << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (public_port == 0 || lifetime == 0)
|
||||
{
|
||||
// this means the mapping was
|
||||
// successfully closed
|
||||
m.protocol = none;
|
||||
}
|
||||
else
|
||||
{
|
||||
m.expires = time_now() + seconds(int(lifetime * 0.7f));
|
||||
m.external_port = public_port;
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "*** ERROR: " << result << std::endl;
|
||||
#endif
|
||||
std::stringstream errmsg;
|
||||
errmsg << "NAT router reports error (" << result << ") ";
|
||||
switch (result)
|
||||
{
|
||||
case 1: errmsg << "Unsupported protocol version"; break;
|
||||
case 2: errmsg << "Not authorized to create port map (enable NAT-PMP on your router)"; break;
|
||||
case 3: errmsg << "Network failure"; break;
|
||||
case 4: errmsg << "Out of resources"; break;
|
||||
case 5: errmsg << "Unsupported opcode"; break;
|
||||
}
|
||||
m.expires = time_now() + hours(2);
|
||||
m_callback(i, 0, errmsg.str());
|
||||
}
|
||||
else if (m.action == mapping_t::action_add)
|
||||
{
|
||||
m_callback(i, m.external_port, "");
|
||||
}
|
||||
|
||||
m_currently_mapping = -1;
|
||||
m_mappings[i].need_update = false;
|
||||
m_send_timer.cancel();
|
||||
m.action = mapping_t::action_none;
|
||||
m_send_timer.cancel(ec);
|
||||
update_expiration_timer();
|
||||
try_next_mapping(i);
|
||||
}
|
||||
|
||||
void natpmp::update_expiration_timer()
|
||||
{
|
||||
if (m_abort) return;
|
||||
|
||||
ptime now = time_now();
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string() << " update_expiration_timer " << std::endl;
|
||||
for (std::vector<mapping_t>::iterator i = m_mappings.begin()
|
||||
, end(m_mappings.end()); i != end; ++i)
|
||||
{
|
||||
m_log << " " << (i - m_mappings.begin()) << " [ "
|
||||
"proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp")
|
||||
<< " port: " << i->external_port
|
||||
<< " local-port: " << i->local_port
|
||||
<< " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete")
|
||||
<< " ttl: " << total_seconds(i->expires - now)
|
||||
<< " ]" << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
ptime min_expire = now + seconds(3600);
|
||||
int min_index = -1;
|
||||
for (int i = 0; i < num_mappings; ++i)
|
||||
if (m_mappings[i].expires < min_expire
|
||||
&& m_mappings[i].local_port != 0)
|
||||
for (std::vector<mapping_t>::iterator i = m_mappings.begin()
|
||||
, end(m_mappings.end()); i != end; ++i)
|
||||
{
|
||||
if (i->protocol == none
|
||||
|| i->action != mapping_t::action_none) continue;
|
||||
if (i->expires < min_expire)
|
||||
{
|
||||
min_expire = m_mappings[i].expires;
|
||||
min_index = i;
|
||||
min_expire = i->expires;
|
||||
min_index = i - m_mappings.begin();
|
||||
}
|
||||
}
|
||||
|
||||
// this is already the mapping we're waiting for
|
||||
if (m_next_refresh == min_index) return;
|
||||
|
||||
if (min_index >= 0)
|
||||
{
|
||||
m_refresh_timer.expires_from_now(min_expire - now);
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string() << " next expiration ["
|
||||
" i: " << min_index
|
||||
<< " ttl: " << total_seconds(min_expire - time_now())
|
||||
<< " ]" << std::endl;
|
||||
#endif
|
||||
asio::error_code ec;
|
||||
if (m_next_refresh >= 0) m_refresh_timer.cancel(ec);
|
||||
m_refresh_timer.expires_from_now(min_expire - now, ec);
|
||||
m_refresh_timer.async_wait(bind(&natpmp::mapping_expired, self(), _1, min_index));
|
||||
m_next_refresh = min_index;
|
||||
}
|
||||
}
|
||||
|
||||
void natpmp::mapping_expired(asio::error_code const& e, int i)
|
||||
{
|
||||
if (e) return;
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "*** mapping " << i << " expired, updating" << std::endl;
|
||||
m_log << time_now_string() << " mapping expired [ i: " << i << " ]" << std::endl;
|
||||
#endif
|
||||
refresh_mapping(i);
|
||||
}
|
||||
|
||||
void natpmp::refresh_mapping(int i)
|
||||
{
|
||||
m_mappings[i].need_update = true;
|
||||
if (m_currently_mapping == -1)
|
||||
{
|
||||
// the socket is not currently in use
|
||||
// send out a mapping request
|
||||
m_retry_count = 0;
|
||||
send_map_request(i);
|
||||
m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
|
||||
, m_remote, bind(&natpmp::on_reply, self(), _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
void natpmp::try_next_mapping(int i)
|
||||
{
|
||||
++i;
|
||||
if (i >= num_mappings) i = 0;
|
||||
if (m_mappings[i].need_update)
|
||||
refresh_mapping(i);
|
||||
m_mappings[i].action = mapping_t::action_add;
|
||||
if (m_next_refresh == i) m_next_refresh = -1;
|
||||
update_mapping(i);
|
||||
}
|
||||
|
||||
void natpmp::close()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
m_abort = true;
|
||||
asio::error_code ec;
|
||||
m_socket.close(ec);
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << time_now_string() << " close" << std::endl;
|
||||
#endif
|
||||
if (m_disabled) return;
|
||||
for (int i = 0; i < num_mappings; ++i)
|
||||
ptime now = time_now();
|
||||
for (std::vector<mapping_t>::iterator i = m_mappings.begin()
|
||||
, end(m_mappings.end()); i != end; ++i)
|
||||
{
|
||||
if (m_mappings[i].local_port == 0)
|
||||
continue;
|
||||
m_mappings[i].external_port = 0;
|
||||
refresh_mapping(i);
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << " " << (i - m_mappings.begin()) << " [ "
|
||||
"proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp")
|
||||
<< " port: " << i->external_port
|
||||
<< " local-port: " << i->local_port
|
||||
<< " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete")
|
||||
<< " ttl: " << total_seconds(i->expires - now)
|
||||
<< " ]" << std::endl;
|
||||
#endif
|
||||
if (i->protocol == none) continue;
|
||||
i->action = mapping_t::action_delete;
|
||||
}
|
||||
m_refresh_timer.cancel();
|
||||
m_send_timer.cancel();
|
||||
m_refresh_timer.cancel(ec);
|
||||
update_mapping(0);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -224,7 +224,10 @@ namespace libtorrent
|
|||
// the number of blocks we want, but it will try to make the picked
|
||||
// blocks be from whole pieces, possibly by returning more blocks
|
||||
// than we requested.
|
||||
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint());
|
||||
#ifndef NDEBUG
|
||||
asio::error_code ec;
|
||||
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec);
|
||||
#endif
|
||||
|
||||
piece_picker::piece_state_t state;
|
||||
peer_connection::peer_speed_t speed = c.peer_speed();
|
||||
|
@ -343,7 +346,7 @@ namespace libtorrent
|
|||
policy::policy(torrent* t)
|
||||
: m_torrent(t)
|
||||
, m_available_free_upload(0)
|
||||
// , m_last_optimistic_disconnect(min_time())
|
||||
, m_num_connect_candidates(0)
|
||||
{ TORRENT_ASSERT(t); }
|
||||
|
||||
// disconnects and removes all peers that are now filtered
|
||||
|
@ -364,7 +367,7 @@ namespace libtorrent
|
|||
|
||||
if (i->second.connection)
|
||||
{
|
||||
i->second.connection->disconnect();
|
||||
i->second.connection->disconnect("peer banned by IP filter");
|
||||
if (ses.m_alerts.should_post(alert::info))
|
||||
{
|
||||
ses.m_alerts.post_alert(peer_blocked_alert(i->second.ip.address()
|
||||
|
@ -382,92 +385,26 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
if (p) p->clear_peer(&i->second);
|
||||
if (i->second.seed) --m_num_seeds;
|
||||
m_peers.erase(i++);
|
||||
}
|
||||
}
|
||||
/*
|
||||
// finds the peer that has the worst download rate
|
||||
// and returns it. May return 0 if all peers are
|
||||
// choked.
|
||||
policy::iterator policy::find_choke_candidate()
|
||||
|
||||
bool policy::is_connect_candidate(peer const& p, bool finished)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
iterator worst_peer = m_peers.end();
|
||||
size_type min_weight = (std::numeric_limits<int>::min)();
|
||||
|
||||
#ifndef NDEBUG
|
||||
int unchoked_counter = m_num_unchoked;
|
||||
#endif
|
||||
if (p.connection
|
||||
|| p.banned
|
||||
|| p.type == peer::not_connectable
|
||||
|| (p.seed && finished)
|
||||
|| p.failcount >= m_torrent->settings().max_failcount)
|
||||
return false;
|
||||
|
||||
// TODO: make this selection better
|
||||
|
||||
for (iterator i = m_peers.begin();
|
||||
i != m_peers.end(); ++i)
|
||||
{
|
||||
peer_connection* c = i->connection;
|
||||
|
||||
if (c == 0) continue;
|
||||
if (c->is_choked()) continue;
|
||||
#ifndef NDEBUG
|
||||
unchoked_counter--;
|
||||
#endif
|
||||
if (c->is_disconnecting()) continue;
|
||||
// if the peer isn't interested, just choke it
|
||||
if (!c->is_peer_interested())
|
||||
return i;
|
||||
|
||||
size_type diff = i->total_download()
|
||||
- i->total_upload();
|
||||
|
||||
size_type weight = static_cast<int>(c->statistics().download_rate() * 10.f)
|
||||
+ diff
|
||||
+ ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024;
|
||||
|
||||
if (weight >= min_weight && worst_peer != m_peers.end()) continue;
|
||||
|
||||
min_weight = weight;
|
||||
worst_peer = i;
|
||||
continue;
|
||||
}
|
||||
TORRENT_ASSERT(unchoked_counter == 0);
|
||||
return worst_peer;
|
||||
aux::session_impl& ses = m_torrent->session();
|
||||
if (ses.m_port_filter.access(p.ip.port()) & port_filter::blocked)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
policy::iterator policy::find_unchoke_candidate()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
// if all of our peers are unchoked, there's
|
||||
// no left to unchoke
|
||||
if (m_num_unchoked == m_torrent->num_peers())
|
||||
return m_peers.end();
|
||||
|
||||
iterator unchoke_peer = m_peers.end();
|
||||
ptime min_time = libtorrent::min_time();
|
||||
float max_down_speed = 0.f;
|
||||
|
||||
// TODO: make this selection better
|
||||
|
||||
for (iterator i = m_peers.begin();
|
||||
i != m_peers.end(); ++i)
|
||||
{
|
||||
peer_connection* c = i->connection;
|
||||
if (c == 0) continue;
|
||||
if (c->is_disconnecting()) continue;
|
||||
if (!c->is_choked()) continue;
|
||||
if (!c->is_peer_interested()) continue;
|
||||
if (c->share_diff() < -free_upload_amount
|
||||
&& m_torrent->ratio() != 0) continue;
|
||||
if (c->statistics().download_rate() < max_down_speed) continue;
|
||||
|
||||
min_time = i->last_optimistically_unchoked;
|
||||
max_down_speed = c->statistics().download_rate();
|
||||
unchoke_peer = i;
|
||||
}
|
||||
return unchoke_peer;
|
||||
}
|
||||
*/
|
||||
policy::iterator policy::find_disconnect_candidate()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
@ -520,13 +457,14 @@ namespace libtorrent
|
|||
ptime min_connect_time(now);
|
||||
iterator candidate = m_peers.end();
|
||||
|
||||
int max_failcount = m_torrent->settings().max_failcount;
|
||||
int min_reconnect_time = m_torrent->settings().min_reconnect_time;
|
||||
int min_cidr_distance = (std::numeric_limits<int>::max)();
|
||||
bool finished = m_torrent->is_finished();
|
||||
address external_ip = m_torrent->session().m_external_address;
|
||||
|
||||
if (external_ip == address())
|
||||
int min_cidr_distance = (std::numeric_limits<int>::max)();
|
||||
address external_ip = m_torrent->session().external_address();
|
||||
|
||||
// don't bias any particular peers when seeding
|
||||
if (finished || external_ip == address())
|
||||
{
|
||||
// set external_ip to a random value, to
|
||||
// radomize which peers we prefer
|
||||
|
@ -535,15 +473,18 @@ namespace libtorrent
|
|||
external_ip = address_v4(bytes);
|
||||
}
|
||||
|
||||
aux::session_impl& ses = m_torrent->session();
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
int max_inet_as_rate = -1;
|
||||
bool has_db = m_torrent->session().has_asnum_db();
|
||||
#endif
|
||||
|
||||
int connect_candidates = 0;
|
||||
int seeds = 0;
|
||||
for (iterator i = m_peers.begin(); i != m_peers.end(); ++i)
|
||||
{
|
||||
if (i->second.connection) continue;
|
||||
if (i->second.banned) continue;
|
||||
if (i->second.type == peer::not_connectable) continue;
|
||||
if (i->second.seed && finished) continue;
|
||||
if (i->second.failcount >= max_failcount) continue;
|
||||
if (i->second.seed) ++seeds;
|
||||
if (!is_connect_candidate(i->second, finished)) continue;
|
||||
++connect_candidates;
|
||||
|
||||
// prefer peers with lower failcount
|
||||
if (candidate != m_peers.end()
|
||||
|
@ -552,26 +493,51 @@ namespace libtorrent
|
|||
|
||||
if (now - i->second.connected < seconds(i->second.failcount * min_reconnect_time))
|
||||
continue;
|
||||
if (ses.m_port_filter.access(i->second.ip.port()) & port_filter::blocked)
|
||||
continue;
|
||||
|
||||
TORRENT_ASSERT(i->second.connected <= now);
|
||||
|
||||
if (i->second.connected > min_connect_time) continue;
|
||||
int distance = cidr_distance(external_ip, i->second.ip.address());
|
||||
if (distance > min_cidr_distance) continue;
|
||||
// don't replace a candidate that is on the local
|
||||
// network with one that isn't. Local peers should
|
||||
// always be tried first
|
||||
if (candidate != m_peers.end()
|
||||
&& is_local(candidate->second.ip.address())
|
||||
&& !is_local(i->second.ip.address()))
|
||||
continue;
|
||||
|
||||
if (i->second.connected > min_connect_time) continue;
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
if (!finished && has_db)
|
||||
{
|
||||
// don't bias fast peers when seeding
|
||||
std::pair<const int, int>* inet_as = i->second.inet_as;
|
||||
int peak_rate = inet_as ? inet_as->second : 0;
|
||||
if (peak_rate <= max_inet_as_rate) continue;
|
||||
max_inet_as_rate = peak_rate;
|
||||
}
|
||||
|
||||
if (max_inet_as_rate <= 0)
|
||||
#endif
|
||||
{
|
||||
int distance = cidr_distance(external_ip, i->second.ip.address());
|
||||
if (distance > min_cidr_distance) continue;
|
||||
|
||||
min_cidr_distance = distance;
|
||||
}
|
||||
|
||||
min_cidr_distance = distance;
|
||||
min_connect_time = i->second.connected;
|
||||
candidate = i;
|
||||
}
|
||||
|
||||
m_num_connect_candidates = connect_candidates;
|
||||
m_num_seeds = seeds;
|
||||
TORRENT_ASSERT(min_connect_time <= now);
|
||||
|
||||
#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING
|
||||
if (candidate != m_peers.end())
|
||||
{
|
||||
(*m_torrent->session().m_logger) << "*** FOUND CONNECTION CANDIDATE ["
|
||||
(*m_torrent->session().m_logger) << time_now_string()
|
||||
<< " *** FOUND CONNECTION CANDIDATE ["
|
||||
" ip: " << candidate->second.ip <<
|
||||
" d: " << min_cidr_distance <<
|
||||
" external: " << external_ip <<
|
||||
|
@ -582,113 +548,7 @@ namespace libtorrent
|
|||
|
||||
return candidate;
|
||||
}
|
||||
/*
|
||||
policy::iterator policy::find_seed_choke_candidate()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
TORRENT_ASSERT(m_num_unchoked > 0);
|
||||
// first choice candidate.
|
||||
// it is a candidate we owe nothing to and which has been unchoked
|
||||
// the longest.
|
||||
iterator candidate = m_peers.end();
|
||||
|
||||
// not valid when candidate == 0
|
||||
ptime last_unchoke = min_time();
|
||||
|
||||
// second choice candidate.
|
||||
// if there is no first choice candidate, this candidate will be chosen.
|
||||
// it is the candidate that we owe the least to.
|
||||
iterator second_candidate = m_peers.end();
|
||||
size_type lowest_share_diff = 0; // not valid when secondCandidate==0
|
||||
|
||||
for (iterator i = m_peers.begin();
|
||||
i != m_peers.end(); ++i)
|
||||
{
|
||||
peer_connection* c = i->connection;
|
||||
// ignore peers that are choked or
|
||||
// whose connection is closed
|
||||
if (c == 0) continue;
|
||||
|
||||
if (c->is_choked()) continue;
|
||||
if (c->is_disconnecting()) continue;
|
||||
|
||||
size_type share_diff = c->share_diff();
|
||||
|
||||
// select as second candidate the one that we owe the least
|
||||
// to
|
||||
if (second_candidate == m_peers.end()
|
||||
|| share_diff <= lowest_share_diff)
|
||||
{
|
||||
lowest_share_diff = share_diff;
|
||||
second_candidate = i;
|
||||
}
|
||||
|
||||
// select as first candidate the one that we don't owe anything to
|
||||
// and has been waiting for an unchoke the longest
|
||||
if (share_diff > 0) continue;
|
||||
if (candidate == m_peers.end()
|
||||
|| last_unchoke > i->last_optimistically_unchoked)
|
||||
{
|
||||
last_unchoke = i->last_optimistically_unchoked;
|
||||
candidate = i;
|
||||
}
|
||||
}
|
||||
if (candidate != m_peers.end()) return candidate;
|
||||
TORRENT_ASSERT(second_candidate != m_peers.end());
|
||||
return second_candidate;
|
||||
}
|
||||
|
||||
policy::iterator policy::find_seed_unchoke_candidate()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
iterator candidate = m_peers.end();
|
||||
ptime last_unchoke = time_now();
|
||||
|
||||
for (iterator i = m_peers.begin();
|
||||
i != m_peers.end(); ++i)
|
||||
{
|
||||
peer_connection* c = i->connection;
|
||||
if (c == 0) continue;
|
||||
if (!c->is_choked()) continue;
|
||||
if (!c->is_peer_interested()) continue;
|
||||
if (c->is_disconnecting()) continue;
|
||||
if (last_unchoke < i->last_optimistically_unchoked) continue;
|
||||
last_unchoke = i->last_optimistically_unchoked;
|
||||
candidate = i;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
bool policy::seed_unchoke_one_peer()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
iterator p = find_seed_unchoke_candidate();
|
||||
if (p != m_peers.end())
|
||||
{
|
||||
TORRENT_ASSERT(p->connection->is_choked());
|
||||
p->connection->send_unchoke();
|
||||
p->last_optimistically_unchoked = time_now();
|
||||
++m_num_unchoked;
|
||||
}
|
||||
return p != m_peers.end();
|
||||
}
|
||||
|
||||
void policy::seed_choke_one_peer()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
iterator p = find_seed_choke_candidate();
|
||||
if (p != m_peers.end())
|
||||
{
|
||||
TORRENT_ASSERT(!p->connection->is_choked());
|
||||
p->connection->send_choke();
|
||||
--m_num_unchoked;
|
||||
}
|
||||
}
|
||||
*/
|
||||
void policy::pulse()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
@ -699,19 +559,39 @@ namespace libtorrent
|
|||
if (m_torrent->has_picker())
|
||||
p = &m_torrent->picker();
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
bool pinged = false;
|
||||
#endif
|
||||
|
||||
ptime now = time_now();
|
||||
// remove old disconnected peers from the list
|
||||
for (iterator i = m_peers.begin(); i != m_peers.end();)
|
||||
{
|
||||
peer& pe = i->second;
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
// try to send a DHT ping to this peer
|
||||
// as well, to figure out if it supports
|
||||
// DHT (uTorrent and BitComet doesn't
|
||||
// advertise support)
|
||||
if (!pinged && !pe.added_to_dht)
|
||||
{
|
||||
udp::endpoint node(pe.ip.address(), pe.ip.port());
|
||||
m_torrent->session().add_dht_node(node);
|
||||
pe.added_to_dht = true;
|
||||
pinged = true;
|
||||
}
|
||||
#endif
|
||||
// this timeout has to be customizable!
|
||||
// don't remove banned peers, they should
|
||||
// remain banned
|
||||
if (i->second.connection == 0
|
||||
&& i->second.connected != min_time()
|
||||
&& !i->second.banned
|
||||
&& now - i->second.connected > minutes(120))
|
||||
if (pe.connection == 0
|
||||
&& pe.connected != min_time()
|
||||
&& !pe.banned
|
||||
&& now - pe.connected > minutes(120))
|
||||
{
|
||||
if (p) p->clear_peer(&i->second);
|
||||
if (p) p->clear_peer(&pe);
|
||||
if (pe.seed) --m_num_seeds;
|
||||
m_peers.erase(i++);
|
||||
}
|
||||
else
|
||||
|
@ -796,123 +676,6 @@ namespace libtorrent
|
|||
, m_torrent->end()
|
||||
, m_available_free_upload);
|
||||
}
|
||||
/*
|
||||
// ------------------------
|
||||
// seed choking policy
|
||||
// ------------------------
|
||||
if (m_torrent->is_seed())
|
||||
{
|
||||
if (m_num_unchoked > m_torrent->m_uploads_quota.given)
|
||||
{
|
||||
do
|
||||
{
|
||||
iterator p = find_seed_choke_candidate();
|
||||
--m_num_unchoked;
|
||||
TORRENT_ASSERT(p != m_peers.end());
|
||||
if (p == m_peers.end()) break;
|
||||
|
||||
TORRENT_ASSERT(!p->connection->is_choked());
|
||||
p->connection->send_choke();
|
||||
} while (m_num_unchoked > m_torrent->m_uploads_quota.given);
|
||||
}
|
||||
else if (m_num_unchoked > 0)
|
||||
{
|
||||
// optimistic unchoke. trade the 'worst'
|
||||
// unchoked peer with one of the choked
|
||||
// TODO: This rotation should happen
|
||||
// far less frequent than this!
|
||||
TORRENT_ASSERT(m_num_unchoked <= m_torrent->num_peers());
|
||||
iterator p = find_seed_unchoke_candidate();
|
||||
if (p != m_peers.end())
|
||||
{
|
||||
TORRENT_ASSERT(p->connection->is_choked());
|
||||
seed_choke_one_peer();
|
||||
p->connection->send_unchoke();
|
||||
++m_num_unchoked;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// make sure we have enough
|
||||
// unchoked peers
|
||||
while (m_num_unchoked < m_torrent->m_uploads_quota.given)
|
||||
{
|
||||
if (!seed_unchoke_one_peer()) break;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
check_invariant();
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------
|
||||
// downloading choking policy
|
||||
// ----------------------------
|
||||
else
|
||||
{
|
||||
if (m_torrent->ratio() != 0)
|
||||
{
|
||||
// choke peers that have leeched too much without giving anything back
|
||||
for (iterator i = m_peers.begin();
|
||||
i != m_peers.end(); ++i)
|
||||
{
|
||||
peer_connection* c = i->connection;
|
||||
if (c == 0) continue;
|
||||
|
||||
size_type diff = i->connection->share_diff();
|
||||
if (diff < -free_upload_amount
|
||||
&& !c->is_choked())
|
||||
{
|
||||
// if we have uploaded more than a piece for free, choke peer and
|
||||
// wait until we catch up with our download.
|
||||
c->send_choke();
|
||||
--m_num_unchoked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_torrent->m_uploads_quota.given < m_torrent->num_peers())
|
||||
{
|
||||
TORRENT_ASSERT(m_torrent->m_uploads_quota.given >= 0);
|
||||
|
||||
// make sure we don't have too many
|
||||
// unchoked peers
|
||||
if (m_num_unchoked > m_torrent->m_uploads_quota.given)
|
||||
{
|
||||
do
|
||||
{
|
||||
iterator p = find_choke_candidate();
|
||||
if (p == m_peers.end()) break;
|
||||
TORRENT_ASSERT(p != m_peers.end());
|
||||
TORRENT_ASSERT(!p->connection->is_choked());
|
||||
p->connection->send_choke();
|
||||
--m_num_unchoked;
|
||||
} while (m_num_unchoked > m_torrent->m_uploads_quota.given);
|
||||
}
|
||||
// this should prevent the choke/unchoke
|
||||
// problem, since it will not unchoke unless
|
||||
// there actually are any choked peers
|
||||
else if (count_choked() > 0)
|
||||
{
|
||||
// optimistic unchoke. trade the 'worst'
|
||||
// unchoked peer with one of the choked
|
||||
TORRENT_ASSERT(m_num_unchoked <= m_torrent->num_peers());
|
||||
iterator p = find_unchoke_candidate();
|
||||
if (p != m_peers.end())
|
||||
{
|
||||
TORRENT_ASSERT(p->connection->is_choked());
|
||||
choke_one_peer();
|
||||
p->connection->send_unchoke();
|
||||
++m_num_unchoked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have enough
|
||||
// unchoked peers
|
||||
while (m_num_unchoked < m_torrent->m_uploads_quota.given
|
||||
&& unchoke_one_peer());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
int policy::count_choked() const
|
||||
|
@ -931,7 +694,7 @@ namespace libtorrent
|
|||
return ret;
|
||||
}
|
||||
|
||||
void policy::new_connection(peer_connection& c)
|
||||
bool policy::new_connection(peer_connection& c)
|
||||
{
|
||||
TORRENT_ASSERT(!c.is_local());
|
||||
|
||||
|
@ -943,16 +706,20 @@ namespace libtorrent
|
|||
|
||||
// TODO: only allow _one_ connection to use this
|
||||
// override at a time
|
||||
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint());
|
||||
asio::error_code ec;
|
||||
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec);
|
||||
|
||||
aux::session_impl& ses = m_torrent->session();
|
||||
|
||||
if (m_torrent->num_peers() >= m_torrent->max_connections()
|
||||
&& m_torrent->session().num_connections() >= m_torrent->session().max_connections()
|
||||
&& ses.num_connections() >= ses.max_connections()
|
||||
&& c.remote().address() != m_torrent->current_tracker().address())
|
||||
{
|
||||
throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect
|
||||
c.disconnect("too many connections, refusing incoming connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
if (c.remote().address() == m_torrent->current_tracker().address())
|
||||
{
|
||||
m_torrent->debug_log("overriding connection limit for tracker NAT-check");
|
||||
|
@ -977,7 +744,10 @@ namespace libtorrent
|
|||
if (i != m_peers.end())
|
||||
{
|
||||
if (i->second.banned)
|
||||
throw protocol_error("ip address banned, closing");
|
||||
{
|
||||
c.disconnect("ip address banned, closing");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i->second.connection != 0)
|
||||
{
|
||||
|
@ -986,16 +756,18 @@ namespace libtorrent
|
|||
// or the current one is already connected
|
||||
if (!i->second.connection->is_connecting() || c.is_local())
|
||||
{
|
||||
throw protocol_error("duplicate connection, closing");
|
||||
c.disconnect("duplicate connection, closing");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
m_torrent->debug_log("duplicate connection. existing connection"
|
||||
" is connecting and this connection is incoming. closing existing "
|
||||
"connection in favour of this one");
|
||||
#endif
|
||||
i->second.connection->disconnect();
|
||||
i->second.connection->disconnect("incoming duplicate connection "
|
||||
"with higher priority, closing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1003,10 +775,18 @@ namespace libtorrent
|
|||
{
|
||||
// we don't have any info about this peer.
|
||||
// add a new entry
|
||||
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint());
|
||||
asio::error_code ec;
|
||||
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec);
|
||||
|
||||
peer p(c.remote(), peer::not_connectable, 0);
|
||||
i = m_peers.insert(std::make_pair(c.remote().address(), p));
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
int as = ses.as_for_ip(c.remote().address());
|
||||
#ifndef NDEBUG
|
||||
i->second.inet_as_num = as;
|
||||
#endif
|
||||
i->second.inet_as = ses.lookup_as(as);
|
||||
#endif
|
||||
}
|
||||
|
||||
c.set_peer_info(&i->second);
|
||||
|
@ -1018,13 +798,17 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(i->second.connection);
|
||||
if (!c.fast_reconnect())
|
||||
i->second.connected = time_now();
|
||||
// m_last_optimistic_disconnect = time_now();
|
||||
if (m_num_connect_candidates > 0)
|
||||
--m_num_connect_candidates;
|
||||
return true;
|
||||
}
|
||||
|
||||
void policy::update_peer_port(int port, policy::peer* p, int src)
|
||||
bool policy::update_peer_port(int port, policy::peer* p, int src)
|
||||
{
|
||||
TORRENT_ASSERT(p != 0);
|
||||
if (p->ip.port() == port) return;
|
||||
TORRENT_ASSERT(p->connection);
|
||||
|
||||
if (p->ip.port() == port) return true;
|
||||
|
||||
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
||||
{
|
||||
|
@ -1037,10 +821,12 @@ namespace libtorrent
|
|||
policy::peer& pp = i->second;
|
||||
if (pp.connection)
|
||||
{
|
||||
throw protocol_error("duplicate connection");
|
||||
p->connection->disconnect("duplicate connection");
|
||||
return false;
|
||||
}
|
||||
if (m_torrent->has_picker())
|
||||
m_torrent->picker().clear_peer(&i->second);
|
||||
if (i->second.seed) --m_num_seeds;
|
||||
m_peers.erase(i);
|
||||
}
|
||||
}
|
||||
|
@ -1050,6 +836,18 @@ namespace libtorrent
|
|||
}
|
||||
p->ip.port(port);
|
||||
p->source |= src;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool policy::has_peer(policy::peer const* p) const
|
||||
{
|
||||
// find p in m_peers
|
||||
for (std::multimap<address, peer>::const_iterator i = m_peers.begin()
|
||||
, end(m_peers.end()); i != end; ++i)
|
||||
{
|
||||
if (&i->second == p) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
policy::peer* policy::peer_from_tracker(tcp::endpoint const& remote, peer_id const& pid
|
||||
|
@ -1075,88 +873,101 @@ namespace libtorrent
|
|||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
iterator i;
|
||||
|
||||
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
||||
{
|
||||
iterator i;
|
||||
|
||||
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
||||
std::pair<iterator, iterator> range = m_peers.equal_range(remote.address());
|
||||
i = std::find_if(range.first, range.second, match_peer_endpoint(remote));
|
||||
if (i == range.second) i = m_peers.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
i = m_peers.find(remote.address());
|
||||
}
|
||||
|
||||
if (i == m_peers.end())
|
||||
{
|
||||
// if the IP is blocked, don't add it
|
||||
if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked)
|
||||
{
|
||||
std::pair<iterator, iterator> range = m_peers.equal_range(remote.address());
|
||||
i = std::find_if(range.first, range.second, match_peer_endpoint(remote));
|
||||
if (i == range.second) i = m_peers.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
i = m_peers.find(remote.address());
|
||||
}
|
||||
|
||||
if (i == m_peers.end())
|
||||
{
|
||||
// if the IP is blocked, don't add it
|
||||
if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked)
|
||||
if (ses.m_alerts.should_post(alert::info))
|
||||
{
|
||||
if (ses.m_alerts.should_post(alert::info))
|
||||
{
|
||||
ses.m_alerts.post_alert(peer_blocked_alert(remote.address()
|
||||
, "blocked peer not added to peer list"));
|
||||
}
|
||||
return 0;
|
||||
ses.m_alerts.post_alert(peer_blocked_alert(remote.address()
|
||||
, "blocked peer not added to peer list"));
|
||||
}
|
||||
|
||||
// we don't have any info about this peer.
|
||||
// add a new entry
|
||||
i = m_peers.insert(std::make_pair(remote.address()
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we don't have any info about this peer.
|
||||
// add a new entry
|
||||
i = m_peers.insert(std::make_pair(remote.address()
|
||||
, peer(remote, peer::connectable, src)));
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
if (flags & 0x01) i->second.pe_support = true;
|
||||
if (flags & 0x01) i->second.pe_support = true;
|
||||
#endif
|
||||
if (flags & 0x02) i->second.seed = true;
|
||||
}
|
||||
else
|
||||
if (flags & 0x02)
|
||||
{
|
||||
i->second.type = peer::connectable;
|
||||
|
||||
i->second.ip = remote;
|
||||
i->second.source |= src;
|
||||
|
||||
// if this peer has failed before, decrease the
|
||||
// counter to allow it another try, since somebody
|
||||
// else is appearantly able to connect to it
|
||||
// if it comes from the DHT it might be stale though
|
||||
if (i->second.failcount > 0 && src != peer_info::dht)
|
||||
--i->second.failcount;
|
||||
|
||||
// if we're connected to this peer
|
||||
// we already know if it's a seed or not
|
||||
// so we don't have to trust this source
|
||||
if ((flags & 0x02) && !i->second.connection) i->second.seed = true;
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (i->second.connection)
|
||||
{
|
||||
// this means we're already connected
|
||||
// to this peer. don't connect to
|
||||
// it again.
|
||||
|
||||
m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":"
|
||||
+ boost::lexical_cast<std::string>(remote.port()) + " "
|
||||
+ boost::lexical_cast<std::string>(i->second.connection->pid()));
|
||||
|
||||
TORRENT_ASSERT(i->second.connection->associated_torrent().lock().get() == m_torrent);
|
||||
}
|
||||
#endif
|
||||
i->second.seed = true;
|
||||
++m_num_seeds;
|
||||
}
|
||||
return &i->second;
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
int as = ses.as_for_ip(remote.address());
|
||||
#ifndef NDEBUG
|
||||
i->second.inet_as_num = as;
|
||||
#endif
|
||||
i->second.inet_as = ses.lookup_as(as);
|
||||
#endif
|
||||
if (is_connect_candidate(i->second, m_torrent->is_finished()))
|
||||
++m_num_connect_candidates;
|
||||
}
|
||||
catch(std::exception& e)
|
||||
else
|
||||
{
|
||||
if (m_torrent->alerts().should_post(alert::debug))
|
||||
bool was_conn_cand = is_connect_candidate(i->second, m_torrent->is_finished());
|
||||
|
||||
i->second.type = peer::connectable;
|
||||
|
||||
i->second.ip = remote;
|
||||
i->second.source |= src;
|
||||
|
||||
// if this peer has failed before, decrease the
|
||||
// counter to allow it another try, since somebody
|
||||
// else is appearantly able to connect to it
|
||||
// only trust this if it comes from the tracker
|
||||
if (i->second.failcount > 0 && src == peer_info::tracker)
|
||||
--i->second.failcount;
|
||||
|
||||
// if we're connected to this peer
|
||||
// we already know if it's a seed or not
|
||||
// so we don't have to trust this source
|
||||
if ((flags & 0x02) && !i->second.connection)
|
||||
{
|
||||
m_torrent->alerts().post_alert(
|
||||
peer_error_alert(remote, pid, e.what()));
|
||||
if (!i->second.seed) ++m_num_seeds;
|
||||
i->second.seed = true;
|
||||
}
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
if (i->second.connection)
|
||||
{
|
||||
// this means we're already connected
|
||||
// to this peer. don't connect to
|
||||
// it again.
|
||||
|
||||
m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":"
|
||||
+ boost::lexical_cast<std::string>(remote.port()) + " "
|
||||
+ boost::lexical_cast<std::string>(i->second.connection->pid()));
|
||||
|
||||
TORRENT_ASSERT(i->second.connection->associated_torrent().lock().get() == m_torrent);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (was_conn_cand != is_connect_candidate(i->second, m_torrent->is_finished()))
|
||||
if (was_conn_cand) --m_num_connect_candidates;
|
||||
else ++m_num_connect_candidates;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return &i->second;
|
||||
}
|
||||
|
||||
// this is called when we are choked by a peer
|
||||
|
@ -1209,6 +1020,8 @@ namespace libtorrent
|
|||
, boost::bind<bool>(std::equal_to<peer_connection*>(), bind(&peer::connection
|
||||
, bind(&iterator::value_type::second, _1)), &c)) != m_peers.end());
|
||||
|
||||
aux::session_impl& ses = m_torrent->session();
|
||||
|
||||
// if the peer is choked and we have upload slots left,
|
||||
// then unchoke it. Another condition that has to be met
|
||||
// is that the torrent doesn't keep track of the individual
|
||||
|
@ -1220,23 +1033,23 @@ namespace libtorrent
|
|||
// In that case we don't care if people are leeching, they
|
||||
// can't pay for their downloads anyway.
|
||||
if (c.is_choked()
|
||||
&& m_torrent->session().num_uploads() < m_torrent->session().max_uploads()
|
||||
&& ses.num_uploads() < ses.max_uploads()
|
||||
&& (m_torrent->ratio() == 0
|
||||
|| c.share_diff() >= -free_upload_amount
|
||||
|| m_torrent->is_finished()))
|
||||
{
|
||||
m_torrent->session().unchoke_peer(c);
|
||||
ses.unchoke_peer(c);
|
||||
}
|
||||
#if defined(TORRENT_VERBOSE_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING
|
||||
else if (c.is_choked())
|
||||
{
|
||||
std::string reason;
|
||||
if (m_torrent->session().num_uploads() >= m_torrent->session().max_uploads())
|
||||
if (ses.num_uploads() >= ses.max_uploads())
|
||||
{
|
||||
reason = "the number of uploads ("
|
||||
+ boost::lexical_cast<std::string>(m_torrent->session().num_uploads())
|
||||
+ boost::lexical_cast<std::string>(ses.num_uploads())
|
||||
+ ") is more than or equal to the limit ("
|
||||
+ boost::lexical_cast<std::string>(m_torrent->session().max_uploads())
|
||||
+ boost::lexical_cast<std::string>(ses.max_uploads())
|
||||
+ ")";
|
||||
}
|
||||
else
|
||||
|
@ -1326,28 +1139,12 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(!p->second.connection);
|
||||
TORRENT_ASSERT(p->second.type == peer::connectable);
|
||||
|
||||
try
|
||||
if (!m_torrent->connect_to_peer(&p->second))
|
||||
{
|
||||
if (!m_torrent->connect_to_peer(&p->second))
|
||||
{
|
||||
++p->second.failcount;
|
||||
return false;
|
||||
}
|
||||
p->second.connection->add_stat(p->second.prev_amount_download, p->second.prev_amount_upload);
|
||||
p->second.prev_amount_download = 0;
|
||||
p->second.prev_amount_upload = 0;
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING)
|
||||
(*m_torrent->session().m_logger) << "*** CONNECTION FAILED '"
|
||||
<< e.what() << "'\n";
|
||||
#endif
|
||||
std::cerr << e.what() << std::endl;
|
||||
++p->second.failcount;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool policy::disconnect_one_peer()
|
||||
|
@ -1355,16 +1152,16 @@ namespace libtorrent
|
|||
iterator p = find_disconnect_candidate();
|
||||
if (p == m_peers.end())
|
||||
return false;
|
||||
#if defined(TORRENT_VERBOSE_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
(*p->second.connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n";
|
||||
#endif
|
||||
|
||||
p->second.connection->disconnect();
|
||||
p->second.connection->disconnect("too many connections, closing");
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is called whenever a peer connection is closed
|
||||
void policy::connection_closed(const peer_connection& c) throw()
|
||||
void policy::connection_closed(const peer_connection& c)
|
||||
{
|
||||
// too expensive
|
||||
// INVARIANT_CHECK;
|
||||
|
@ -1397,6 +1194,9 @@ namespace libtorrent
|
|||
// p->connected = time_now();
|
||||
}
|
||||
|
||||
if (is_connect_candidate(*p, m_torrent->is_finished()))
|
||||
++m_num_connect_candidates;
|
||||
|
||||
// if the share ratio is 0 (infinite), the
|
||||
// m_available_free_upload isn't used,
|
||||
// because it isn't necessary.
|
||||
|
@ -1406,6 +1206,8 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(c.share_diff() < (std::numeric_limits<size_type>::max)());
|
||||
m_available_free_upload += c.share_diff();
|
||||
}
|
||||
TORRENT_ASSERT(p->prev_amount_upload == 0);
|
||||
TORRENT_ASSERT(p->prev_amount_download == 0);
|
||||
p->prev_amount_download += c.statistics().total_payload_download();
|
||||
p->prev_amount_upload += c.statistics().total_payload_upload();
|
||||
}
|
||||
|
@ -1428,8 +1230,8 @@ namespace libtorrent
|
|||
// INVARIANT_CHECK;
|
||||
|
||||
TORRENT_ASSERT(c);
|
||||
try { TORRENT_ASSERT(c->remote() == c->get_socket()->remote_endpoint()); }
|
||||
catch (std::exception&) {}
|
||||
asio::error_code ec;
|
||||
TORRENT_ASSERT(c->remote() == c->get_socket()->remote_endpoint(ec) || ec);
|
||||
|
||||
return std::find_if(
|
||||
m_peers.begin()
|
||||
|
@ -1439,6 +1241,7 @@ namespace libtorrent
|
|||
|
||||
void policy::check_invariant() const
|
||||
{
|
||||
TORRENT_ASSERT(m_num_connect_candidates >= 0);
|
||||
if (m_torrent->is_aborted()) return;
|
||||
|
||||
int connected_peers = 0;
|
||||
|
@ -1451,6 +1254,9 @@ namespace libtorrent
|
|||
i != m_peers.end(); ++i)
|
||||
{
|
||||
peer const& p = i->second;
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
TORRENT_ASSERT(p.inet_as == 0 || p.inet_as->first == p.inet_as_num);
|
||||
#endif
|
||||
if (!m_torrent->settings().allow_multiple_connections_per_ip)
|
||||
{
|
||||
TORRENT_ASSERT(m_peers.count(p.ip.address()) == 1);
|
||||
|
@ -1467,6 +1273,8 @@ namespace libtorrent
|
|||
{
|
||||
continue;
|
||||
}
|
||||
TORRENT_ASSERT(p.prev_amount_upload == 0);
|
||||
TORRENT_ASSERT(p.prev_amount_download == 0);
|
||||
if (p.optimistically_unchoked)
|
||||
{
|
||||
TORRENT_ASSERT(p.connection);
|
||||
|
@ -1536,25 +1344,32 @@ namespace libtorrent
|
|||
|
||||
policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t, int src)
|
||||
: ip(ip_)
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
, inet_as(0)
|
||||
#endif
|
||||
, failcount(0)
|
||||
, trust_points(0)
|
||||
, source(src)
|
||||
, hashfails(0)
|
||||
, type(t)
|
||||
, fast_reconnects(0)
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
, pe_support(true)
|
||||
#endif
|
||||
, failcount(0)
|
||||
, hashfails(0)
|
||||
, seed(false)
|
||||
, fast_reconnects(0)
|
||||
, optimistically_unchoked(false)
|
||||
, last_optimistically_unchoked(min_time())
|
||||
, connected(min_time())
|
||||
, trust_points(0)
|
||||
, seed(false)
|
||||
, on_parole(false)
|
||||
, banned(false)
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
, added_to_dht(false)
|
||||
#endif
|
||||
, connection(0)
|
||||
, prev_amount_upload(0)
|
||||
, prev_amount_download(0)
|
||||
, banned(false)
|
||||
, source(src)
|
||||
, connection(0)
|
||||
, last_optimistically_unchoked(min_time())
|
||||
, connected(min_time())
|
||||
{
|
||||
TORRENT_ASSERT((src & 0xff) == src);
|
||||
TORRENT_ASSERT(connected < time_now());
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,11 @@ using boost::bind;
|
|||
using boost::mutex;
|
||||
using libtorrent::aux::session_impl;
|
||||
|
||||
#ifdef TORRENT_MEMDEBUG
|
||||
void start_malloc_debug();
|
||||
void stop_malloc_debug();
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
|
@ -107,16 +112,19 @@ namespace libtorrent
|
|||
fingerprint const& id
|
||||
, std::pair<int, int> listen_port_range
|
||||
, char const* listen_interface
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
, fs::path logpath
|
||||
#endif
|
||||
)
|
||||
: m_impl(new session_impl(listen_port_range, id, listen_interface
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
, logpath
|
||||
#endif
|
||||
))
|
||||
{
|
||||
#ifdef TORRENT_MEMDEBUG
|
||||
start_malloc_debug();
|
||||
#endif
|
||||
// turn off the filename checking in boost.filesystem
|
||||
TORRENT_ASSERT(listen_port_range.first > 0);
|
||||
TORRENT_ASSERT(listen_port_range.first < listen_port_range.second);
|
||||
|
@ -130,16 +138,19 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
session::session(fingerprint const& id
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
, fs::path logpath
|
||||
#endif
|
||||
)
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
: m_impl(new session_impl(std::make_pair(0, 0), id, "0.0.0.0", logpath))
|
||||
#else
|
||||
: m_impl(new session_impl(std::make_pair(0, 0), id, "0.0.0.0"))
|
||||
#endif
|
||||
{
|
||||
#ifdef TORRENT_MEMDEBUG
|
||||
start_malloc_debug();
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
boost::function0<void> test = boost::ref(*m_impl);
|
||||
TORRENT_ASSERT(!test.empty());
|
||||
|
@ -148,6 +159,9 @@ namespace libtorrent
|
|||
|
||||
session::~session()
|
||||
{
|
||||
#ifdef TORRENT_MEMDEBUG
|
||||
stop_malloc_debug();
|
||||
#endif
|
||||
TORRENT_ASSERT(m_impl);
|
||||
// if there is at least one destruction-proxy
|
||||
// abort the session and let the destructor
|
||||
|
@ -161,6 +175,28 @@ namespace libtorrent
|
|||
m_impl->add_extension(ext);
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
bool session::load_asnum_db(char const* file)
|
||||
{
|
||||
return m_impl->load_asnum_db(file);
|
||||
}
|
||||
|
||||
bool session::load_country_db(char const* file)
|
||||
{
|
||||
return m_impl->load_country_db(file);
|
||||
}
|
||||
#endif
|
||||
|
||||
void session::load_state(entry const& ses_state)
|
||||
{
|
||||
m_impl->load_state(ses_state);
|
||||
}
|
||||
|
||||
entry session::state() const
|
||||
{
|
||||
return m_impl->state();
|
||||
}
|
||||
|
||||
void session::set_ip_filter(ip_filter const& f)
|
||||
{
|
||||
m_impl->set_ip_filter(f);
|
||||
|
@ -263,6 +299,17 @@ namespace libtorrent
|
|||
return m_impl->status();
|
||||
}
|
||||
|
||||
void session::get_cache_info(sha1_hash const& ih
|
||||
, std::vector<cached_piece_info>& ret) const
|
||||
{
|
||||
m_impl->m_disk_thread.get_cache_info(ih, ret);
|
||||
}
|
||||
|
||||
cache_status session::get_cache_status() const
|
||||
{
|
||||
return m_impl->m_disk_thread.status();
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
|
||||
void session::start_dht(entry const& startup_state)
|
||||
|
@ -437,14 +484,14 @@ namespace libtorrent
|
|||
m_impl->start_lsd();
|
||||
}
|
||||
|
||||
void session::start_natpmp()
|
||||
natpmp* session::start_natpmp()
|
||||
{
|
||||
m_impl->start_natpmp();
|
||||
return m_impl->start_natpmp();
|
||||
}
|
||||
|
||||
void session::start_upnp()
|
||||
upnp* session::start_upnp()
|
||||
{
|
||||
m_impl->start_upnp();
|
||||
return m_impl->start_upnp();
|
||||
}
|
||||
|
||||
void session::stop_lsd()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2007, 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 "libtorrent/pch.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <numeric>
|
||||
#include <cstdio>
|
||||
|
||||
#include "libtorrent/peer_connection.hpp"
|
||||
#include "libtorrent/bt_peer_connection.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/torrent.hpp"
|
||||
#include "libtorrent/extensions.hpp"
|
||||
#include "libtorrent/extensions/smart_ban.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/disk_io_thread.hpp"
|
||||
#include "libtorrent/aux_/session_impl.hpp"
|
||||
|
||||
namespace libtorrent { namespace
|
||||
{
|
||||
|
||||
struct smart_ban_plugin : torrent_plugin, boost::enable_shared_from_this<smart_ban_plugin>
|
||||
{
|
||||
smart_ban_plugin(torrent& t)
|
||||
: m_torrent(t)
|
||||
, m_salt(rand())
|
||||
{
|
||||
}
|
||||
|
||||
void on_piece_pass(int p)
|
||||
{
|
||||
#ifdef TORRENT_LOGGING
|
||||
(*m_torrent.session().m_logger) << time_now_string() << " PIECE PASS [ p: " << p
|
||||
<< " | block_crc_size: " << m_block_crc.size() << " ]\n";
|
||||
#endif
|
||||
// has this piece failed earlier? If it has, go through the
|
||||
// CRCs from the time it failed and ban the peers that
|
||||
// sent bad blocks
|
||||
std::map<piece_block, block_entry>::iterator i = m_block_crc.lower_bound(piece_block(p, 0));
|
||||
if (i == m_block_crc.end() || i->first.piece_index != p) return;
|
||||
|
||||
int size = m_torrent.torrent_file().piece_size(p);
|
||||
peer_request r = {p, 0, (std::min)(16*1024, size)};
|
||||
piece_block pb(p, 0);
|
||||
while (size > 0)
|
||||
{
|
||||
if (i->first.block_index == pb.block_index)
|
||||
{
|
||||
m_torrent.filesystem().async_read(r, bind(&smart_ban_plugin::on_read_ok_block
|
||||
, shared_from_this(), *i, _1, _2));
|
||||
m_block_crc.erase(i++);
|
||||
}
|
||||
else
|
||||
{
|
||||
TORRENT_ASSERT(i->first.block_index > pb.block_index);
|
||||
}
|
||||
|
||||
if (i == m_block_crc.end() || i->first.piece_index != p)
|
||||
break;
|
||||
|
||||
r.start += 16*1024;
|
||||
size -= 16*1024;
|
||||
r.length = (std::min)(16*1024, size);
|
||||
++pb.block_index;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// make sure we actually removed all the entries for piece 'p'
|
||||
i = m_block_crc.lower_bound(piece_block(p, 0));
|
||||
TORRENT_ASSERT(i == m_block_crc.end() || i->first.piece_index != p);
|
||||
#endif
|
||||
|
||||
if (m_torrent.is_seed())
|
||||
{
|
||||
std::map<piece_block, block_entry>().swap(m_block_crc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void on_piece_failed(int p)
|
||||
{
|
||||
// The piece failed the hash check. Record
|
||||
// the CRC and origin peer of every block
|
||||
|
||||
std::vector<void*> downloaders;
|
||||
m_torrent.picker().get_downloaders(downloaders, p);
|
||||
|
||||
int size = m_torrent.torrent_file().piece_size(p);
|
||||
peer_request r = {p, 0, (std::min)(16*1024, size)};
|
||||
piece_block pb(p, 0);
|
||||
for (std::vector<void*>::iterator i = downloaders.begin()
|
||||
, end(downloaders.end()); i != end; ++i)
|
||||
{
|
||||
if (*i != 0)
|
||||
{
|
||||
m_torrent.filesystem().async_read(r, bind(&smart_ban_plugin::on_read_failed_block
|
||||
, shared_from_this(), pb, (policy::peer*)*i, _1, _2));
|
||||
}
|
||||
|
||||
r.start += 16*1024;
|
||||
size -= 16*1024;
|
||||
r.length = (std::min)(16*1024, size);
|
||||
++pb.block_index;
|
||||
}
|
||||
TORRENT_ASSERT(size <= 0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// this entry ties a specific block CRC to
|
||||
// a peer.
|
||||
struct block_entry
|
||||
{
|
||||
policy::peer* peer;
|
||||
unsigned long crc;
|
||||
};
|
||||
|
||||
void on_read_failed_block(piece_block b, policy::peer* p, int ret, disk_io_job const& j)
|
||||
{
|
||||
TORRENT_ASSERT(p);
|
||||
// ignore read errors
|
||||
if (ret != j.buffer_size) return;
|
||||
|
||||
adler32_crc crc;
|
||||
crc.update(j.buffer, j.buffer_size);
|
||||
crc.update((char const*)&m_salt, sizeof(m_salt));
|
||||
|
||||
block_entry e = {p, crc.final()};
|
||||
|
||||
// since this callback is called directory from the disk io
|
||||
// thread, the session mutex is not locked when we get here
|
||||
aux::session_impl::mutex_t::scoped_lock l(m_torrent.session().m_mutex);
|
||||
|
||||
std::map<piece_block, block_entry>::iterator i = m_block_crc.lower_bound(b);
|
||||
if (i != m_block_crc.end() && i->first == b && i->second.peer == p)
|
||||
{
|
||||
// this peer has sent us this block before
|
||||
if (i->second.crc != e.crc)
|
||||
{
|
||||
// this time the crc of the block is different
|
||||
// from the first time it sent it
|
||||
// at least one of them must be bad
|
||||
|
||||
if (p == 0) return;
|
||||
if (!m_torrent.get_policy().has_peer(p)) return;
|
||||
|
||||
#ifdef TORRENT_LOGGING
|
||||
char const* client = "-";
|
||||
peer_info info;
|
||||
if (p->connection)
|
||||
{
|
||||
p->connection->get_peer_info(info);
|
||||
client = info.client.c_str();
|
||||
}
|
||||
(*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.piece_index
|
||||
<< " | b: " << b.block_index
|
||||
<< " | c: " << client
|
||||
<< " | crc1: " << i->second.crc
|
||||
<< " | crc2: " << e.crc
|
||||
<< " | ip: " << p->ip << " ]\n";
|
||||
#endif
|
||||
p->banned = true;
|
||||
if (p->connection) p->connection->disconnect("banning peer for sending bad data");
|
||||
}
|
||||
// we already have this exact entry in the map
|
||||
// we don't have to insert it
|
||||
return;
|
||||
}
|
||||
|
||||
m_block_crc.insert(i, std::make_pair(b, e));
|
||||
|
||||
#ifdef TORRENT_LOGGING
|
||||
char const* client = "-";
|
||||
peer_info info;
|
||||
if (p->connection)
|
||||
{
|
||||
p->connection->get_peer_info(info);
|
||||
client = info.client.c_str();
|
||||
}
|
||||
(*m_torrent.session().m_logger) << time_now_string() << " STORE BLOCK CRC [ p: " << b.piece_index
|
||||
<< " | b: " << b.block_index
|
||||
<< " | c: " << client
|
||||
<< " | crc: " << e.crc
|
||||
<< " | ip: " << p->ip << " ]\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
void on_read_ok_block(std::pair<piece_block, block_entry> b, int ret, disk_io_job const& j)
|
||||
{
|
||||
// since this callback is called directory from the disk io
|
||||
// thread, the session mutex is not locked when we get here
|
||||
aux::session_impl::mutex_t::scoped_lock l(m_torrent.session().m_mutex);
|
||||
|
||||
// ignore read errors
|
||||
if (ret != j.buffer_size) return;
|
||||
|
||||
adler32_crc crc;
|
||||
crc.update(j.buffer, j.buffer_size);
|
||||
crc.update((char const*)&m_salt, sizeof(m_salt));
|
||||
unsigned long ok_crc = crc.final();
|
||||
|
||||
if (b.second.crc == ok_crc) return;
|
||||
|
||||
policy::peer* p = b.second.peer;
|
||||
|
||||
if (p == 0) return;
|
||||
if (!m_torrent.get_policy().has_peer(p)) return;
|
||||
|
||||
#ifdef TORRENT_LOGGING
|
||||
char const* client = "-";
|
||||
peer_info info;
|
||||
if (p->connection)
|
||||
{
|
||||
p->connection->get_peer_info(info);
|
||||
client = info.client.c_str();
|
||||
}
|
||||
(*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.first.piece_index
|
||||
<< " | b: " << b.first.block_index
|
||||
<< " | c: " << client
|
||||
<< " | ok_crc: " << ok_crc
|
||||
<< " | bad_crc: " << b.second.crc
|
||||
<< " | ip: " << p->ip << " ]\n";
|
||||
#endif
|
||||
p->banned = true;
|
||||
if (p->connection) p->connection->disconnect("banning peer for sending bad data");
|
||||
}
|
||||
|
||||
torrent& m_torrent;
|
||||
|
||||
// This table maps a piece_block (piece and block index
|
||||
// pair) to a peer and the block CRC. The CRC is calculated
|
||||
// from the data in the block + the salt
|
||||
std::map<piece_block, block_entry> m_block_crc;
|
||||
|
||||
// This salt is a random value used to calculate the block CRCs
|
||||
// Since the CRC function that is used is not a one way function
|
||||
// the salt is required to avoid attacks where bad data is sent
|
||||
// that is forged to match the CRC of the good data.
|
||||
int m_salt;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
boost::shared_ptr<torrent_plugin> create_smart_ban_plugin(torrent* t, void*)
|
||||
{
|
||||
return boost::shared_ptr<torrent_plugin>(new smart_ban_plugin(*t));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -220,8 +220,7 @@ namespace libtorrent
|
|||
write_uint8(1, p); // CONNECT command
|
||||
write_uint8(0, p); // reserved
|
||||
write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type
|
||||
write_address(m_remote_endpoint.address(), p);
|
||||
write_uint16(m_remote_endpoint.port(), p);
|
||||
write_endpoint(m_remote_endpoint, p);
|
||||
TORRENT_ASSERT(p - &m_buffer[0] == int(m_buffer.size()));
|
||||
|
||||
asio::async_write(m_sock, asio::buffer(m_buffer)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -81,59 +81,41 @@ using libtorrent::aux::session_impl;
|
|||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
|
||||
#define TORRENT_FORWARD(call) \
|
||||
if (m_ses == 0) return; \
|
||||
TORRENT_ASSERT(m_chk); \
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
|
||||
mutex::scoped_lock l2(m_chk->m_mutex); \
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \
|
||||
if (t == 0) return; \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) return; \
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex); \
|
||||
t->call
|
||||
|
||||
#define TORRENT_FORWARD_RETURN(call, def) \
|
||||
if (m_ses == 0) return def; \
|
||||
TORRENT_ASSERT(m_chk); \
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
|
||||
mutex::scoped_lock l2(m_chk->m_mutex); \
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \
|
||||
if (t == 0) return def; \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) return def; \
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex); \
|
||||
return t->call
|
||||
|
||||
#define TORRENT_FORWARD_RETURN2(call, def) \
|
||||
if (m_ses == 0) return def; \
|
||||
TORRENT_ASSERT(m_chk); \
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
|
||||
mutex::scoped_lock l2(m_chk->m_mutex); \
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \
|
||||
if (t == 0) return def; \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) return def; \
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex); \
|
||||
t->call
|
||||
|
||||
#else
|
||||
|
||||
#define TORRENT_FORWARD(call) \
|
||||
if (m_ses == 0) throw_invalid_handle(); \
|
||||
TORRENT_ASSERT(m_chk); \
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
|
||||
mutex::scoped_lock l2(m_chk->m_mutex); \
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \
|
||||
if (t == 0) throw_invalid_handle(); \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) throw_invalid_handle(); \
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex); \
|
||||
t->call
|
||||
|
||||
#define TORRENT_FORWARD_RETURN(call, def) \
|
||||
if (m_ses == 0) throw_invalid_handle(); \
|
||||
TORRENT_ASSERT(m_chk); \
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
|
||||
mutex::scoped_lock l2(m_chk->m_mutex); \
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \
|
||||
if (t == 0) return def; \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) throw_invalid_handle(); \
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex); \
|
||||
return t->call
|
||||
|
||||
#define TORRENT_FORWARD_RETURN2(call, def) \
|
||||
if (m_ses == 0) throw_invalid_handle(); \
|
||||
TORRENT_ASSERT(m_chk); \
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
|
||||
mutex::scoped_lock l2(m_chk->m_mutex); \
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \
|
||||
if (t == 0) return def; \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) throw_invalid_handle(); \
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex); \
|
||||
t->call
|
||||
|
||||
#endif
|
||||
|
@ -150,30 +132,22 @@ namespace libtorrent
|
|||
throw invalid_handle();
|
||||
}
|
||||
#endif
|
||||
|
||||
torrent* find_torrent(
|
||||
session_impl* ses
|
||||
, aux::checker_impl* chk
|
||||
, sha1_hash const& hash)
|
||||
{
|
||||
aux::piece_checker_data* d = chk->find_torrent(hash);
|
||||
if (d != 0) return d->torrent_ptr.get();
|
||||
|
||||
boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
|
||||
if (t) return t.get();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
void torrent_handle::check_invariant() const
|
||||
{
|
||||
TORRENT_ASSERT((m_ses == 0 && m_chk == 0) || (m_ses != 0 && m_chk != 0));
|
||||
}
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
sha1_hash torrent_handle::info_hash() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
const static sha1_hash empty;
|
||||
TORRENT_FORWARD_RETURN(torrent_file().info_hash(), empty);
|
||||
}
|
||||
|
||||
void torrent_handle::set_max_uploads(int max_uploads) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
@ -279,6 +253,12 @@ namespace libtorrent
|
|||
TORRENT_FORWARD(pause());
|
||||
}
|
||||
|
||||
void torrent_handle::save_resume_data() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
TORRENT_FORWARD(save_resume_data());
|
||||
}
|
||||
|
||||
void torrent_handle::resume() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
@ -301,50 +281,13 @@ namespace libtorrent
|
|||
torrent_status torrent_handle::status() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0)
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return torrent_status();
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
TORRENT_ASSERT(m_chk);
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
mutex::scoped_lock l2(m_chk->m_mutex);
|
||||
|
||||
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
|
||||
if (d != 0)
|
||||
{
|
||||
torrent_status st = d->torrent_ptr->status();
|
||||
|
||||
if (d->processing)
|
||||
{
|
||||
if (d->torrent_ptr->is_allocating())
|
||||
st.state = torrent_status::allocating;
|
||||
else
|
||||
st.state = torrent_status::checking_files;
|
||||
}
|
||||
else
|
||||
st.state = torrent_status::queued_for_checking;
|
||||
st.progress = d->progress;
|
||||
st.paused = d->torrent_ptr->is_paused();
|
||||
return st;
|
||||
}
|
||||
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
if (t) return t->status();
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
return torrent_status();
|
||||
TORRENT_FORWARD_RETURN(status(), torrent_status());
|
||||
}
|
||||
|
||||
void torrent_handle::set_sequenced_download_threshold(int threshold) const
|
||||
void torrent_handle::set_sequential_download(bool sd) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
TORRENT_FORWARD(set_sequenced_download_threshold(threshold));
|
||||
TORRENT_FORWARD(set_sequential_download(sd));
|
||||
}
|
||||
|
||||
std::string torrent_handle::name() const
|
||||
|
@ -466,15 +409,16 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
const static torrent_info empty;
|
||||
if (m_ses == 0) return empty;
|
||||
#else
|
||||
if (m_ses == 0) throw_invalid_handle();
|
||||
#endif
|
||||
TORRENT_ASSERT(m_chk);
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex);
|
||||
mutex::scoped_lock l2(m_chk->m_mutex);
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash);
|
||||
if (t == 0 || !t->valid_metadata())
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
if (!t)
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return empty;
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex);
|
||||
if (!t->valid_metadata())
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return empty;
|
||||
#else
|
||||
|
@ -486,158 +430,15 @@ namespace libtorrent
|
|||
bool torrent_handle::is_valid() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
if (m_ses == 0) return false;
|
||||
TORRENT_ASSERT(m_chk);
|
||||
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex);
|
||||
mutex::scoped_lock l2(m_chk->m_mutex);
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash);
|
||||
return t != 0;
|
||||
return !m_torrent.expired();
|
||||
}
|
||||
|
||||
entry torrent_handle::write_resume_data() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0)
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return entry();
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
TORRENT_ASSERT(m_chk);
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
mutex::scoped_lock l2(m_chk->m_mutex);
|
||||
|
||||
torrent* t = find_torrent(m_ses, m_chk, m_info_hash);
|
||||
if (!t || !t->valid_metadata())
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return entry();
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
|
||||
std::vector<bool> have_pieces = t->pieces();
|
||||
|
||||
entry ret(entry::dictionary_t);
|
||||
|
||||
ret["file-format"] = "libtorrent resume file";
|
||||
ret["file-version"] = 1;
|
||||
|
||||
ret["allocation"] = t->filesystem().compact_allocation()?"compact":"full";
|
||||
|
||||
const sha1_hash& info_hash = t->torrent_file().info_hash();
|
||||
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
|
||||
|
||||
// blocks per piece
|
||||
int num_blocks_per_piece =
|
||||
static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
|
||||
ret["blocks per piece"] = num_blocks_per_piece;
|
||||
|
||||
// if this torrent is a seed, we won't have a piece picker
|
||||
// and there will be no half-finished pieces.
|
||||
if (!t->is_seed())
|
||||
{
|
||||
const piece_picker& p = t->picker();
|
||||
|
||||
const std::vector<piece_picker::downloading_piece>& q
|
||||
= p.get_download_queue();
|
||||
|
||||
// unfinished pieces
|
||||
ret["unfinished"] = entry::list_type();
|
||||
entry::list_type& up = ret["unfinished"].list();
|
||||
|
||||
// info for each unfinished piece
|
||||
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
||||
= q.begin(); i != q.end(); ++i)
|
||||
{
|
||||
if (i->finished == 0) continue;
|
||||
|
||||
entry piece_struct(entry::dictionary_t);
|
||||
|
||||
// the unfinished piece's index
|
||||
piece_struct["piece"] = i->index;
|
||||
|
||||
have_pieces[i->index] = true;
|
||||
|
||||
std::string bitmask;
|
||||
const int num_bitmask_bytes
|
||||
= (std::max)(num_blocks_per_piece / 8, 1);
|
||||
|
||||
for (int j = 0; j < num_bitmask_bytes; ++j)
|
||||
{
|
||||
unsigned char v = 0;
|
||||
int bits = (std::min)(num_blocks_per_piece - j*8, 8);
|
||||
for (int k = 0; k < bits; ++k)
|
||||
v |= (i->info[j*8+k].state == piece_picker::block_info::state_finished)
|
||||
? (1 << k) : 0;
|
||||
bitmask.insert(bitmask.end(), v);
|
||||
TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1);
|
||||
}
|
||||
piece_struct["bitmask"] = bitmask;
|
||||
/*
|
||||
TORRENT_ASSERT(t->filesystem().slot_for(i->index) >= 0);
|
||||
unsigned long adler
|
||||
= t->filesystem().piece_crc(
|
||||
t->filesystem().slot_for(i->index)
|
||||
, t->block_size()
|
||||
, i->info);
|
||||
|
||||
piece_struct["adler32"] = adler;
|
||||
*/
|
||||
// push the struct onto the unfinished-piece list
|
||||
up.push_back(piece_struct);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> piece_index;
|
||||
t->filesystem().export_piece_map(piece_index, have_pieces);
|
||||
entry::list_type& slots = ret["slots"].list();
|
||||
std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
|
||||
|
||||
// write local peers
|
||||
|
||||
entry::list_type& peer_list = ret["peers"].list();
|
||||
entry::list_type& banned_peer_list = ret["banned_peers"].list();
|
||||
|
||||
policy& pol = t->get_policy();
|
||||
|
||||
int max_failcount = t->settings().max_failcount;
|
||||
|
||||
for (policy::iterator i = pol.begin_peer()
|
||||
, end(pol.end_peer()); i != end; ++i)
|
||||
{
|
||||
asio::error_code ec;
|
||||
if (i->second.banned)
|
||||
{
|
||||
tcp::endpoint ip = i->second.ip;
|
||||
entry peer(entry::dictionary_t);
|
||||
peer["ip"] = ip.address().to_string(ec);
|
||||
if (ec) continue;
|
||||
peer["port"] = ip.port();
|
||||
banned_peer_list.push_back(peer);
|
||||
continue;
|
||||
}
|
||||
// we cannot save remote connection
|
||||
// since we don't know their listen port
|
||||
// unless they gave us their listen port
|
||||
// through the extension handshake
|
||||
// so, if the peer is not connectable (i.e. we
|
||||
// don't know its listen port) or if it has
|
||||
// been banned, don't save it.
|
||||
if (i->second.type == policy::peer::not_connectable) continue;
|
||||
|
||||
// don't save peers that doesn't work
|
||||
if (i->second.failcount >= max_failcount) continue;
|
||||
|
||||
tcp::endpoint ip = i->second.ip;
|
||||
entry peer(entry::dictionary_t);
|
||||
peer["ip"] = ip.address().to_string(ec);
|
||||
if (ec) continue;
|
||||
peer["port"] = ip.port();
|
||||
peer_list.push_back(peer);
|
||||
}
|
||||
|
||||
TORRENT_FORWARD(write_resume_data(ret));
|
||||
t->filesystem().write_resume_data(ret);
|
||||
|
||||
return ret;
|
||||
|
@ -654,35 +455,15 @@ namespace libtorrent
|
|||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses == 0)
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
if (!t)
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return;
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
TORRENT_ASSERT(m_chk);
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
|
||||
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex);
|
||||
|
||||
if (!t)
|
||||
{
|
||||
// the torrent is being checked. Add the peer to its
|
||||
// peer list. The entries in there will be connected
|
||||
// once the checking is complete.
|
||||
mutex::scoped_lock l2(m_chk->m_mutex);
|
||||
|
||||
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
|
||||
if (d == 0)
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return;
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
d->peers.push_back(adr);
|
||||
return;
|
||||
}
|
||||
|
||||
peer_id id;
|
||||
std::fill(id.begin(), id.end(), 0);
|
||||
t->get_policy().peer_from_tracker(adr, id, source, 0);
|
||||
|
@ -731,6 +512,12 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
void torrent_handle::get_full_peer_list(std::vector<peer_list_entry>& v) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
TORRENT_FORWARD(get_full_peer_list(v));
|
||||
}
|
||||
|
||||
void torrent_handle::get_peer_info(std::vector<peer_info>& v) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
|
|
@ -58,7 +58,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
|
||||
namespace pt = boost::posix_time;
|
||||
namespace gr = boost::gregorian;
|
||||
|
||||
using namespace libtorrent;
|
||||
|
@ -160,10 +159,13 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
void extract_single_file(const entry& dict, file_entry& target
|
||||
bool extract_single_file(entry const& dict, file_entry& target
|
||||
, std::string const& root_dir)
|
||||
{
|
||||
target.size = dict["length"].integer();
|
||||
entry const* length = dict.find_key("length");
|
||||
if (length == 0 || length->type() != entry::int_t)
|
||||
return false;
|
||||
target.size = length->integer();
|
||||
target.path = root_dir;
|
||||
target.file_base = 0;
|
||||
|
||||
|
@ -172,38 +174,46 @@ namespace
|
|||
// likely to be correctly encoded
|
||||
|
||||
const entry::list_type* list = 0;
|
||||
if (entry const* p = dict.find_key("path.utf-8"))
|
||||
entry const* p8 = dict.find_key("path.utf-8");
|
||||
if (p8 != 0 && p8->type() == entry::list_t)
|
||||
{
|
||||
list = &p->list();
|
||||
list = &p8->list();
|
||||
}
|
||||
else
|
||||
{
|
||||
list = &dict["path"].list();
|
||||
entry const* p = dict.find_key("path");
|
||||
if (p == 0 || p->type() != entry::list_t)
|
||||
return false;
|
||||
list = &p->list();
|
||||
}
|
||||
|
||||
for (entry::list_type::const_iterator i = list->begin();
|
||||
i != list->end(); ++i)
|
||||
{
|
||||
if (i->type() != entry::string_t)
|
||||
return false;
|
||||
if (i->string() != "..")
|
||||
target.path /= i->string();
|
||||
}
|
||||
verify_encoding(target);
|
||||
if (target.path.is_complete()) throw std::runtime_error("torrent contains "
|
||||
"a file with an absolute path: '"
|
||||
+ target.path.native_file_string() + "'");
|
||||
if (target.path.is_complete())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void extract_files(const entry::list_type& list, std::vector<file_entry>& target
|
||||
bool extract_files(const entry::list_type& list, std::vector<file_entry>& target
|
||||
, std::string const& root_dir)
|
||||
{
|
||||
size_type offset = 0;
|
||||
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
target.push_back(file_entry());
|
||||
extract_single_file(*i, target.back(), root_dir);
|
||||
if (!extract_single_file(*i, target.back(), root_dir))
|
||||
return false;
|
||||
target.back().offset = offset;
|
||||
offset += target.back().size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
void remove_dir(fs::path& p)
|
||||
|
@ -231,14 +241,13 @@ namespace libtorrent
|
|||
, m_half_metadata(false)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
read_torrent_info(torrent_file);
|
||||
}
|
||||
catch(type_error&)
|
||||
{
|
||||
std::string error;
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (!read_torrent_info(torrent_file, error))
|
||||
throw invalid_torrent_file();
|
||||
}
|
||||
#else
|
||||
read_torrent_info(torrent_file, error);
|
||||
#endif
|
||||
}
|
||||
|
||||
// constructor used for creating new torrents
|
||||
|
@ -330,8 +339,14 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
void torrent_info::parse_info_section(entry const& info)
|
||||
bool torrent_info::parse_info_section(entry const& info, std::string& error)
|
||||
{
|
||||
if (info.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "'info' entry is not a dictionary";
|
||||
return false;
|
||||
}
|
||||
|
||||
// encode the info-field in order to calculate it's sha1-hash
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), info);
|
||||
|
@ -340,49 +355,82 @@ namespace libtorrent
|
|||
m_info_hash = h.final();
|
||||
|
||||
// extract piece length
|
||||
m_piece_length = int(info["piece length"].integer());
|
||||
if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0");
|
||||
entry const* piece_length = info.find_key("piece length");
|
||||
if (piece_length == 0 || piece_length->type() != entry::int_t)
|
||||
{
|
||||
error = "invalid or missing 'piece length' entry in torrent file";
|
||||
return false;
|
||||
}
|
||||
m_piece_length = int(piece_length->integer());
|
||||
if (m_piece_length <= 0)
|
||||
{
|
||||
error = "invalid torrent. piece length <= 0";
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract file name (or the directory name if it's a multifile libtorrent)
|
||||
if (entry const* e = info.find_key("name.utf-8"))
|
||||
entry const* e = info.find_key("name.utf-8");
|
||||
if (e && e->type() == entry::string_t)
|
||||
{ m_name = e->string(); }
|
||||
else
|
||||
{ m_name = info["name"].string(); }
|
||||
{
|
||||
entry const* e = info.find_key("name");
|
||||
if (e == 0 || e->type() != entry::string_t)
|
||||
{
|
||||
error = "invalid name in torrent file";
|
||||
return false;
|
||||
}
|
||||
m_name = e->string();
|
||||
}
|
||||
|
||||
fs::path tmp = m_name;
|
||||
if (tmp.is_complete())
|
||||
{
|
||||
m_name = tmp.leaf();
|
||||
}
|
||||
else if (tmp.has_branch_path())
|
||||
{
|
||||
fs::path p;
|
||||
for (fs::path::iterator i = tmp.begin()
|
||||
, end(tmp.end()); i != end; ++i)
|
||||
{
|
||||
if (*i == "." || *i == "..") continue;
|
||||
p /= *i;
|
||||
}
|
||||
m_name = p.string();
|
||||
}
|
||||
if (m_name == ".." || m_name == ".")
|
||||
throw std::runtime_error("invalid 'name' of torrent (possible exploit attempt)");
|
||||
if (tmp.is_complete())
|
||||
{
|
||||
m_name = tmp.leaf();
|
||||
}
|
||||
else if (tmp.has_branch_path())
|
||||
{
|
||||
fs::path p;
|
||||
for (fs::path::iterator i = tmp.begin()
|
||||
, end(tmp.end()); i != end; ++i)
|
||||
{
|
||||
if (*i == "." || *i == "..") continue;
|
||||
p /= *i;
|
||||
}
|
||||
m_name = p.string();
|
||||
}
|
||||
if (m_name == ".." || m_name == ".")
|
||||
{
|
||||
|
||||
error = "invalid 'name' of torrent (possible exploit attempt)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract file list
|
||||
entry const* i = info.find_key("files");
|
||||
if (i == 0)
|
||||
{
|
||||
entry const* length = info.find_key("length");
|
||||
if (length == 0 || length->type() != entry::int_t)
|
||||
{
|
||||
error = "invalid length of torrent";
|
||||
return false;
|
||||
}
|
||||
// if there's no list of files, there has to be a length
|
||||
// field.
|
||||
file_entry e;
|
||||
e.path = m_name;
|
||||
e.offset = 0;
|
||||
e.size = info["length"].integer();
|
||||
e.size = length->integer();
|
||||
m_files.push_back(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
extract_files(i->list(), m_files, m_name);
|
||||
if (!extract_files(i->list(), m_files, m_name))
|
||||
{
|
||||
error = "failed to parse files from torrent file";
|
||||
return false;
|
||||
}
|
||||
m_multifile = true;
|
||||
}
|
||||
|
||||
|
@ -395,12 +443,22 @@ namespace libtorrent
|
|||
// we want this division to round upwards, that's why we have the
|
||||
// extra addition
|
||||
|
||||
entry const* pieces_ent = info.find_key("pieces");
|
||||
if (pieces_ent == 0 || pieces_ent->type() != entry::string_t)
|
||||
{
|
||||
error = "invalid or missing 'pieces' entry in torrent file";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
|
||||
m_piece_hash.resize(m_num_pieces);
|
||||
const std::string& hash_string = info["pieces"].string();
|
||||
const std::string& hash_string = pieces_ent->string();
|
||||
|
||||
if ((int)hash_string.length() != m_num_pieces * 20)
|
||||
throw invalid_torrent_file();
|
||||
{
|
||||
error = "incorrect number of piece hashes in torrent file";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_num_pieces; ++i)
|
||||
std::copy(
|
||||
|
@ -436,34 +494,37 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(hasher(&info_section_buf[0], info_section_buf.size()).final()
|
||||
== m_info_hash);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// extracts information from a libtorrent file and fills in the structures in
|
||||
// the torrent object
|
||||
void torrent_info::read_torrent_info(const entry& torrent_file)
|
||||
bool torrent_info::read_torrent_info(const entry& torrent_file, std::string& error)
|
||||
{
|
||||
if (torrent_file.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "torrent file is not a dictionary";
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract the url of the tracker
|
||||
if (entry const* i = torrent_file.find_key("announce-list"))
|
||||
entry const* i = torrent_file.find_key("announce-list");
|
||||
if (i && i->type() == entry::list_t)
|
||||
{
|
||||
const entry::list_type& l = i->list();
|
||||
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
|
||||
{
|
||||
if (j->type() != entry::list_t) break;
|
||||
const entry::list_type& ll = j->list();
|
||||
for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
|
||||
{
|
||||
if (k->type() != entry::string_t) break;
|
||||
announce_entry e(k->string());
|
||||
e.tier = (int)std::distance(l.begin(), j);
|
||||
m_urls.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_urls.size() == 0)
|
||||
{
|
||||
// the announce-list is empty
|
||||
// fall back to look for announce
|
||||
m_urls.push_back(announce_entry(
|
||||
torrent_file["announce"].string()));
|
||||
}
|
||||
// shuffle each tier
|
||||
std::vector<announce_entry>::iterator start = m_urls.begin();
|
||||
std::vector<announce_entry>::iterator stop;
|
||||
|
@ -479,14 +540,17 @@ namespace libtorrent
|
|||
}
|
||||
std::random_shuffle(start, stop);
|
||||
}
|
||||
else if (entry const* i = torrent_file.find_key("announce"))
|
||||
|
||||
entry const* announce = torrent_file.find_key("announce");
|
||||
if (m_urls.empty() && announce && announce->type() == entry::string_t)
|
||||
{
|
||||
m_urls.push_back(announce_entry(i->string()));
|
||||
m_urls.push_back(announce_entry(announce->string()));
|
||||
}
|
||||
|
||||
if (entry const* i = torrent_file.find_key("nodes"))
|
||||
entry const* nodes = torrent_file.find_key("nodes");
|
||||
if (nodes && nodes->type() == entry::list_t)
|
||||
{
|
||||
entry::list_type const& list = i->list();
|
||||
entry::list_type const& list = nodes->list();
|
||||
for (entry::list_type::const_iterator i(list.begin())
|
||||
, end(list.end()); i != end; ++i)
|
||||
{
|
||||
|
@ -494,41 +558,40 @@ namespace libtorrent
|
|||
entry::list_type const& l = i->list();
|
||||
entry::list_type::const_iterator iter = l.begin();
|
||||
if (l.size() < 1) continue;
|
||||
if (iter->type() != entry::string_t) continue;
|
||||
std::string const& hostname = iter->string();
|
||||
++iter;
|
||||
int port = 6881;
|
||||
if (iter->type() != entry::int_t) continue;
|
||||
if (l.end() != iter) port = int(iter->integer());
|
||||
m_nodes.push_back(std::make_pair(hostname, port));
|
||||
}
|
||||
}
|
||||
|
||||
// extract creation date
|
||||
try
|
||||
entry const* creation_date = torrent_file.find_key("creation date");
|
||||
if (creation_date && creation_date->type() == entry::int_t)
|
||||
{
|
||||
m_creation_date = pt::ptime(gr::date(1970, gr::Jan, 1))
|
||||
+ pt::seconds(long(torrent_file["creation date"].integer()));
|
||||
+ pt::seconds(long(creation_date->integer()));
|
||||
}
|
||||
catch (type_error) {}
|
||||
|
||||
// if there are any url-seeds, extract them
|
||||
try
|
||||
entry const* url_seeds = torrent_file.find_key("url-list");
|
||||
if (url_seeds && url_seeds->type() == entry::string_t)
|
||||
{
|
||||
entry const& url_seeds = torrent_file["url-list"];
|
||||
if (url_seeds.type() == entry::string_t)
|
||||
m_url_seeds.push_back(url_seeds->string());
|
||||
}
|
||||
else if (url_seeds && url_seeds->type() == entry::list_t)
|
||||
{
|
||||
entry::list_type const& l = url_seeds->list();
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
m_url_seeds.push_back(url_seeds.string());
|
||||
}
|
||||
else if (url_seeds.type() == entry::list_t)
|
||||
{
|
||||
entry::list_type const& l = url_seeds.list();
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
m_url_seeds.push_back(i->string());
|
||||
}
|
||||
if (i->type() != entry::string_t) continue;
|
||||
m_url_seeds.push_back(i->string());
|
||||
}
|
||||
}
|
||||
catch (type_error&) {}
|
||||
|
||||
// extract comment
|
||||
if (entry const* e = torrent_file.find_key("comment.utf-8"))
|
||||
|
@ -541,7 +604,13 @@ namespace libtorrent
|
|||
else if (entry const* e = torrent_file.find_key("created by"))
|
||||
{ m_created_by = e->string(); }
|
||||
|
||||
parse_info_section(torrent_file["info"]);
|
||||
entry const* info = torrent_file.find_key("info");
|
||||
if (info == 0 || info->type() != entry::dictionary_t)
|
||||
{
|
||||
error = "missing or invalid 'info' section in torrent file";
|
||||
return false;
|
||||
}
|
||||
return parse_info_section(*info, error);
|
||||
}
|
||||
|
||||
boost::optional<pt::ptime>
|
||||
|
@ -822,8 +891,8 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(index >= 0 && index < num_pieces());
|
||||
if (index == num_pieces()-1)
|
||||
{
|
||||
size_type size = total_size()
|
||||
- (num_pieces() - 1) * piece_length();
|
||||
int size = int(total_size()
|
||||
- (num_pieces() - 1) * piece_length());
|
||||
TORRENT_ASSERT(size > 0);
|
||||
TORRENT_ASSERT(size <= piece_length());
|
||||
return int(size);
|
||||
|
@ -934,3 +1003,4 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
|
@ -63,237 +61,14 @@ namespace
|
|||
http_buffer_size = 2048
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
FTEXT = 0x01,
|
||||
FHCRC = 0x02,
|
||||
FEXTRA = 0x04,
|
||||
FNAME = 0x08,
|
||||
FCOMMENT = 0x10,
|
||||
FRESERVED = 0xe0,
|
||||
|
||||
GZIP_MAGIC0 = 0x1f,
|
||||
GZIP_MAGIC1 = 0x8b
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
// returns -1 if gzip header is invalid or the header size in bytes
|
||||
int gzip_header(const char* buf, int size)
|
||||
{
|
||||
TORRENT_ASSERT(buf != 0);
|
||||
TORRENT_ASSERT(size > 0);
|
||||
|
||||
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
|
||||
const int total_size = size;
|
||||
|
||||
// The zip header cannot be shorter than 10 bytes
|
||||
if (size < 10) return -1;
|
||||
|
||||
// check the magic header of gzip
|
||||
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
|
||||
|
||||
int method = buffer[2];
|
||||
int flags = buffer[3];
|
||||
|
||||
// check for reserved flag and make sure it's compressed with the correct metod
|
||||
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
|
||||
|
||||
// skip time, xflags, OS code
|
||||
size -= 10;
|
||||
buffer += 10;
|
||||
|
||||
if (flags & FEXTRA)
|
||||
{
|
||||
int extra_len;
|
||||
|
||||
if (size < 2) return -1;
|
||||
|
||||
extra_len = (buffer[1] << 8) | buffer[0];
|
||||
|
||||
if (size < (extra_len+2)) return -1;
|
||||
size -= (extra_len + 2);
|
||||
buffer += (extra_len + 2);
|
||||
}
|
||||
|
||||
if (flags & FNAME)
|
||||
{
|
||||
while (size && *buffer)
|
||||
{
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
if (!size || *buffer) return -1;
|
||||
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
|
||||
if (flags & FCOMMENT)
|
||||
{
|
||||
while (size && *buffer)
|
||||
{
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
if (!size || *buffer) return -1;
|
||||
|
||||
--size;
|
||||
++buffer;
|
||||
}
|
||||
|
||||
if (flags & FHCRC)
|
||||
{
|
||||
if (size < 2) return -1;
|
||||
|
||||
size -= 2;
|
||||
buffer += 2;
|
||||
}
|
||||
|
||||
return total_size - size;
|
||||
}
|
||||
|
||||
bool inflate_gzip(
|
||||
std::vector<char>& buffer
|
||||
, tracker_request const& req
|
||||
, request_callback* requester
|
||||
, int maximum_tracker_response_length)
|
||||
{
|
||||
TORRENT_ASSERT(maximum_tracker_response_length > 0);
|
||||
|
||||
int header_len = gzip_header(&buffer[0], (int)buffer.size());
|
||||
if (header_len < 0)
|
||||
{
|
||||
requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
|
||||
return true;
|
||||
}
|
||||
|
||||
// start off wth one kilobyte and grow
|
||||
// if needed
|
||||
std::vector<char> inflate_buffer(1024);
|
||||
|
||||
// initialize the zlib-stream
|
||||
z_stream str;
|
||||
|
||||
// subtract 8 from the end of the buffer since that's CRC32 and input size
|
||||
// and those belong to the gzip file
|
||||
str.avail_in = (int)buffer.size() - header_len - 8;
|
||||
str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
|
||||
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
|
||||
str.avail_out = (int)inflate_buffer.size();
|
||||
str.zalloc = Z_NULL;
|
||||
str.zfree = Z_NULL;
|
||||
str.opaque = 0;
|
||||
// -15 is really important. It will make inflate() not look for a zlib header
|
||||
// and just deflate the buffer
|
||||
if (inflateInit2(&str, -15) != Z_OK)
|
||||
{
|
||||
requester->tracker_request_error(req, 200, "gzip out of memory");
|
||||
return true;
|
||||
}
|
||||
|
||||
// inflate and grow inflate_buffer as needed
|
||||
int ret = inflate(&str, Z_SYNC_FLUSH);
|
||||
while (ret == Z_OK)
|
||||
{
|
||||
if (str.avail_out == 0)
|
||||
{
|
||||
if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
|
||||
{
|
||||
inflateEnd(&str);
|
||||
requester->tracker_request_error(req, 200
|
||||
, "tracker response too large");
|
||||
return true;
|
||||
}
|
||||
int new_size = (int)inflate_buffer.size() * 2;
|
||||
if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
|
||||
int old_size = (int)inflate_buffer.size();
|
||||
|
||||
inflate_buffer.resize(new_size);
|
||||
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
|
||||
str.avail_out = new_size - old_size;
|
||||
}
|
||||
|
||||
ret = inflate(&str, Z_SYNC_FLUSH);
|
||||
}
|
||||
|
||||
inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
|
||||
inflateEnd(&str);
|
||||
|
||||
if (ret != Z_STREAM_END)
|
||||
{
|
||||
requester->tracker_request_error(req, 200, "gzip error");
|
||||
return true;
|
||||
}
|
||||
|
||||
// commit the resulting buffer
|
||||
std::swap(buffer, inflate_buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string base64encode(const std::string& s)
|
||||
{
|
||||
static const char base64_table[] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
unsigned char inbuf[3];
|
||||
unsigned char outbuf[4];
|
||||
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = s.begin(); i != s.end();)
|
||||
{
|
||||
// available input is 1,2 or 3 bytes
|
||||
// since we read 3 bytes at a time at most
|
||||
int available_input = (std::min)(3, (int)std::distance(i, s.end()));
|
||||
|
||||
// clear input buffer
|
||||
std::fill(inbuf, inbuf+3, 0);
|
||||
|
||||
// read a chunk of input into inbuf
|
||||
for (int j = 0; j < available_input; ++j)
|
||||
{
|
||||
inbuf[j] = *i;
|
||||
++i;
|
||||
}
|
||||
|
||||
// encode inbuf to outbuf
|
||||
outbuf[0] = (inbuf[0] & 0xfc) >> 2;
|
||||
outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
|
||||
outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
|
||||
outbuf[3] = inbuf[2] & 0x3f;
|
||||
|
||||
// write output
|
||||
for (int j = 0; j < available_input+1; ++j)
|
||||
{
|
||||
ret += base64_table[outbuf[j]];
|
||||
}
|
||||
|
||||
// write pad
|
||||
for (int j = 0; j < 3 - available_input; ++j)
|
||||
{
|
||||
ret += '=';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
timeout_handler::timeout_handler(asio::strand& str)
|
||||
: m_strand(str)
|
||||
, m_start_time(time_now())
|
||||
timeout_handler::timeout_handler(io_service& ios)
|
||||
: m_start_time(time_now())
|
||||
, m_read_time(time_now())
|
||||
, m_timeout(str.io_service())
|
||||
, m_timeout(ios)
|
||||
, m_completion_timeout(0)
|
||||
, m_read_timeout(0)
|
||||
, m_abort(false)
|
||||
|
@ -309,9 +84,10 @@ namespace libtorrent
|
|||
|
||||
int timeout = (std::min)(
|
||||
m_read_timeout, (std::min)(m_completion_timeout, m_read_timeout));
|
||||
m_timeout.expires_at(m_read_time + seconds(timeout));
|
||||
m_timeout.async_wait(m_strand.wrap(bind(
|
||||
&timeout_handler::timeout_callback, self(), _1)));
|
||||
asio::error_code ec;
|
||||
m_timeout.expires_at(m_read_time + seconds(timeout), ec);
|
||||
m_timeout.async_wait(bind(
|
||||
&timeout_handler::timeout_callback, self(), _1));
|
||||
}
|
||||
|
||||
void timeout_handler::restart_read_timeout()
|
||||
|
@ -323,10 +99,11 @@ namespace libtorrent
|
|||
{
|
||||
m_abort = true;
|
||||
m_completion_timeout = 0;
|
||||
m_timeout.cancel();
|
||||
asio::error_code ec;
|
||||
m_timeout.cancel(ec);
|
||||
}
|
||||
|
||||
void timeout_handler::timeout_callback(asio::error_code const& error) try
|
||||
void timeout_handler::timeout_callback(asio::error_code const& error)
|
||||
{
|
||||
if (error) return;
|
||||
if (m_completion_timeout == 0) return;
|
||||
|
@ -348,22 +125,19 @@ namespace libtorrent
|
|||
|
||||
int timeout = (std::min)(
|
||||
m_read_timeout, (std::min)(m_completion_timeout, m_read_timeout));
|
||||
m_timeout.expires_at(m_read_time + seconds(timeout));
|
||||
m_timeout.async_wait(m_strand.wrap(
|
||||
bind(&timeout_handler::timeout_callback, self(), _1)));
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
TORRENT_ASSERT(false);
|
||||
asio::error_code ec;
|
||||
m_timeout.expires_at(m_read_time + seconds(timeout), ec);
|
||||
m_timeout.async_wait(
|
||||
bind(&timeout_handler::timeout_callback, self(), _1));
|
||||
}
|
||||
|
||||
tracker_connection::tracker_connection(
|
||||
tracker_manager& man
|
||||
, tracker_request const& req
|
||||
, asio::strand& str
|
||||
, io_service& ios
|
||||
, address bind_interface_
|
||||
, boost::weak_ptr<request_callback> r)
|
||||
: timeout_handler(str)
|
||||
: timeout_handler(ios)
|
||||
, m_requester(r)
|
||||
, m_bind_interface(bind_interface_)
|
||||
, m_man(man)
|
||||
|
@ -412,9 +186,13 @@ namespace libtorrent
|
|||
{
|
||||
std::string hostname; // hostname only
|
||||
std::string auth; // user:pass
|
||||
std::string protocol; // should be http
|
||||
std::string protocol; // http or https for instance
|
||||
int port = 80;
|
||||
|
||||
std::string::iterator at;
|
||||
std::string::iterator colon;
|
||||
std::string::iterator port_pos;
|
||||
|
||||
// PARSE URL
|
||||
std::string::iterator start = url.begin();
|
||||
// remove white spaces in front of the url
|
||||
|
@ -424,18 +202,20 @@ namespace libtorrent
|
|||
= std::find(url.begin(), url.end(), ':');
|
||||
protocol.assign(start, end);
|
||||
|
||||
if (end == url.end()) throw std::runtime_error("invalid url");
|
||||
if (protocol == "https") port = 443;
|
||||
|
||||
if (end == url.end()) goto exit;
|
||||
++end;
|
||||
if (end == url.end()) throw std::runtime_error("invalid url");
|
||||
if (*end != '/') throw std::runtime_error("invalid url");
|
||||
if (end == url.end()) goto exit;
|
||||
if (*end != '/') goto exit;
|
||||
++end;
|
||||
if (end == url.end()) throw std::runtime_error("invalid url");
|
||||
if (*end != '/') throw std::runtime_error("invalid url");
|
||||
if (end == url.end()) goto exit;
|
||||
if (*end != '/') goto exit;
|
||||
++end;
|
||||
start = end;
|
||||
|
||||
std::string::iterator at = std::find(start, url.end(), '@');
|
||||
std::string::iterator colon = std::find(start, url.end(), ':');
|
||||
at = std::find(start, url.end(), '@');
|
||||
colon = std::find(start, url.end(), ':');
|
||||
end = std::find(start, url.end(), '/');
|
||||
|
||||
if (at != url.end()
|
||||
|
@ -448,13 +228,11 @@ namespace libtorrent
|
|||
++start;
|
||||
}
|
||||
|
||||
std::string::iterator port_pos;
|
||||
|
||||
// this is for IPv6 addresses
|
||||
if (start != url.end() && *start == '[')
|
||||
{
|
||||
port_pos = std::find(start, url.end(), ']');
|
||||
if (port_pos == url.end()) throw std::runtime_error("invalid hostname syntax");
|
||||
if (port_pos == url.end()) goto exit;
|
||||
port_pos = std::find(port_pos, url.end(), ':');
|
||||
}
|
||||
else
|
||||
|
@ -466,15 +244,7 @@ namespace libtorrent
|
|||
{
|
||||
hostname.assign(start, port_pos);
|
||||
++port_pos;
|
||||
try
|
||||
{
|
||||
port = boost::lexical_cast<int>(std::string(port_pos, end));
|
||||
}
|
||||
catch(boost::bad_lexical_cast&)
|
||||
{
|
||||
throw std::runtime_error("invalid url: \"" + url
|
||||
+ "\", port number expected");
|
||||
}
|
||||
port = atoi(std::string(port_pos, end).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -482,12 +252,13 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
start = end;
|
||||
exit:
|
||||
return make_tuple(protocol, auth, hostname, port
|
||||
, std::string(start, url.end()));
|
||||
}
|
||||
|
||||
void tracker_manager::queue_request(
|
||||
asio::strand& str
|
||||
io_service& ios
|
||||
, connection_queue& cc
|
||||
, tracker_request req
|
||||
, std::string const& auth
|
||||
|
@ -503,63 +274,38 @@ namespace libtorrent
|
|||
if (m_abort && req.event != tracker_request::stopped)
|
||||
return;
|
||||
|
||||
try
|
||||
std::string protocol = req.url.substr(0, req.url.find(':'));
|
||||
|
||||
boost::intrusive_ptr<tracker_connection> con;
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (protocol == "http" || protocol == "https")
|
||||
#else
|
||||
if (protocol == "http")
|
||||
#endif
|
||||
{
|
||||
std::string protocol;
|
||||
std::string hostname;
|
||||
int port;
|
||||
std::string request_string;
|
||||
|
||||
using boost::tuples::ignore;
|
||||
// TODO: should auth be used here?
|
||||
boost::tie(protocol, ignore, hostname, port, request_string)
|
||||
= parse_url_components(req.url);
|
||||
|
||||
boost::intrusive_ptr<tracker_connection> con;
|
||||
|
||||
if (protocol == "http")
|
||||
{
|
||||
con = new http_tracker_connection(
|
||||
str
|
||||
, cc
|
||||
, *this
|
||||
, req
|
||||
, hostname
|
||||
, port
|
||||
, request_string
|
||||
, bind_infc
|
||||
, c
|
||||
, m_settings
|
||||
, m_proxy
|
||||
, auth);
|
||||
}
|
||||
else if (protocol == "udp")
|
||||
{
|
||||
con = new udp_tracker_connection(
|
||||
str
|
||||
, *this
|
||||
, req
|
||||
, hostname
|
||||
, port
|
||||
, bind_infc
|
||||
, c
|
||||
, m_settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("unkown protocol in tracker url");
|
||||
}
|
||||
|
||||
m_connections.push_back(con);
|
||||
|
||||
boost::shared_ptr<request_callback> cb = con->requester();
|
||||
if (cb) cb->m_manager = this;
|
||||
con = new http_tracker_connection(
|
||||
ios, cc, *this, req, bind_infc, c
|
||||
, m_settings, m_proxy, auth);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
else if (protocol == "udp")
|
||||
{
|
||||
con = new udp_tracker_connection(
|
||||
ios, cc, *this, req, bind_infc
|
||||
, c, m_settings, m_proxy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (boost::shared_ptr<request_callback> r = c.lock())
|
||||
r->tracker_request_error(req, -1, e.what());
|
||||
r->tracker_request_error(req, -1, "unknown protocol in tracker url: "
|
||||
+ req.url);
|
||||
return;
|
||||
}
|
||||
|
||||
m_connections.push_back(con);
|
||||
|
||||
boost::shared_ptr<request_callback> cb = con->requester();
|
||||
if (cb) cb->m_manager = this;
|
||||
}
|
||||
|
||||
void tracker_manager::abort_all_requests()
|
||||
|
@ -589,6 +335,11 @@ namespace libtorrent
|
|||
}
|
||||
// close will remove the entry from m_connections
|
||||
// so no need to pop
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
boost::shared_ptr<request_callback> rc = c->requester();
|
||||
if (rc) rc->debug_log("aborting: " + req.url);
|
||||
#endif
|
||||
c->close();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,435 @@
|
|||
#include "libtorrent/udp_socket.hpp"
|
||||
#include "libtorrent/connection_queue.hpp"
|
||||
#include <stdlib.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <asio/read.hpp>
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
udp_socket::udp_socket(asio::io_service& ios, udp_socket::callback_t const& c
|
||||
, connection_queue& cc)
|
||||
: m_callback(c)
|
||||
, m_ipv4_sock(ios)
|
||||
, m_ipv6_sock(ios)
|
||||
, m_bind_port(0)
|
||||
, m_socks5_sock(ios)
|
||||
, m_connection_ticket(-1)
|
||||
, m_cc(cc)
|
||||
, m_resolver(ios)
|
||||
, m_tunnel_packets(false)
|
||||
{
|
||||
}
|
||||
|
||||
void udp_socket::send(udp::endpoint const& ep, char const* p, int len, asio::error_code& ec)
|
||||
{
|
||||
if (m_tunnel_packets)
|
||||
{
|
||||
// send udp packets through SOCKS5 server
|
||||
wrap(ep, p, len, ec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ep.address().is_v4() && m_ipv4_sock.is_open())
|
||||
m_ipv4_sock.send_to(asio::buffer(p, len), ep, 0, ec);
|
||||
else
|
||||
m_ipv6_sock.send_to(asio::buffer(p, len), ep, 0, ec);
|
||||
}
|
||||
|
||||
void udp_socket::on_read(udp::socket* s, asio::error_code const& e, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!m_callback) return;
|
||||
|
||||
if (e)
|
||||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
if (s == &m_ipv4_sock)
|
||||
m_callback(e, m_v4_ep, 0, 0);
|
||||
else
|
||||
m_callback(e, m_v6_ep, 0, 0);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch(std::exception&) {}
|
||||
#endif
|
||||
|
||||
// don't stop listening on recoverable errors
|
||||
if (e != asio::error::host_unreachable
|
||||
&& e != asio::error::fault
|
||||
&& e != asio::error::connection_reset
|
||||
&& e != asio::error::connection_refused
|
||||
&& e != asio::error::connection_aborted
|
||||
&& e != asio::error::message_size)
|
||||
return;
|
||||
|
||||
if (s == &m_ipv4_sock)
|
||||
s->async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
else
|
||||
s->async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (s == &m_ipv4_sock)
|
||||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
|
||||
if (m_tunnel_packets && m_v4_ep == m_proxy_addr)
|
||||
unwrap(e, m_v4_buf, bytes_transferred);
|
||||
else
|
||||
m_callback(e, m_v4_ep, m_v4_buf, bytes_transferred);
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch(std::exception&) {}
|
||||
#endif
|
||||
s->async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
|
||||
if (m_tunnel_packets && m_v6_ep == m_proxy_addr)
|
||||
unwrap(e, m_v6_buf, bytes_transferred);
|
||||
else
|
||||
m_callback(e, m_v6_ep, m_v6_buf, bytes_transferred);
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch(std::exception&) {}
|
||||
#endif
|
||||
s->async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, asio::error_code& ec)
|
||||
{
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
char header[20];
|
||||
char* h = header;
|
||||
|
||||
write_uint16(0, h); // reserved
|
||||
write_uint8(0, h); // fragment
|
||||
write_uint8(ep.address().is_v4()?1:4, h); // atyp
|
||||
write_address(ep.address(), h);
|
||||
write_uint16(ep.port(), h);
|
||||
|
||||
boost::array<asio::const_buffer, 2> iovec;
|
||||
iovec[0] = asio::const_buffer(header, h - header);
|
||||
iovec[1] = asio::const_buffer(p, len);
|
||||
|
||||
if (m_proxy_addr.address().is_v4() && m_ipv4_sock.is_open())
|
||||
m_ipv4_sock.send_to(iovec, m_proxy_addr, 0, ec);
|
||||
else
|
||||
m_ipv6_sock.send_to(iovec, m_proxy_addr, 0, ec);
|
||||
}
|
||||
|
||||
// unwrap the UDP packet from the SOCKS5 header
|
||||
void udp_socket::unwrap(asio::error_code const& e, char const* buf, int size)
|
||||
{
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
// the minimum socks5 header size
|
||||
if (size <= 10) return;
|
||||
|
||||
char const* p = buf;
|
||||
p += 2; // reserved
|
||||
int frag = read_uint8(p);
|
||||
// fragmentation is not supported
|
||||
if (frag != 0) return;
|
||||
|
||||
udp::endpoint sender;
|
||||
|
||||
int atyp = read_uint8(p);
|
||||
if (atyp == 1)
|
||||
{
|
||||
// IPv4
|
||||
sender = read_v4_endpoint<udp::endpoint>(p);
|
||||
}
|
||||
else if (atyp == 4)
|
||||
{
|
||||
// IPv6
|
||||
sender = read_v6_endpoint<udp::endpoint>(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
// domain name not supported
|
||||
return;
|
||||
}
|
||||
|
||||
m_callback(e, sender, p, size - (p - buf));
|
||||
}
|
||||
|
||||
void udp_socket::close()
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_ipv4_sock.close(ec);
|
||||
m_ipv6_sock.close(ec);
|
||||
m_socks5_sock.close(ec);
|
||||
m_callback.clear();
|
||||
if (m_connection_ticket >= 0)
|
||||
{
|
||||
m_cc.done(m_connection_ticket);
|
||||
m_connection_ticket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void udp_socket::bind(udp::endpoint const& ep, asio::error_code& ec)
|
||||
{
|
||||
if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec);
|
||||
if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec);
|
||||
|
||||
if (ep.address().is_v4())
|
||||
{
|
||||
m_ipv4_sock.open(udp::v4(), ec);
|
||||
if (ec) return;
|
||||
m_ipv4_sock.bind(ep, ec);
|
||||
if (ec) return;
|
||||
m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, &m_ipv4_sock, _1, _2));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ipv6_sock.set_option(v6only(true), ec);
|
||||
if (ec) return;
|
||||
m_ipv6_sock.bind(ep, ec);
|
||||
if (ec) return;
|
||||
m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2));
|
||||
}
|
||||
m_bind_port = ep.port();
|
||||
}
|
||||
|
||||
void udp_socket::bind(int port)
|
||||
{
|
||||
asio::error_code ec;
|
||||
|
||||
if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec);
|
||||
if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec);
|
||||
|
||||
m_ipv4_sock.open(udp::v4(), ec);
|
||||
if (!ec)
|
||||
{
|
||||
m_ipv4_sock.bind(udp::endpoint(address_v4::any(), port), ec);
|
||||
m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, &m_ipv4_sock, _1, _2));
|
||||
}
|
||||
m_ipv6_sock.open(udp::v6(), ec);
|
||||
if (!ec)
|
||||
{
|
||||
m_ipv6_sock.set_option(v6only(true), ec);
|
||||
m_ipv6_sock.bind(udp::endpoint(address_v6::any(), port), ec);
|
||||
m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2));
|
||||
}
|
||||
m_bind_port = port;
|
||||
}
|
||||
|
||||
void udp_socket::set_proxy_settings(proxy_settings const& ps)
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_socks5_sock.close(ec);
|
||||
m_tunnel_packets = false;
|
||||
|
||||
m_proxy_settings = ps;
|
||||
|
||||
if (ps.type == proxy_settings::socks5
|
||||
|| ps.type == proxy_settings::socks5_pw)
|
||||
{
|
||||
// connect to socks5 server and open up the UDP tunnel
|
||||
tcp::resolver::query q(ps.hostname
|
||||
, boost::lexical_cast<std::string>(ps.port));
|
||||
m_resolver.async_resolve(q, boost::bind(
|
||||
&udp_socket::on_name_lookup, this, _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
void udp_socket::on_name_lookup(asio::error_code const& e, tcp::resolver::iterator i)
|
||||
{
|
||||
if (e) return;
|
||||
m_proxy_addr.address(i->endpoint().address());
|
||||
m_proxy_addr.port(i->endpoint().port());
|
||||
m_cc.enqueue(boost::bind(&udp_socket::on_connect, this, _1)
|
||||
, boost::bind(&udp_socket::on_timeout, this), seconds(10));
|
||||
}
|
||||
|
||||
void udp_socket::on_timeout()
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_socks5_sock.close(ec);
|
||||
m_connection_ticket = -1;
|
||||
}
|
||||
|
||||
void udp_socket::on_connect(int ticket)
|
||||
{
|
||||
m_connection_ticket = ticket;
|
||||
asio::error_code ec;
|
||||
m_socks5_sock.open(m_proxy_addr.address().is_v4()?tcp::v4():tcp::v6(), ec);
|
||||
m_socks5_sock.async_connect(tcp::endpoint(m_proxy_addr.address(), m_proxy_addr.port())
|
||||
, boost::bind(&udp_socket::on_connected, this, _1));
|
||||
}
|
||||
|
||||
void udp_socket::on_connected(asio::error_code const& e)
|
||||
{
|
||||
m_cc.done(m_connection_ticket);
|
||||
m_connection_ticket = -1;
|
||||
if (e) return;
|
||||
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
// send SOCKS5 authentication methods
|
||||
char* p = &m_tmp_buf[0];
|
||||
write_uint8(5, p); // SOCKS VERSION 5
|
||||
if (m_proxy_settings.username.empty()
|
||||
|| m_proxy_settings.type == proxy_settings::socks5)
|
||||
{
|
||||
write_uint8(1, p); // 1 authentication method (no auth)
|
||||
write_uint8(0, p); // no authentication
|
||||
}
|
||||
else
|
||||
{
|
||||
write_uint8(2, p); // 2 authentication methods
|
||||
write_uint8(0, p); // no authentication
|
||||
write_uint8(2, p); // username/password
|
||||
}
|
||||
asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf)
|
||||
, boost::bind(&udp_socket::handshake1, this, _1));
|
||||
}
|
||||
|
||||
void udp_socket::handshake1(asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 2)
|
||||
, boost::bind(&udp_socket::handshake2, this, _1));
|
||||
}
|
||||
|
||||
void udp_socket::handshake2(asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
char* p = &m_tmp_buf[0];
|
||||
int version = read_uint8(p);
|
||||
int method = read_uint8(p);
|
||||
|
||||
if (version < 5) return;
|
||||
|
||||
if (method == 0)
|
||||
{
|
||||
socks_forward_udp();
|
||||
}
|
||||
else if (method == 2)
|
||||
{
|
||||
if (m_proxy_settings.username.empty())
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_socks5_sock.close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
// start sub-negotiation
|
||||
char* p = &m_tmp_buf[0];
|
||||
write_uint8(1, p);
|
||||
write_uint8(m_proxy_settings.username.size(), p);
|
||||
write_string(m_proxy_settings.username, p);
|
||||
write_uint8(m_proxy_settings.password.size(), p);
|
||||
write_string(m_proxy_settings.password, p);
|
||||
asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf)
|
||||
, boost::bind(&udp_socket::handshake3, this, _1));
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_socks5_sock.close(ec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void udp_socket::handshake3(asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 2)
|
||||
, boost::bind(&udp_socket::handshake4, this, _1));
|
||||
}
|
||||
|
||||
void udp_socket::handshake4(asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
char* p = &m_tmp_buf[0];
|
||||
int version = read_uint8(p);
|
||||
int status = read_uint8(p);
|
||||
|
||||
if (version != 1) return;
|
||||
if (status != 0) return;
|
||||
|
||||
socks_forward_udp();
|
||||
}
|
||||
|
||||
void udp_socket::socks_forward_udp()
|
||||
{
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
// send SOCKS5 UDP command
|
||||
char* p = &m_tmp_buf[0];
|
||||
write_uint8(5, p); // SOCKS VERSION 5
|
||||
write_uint8(3, p); // UDP ASSOCIATE command
|
||||
write_uint8(0, p); // reserved
|
||||
write_uint8(0, p); // ATYP IPv4
|
||||
write_uint32(0, p); // IP any
|
||||
write_uint16(m_bind_port, p);
|
||||
|
||||
asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf)
|
||||
, boost::bind(&udp_socket::connect1, this, _1));
|
||||
}
|
||||
|
||||
void udp_socket::connect1(asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 10)
|
||||
, boost::bind(&udp_socket::connect2, this, _1));
|
||||
}
|
||||
|
||||
void udp_socket::connect2(asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
char* p = &m_tmp_buf[0];
|
||||
int version = read_uint8(p); // VERSION
|
||||
int status = read_uint8(p); // STATUS
|
||||
read_uint8(p); // RESERVED
|
||||
int atyp = read_uint8(p); // address type
|
||||
|
||||
if (version != 5) return;
|
||||
if (status != 0) return;
|
||||
|
||||
if (atyp == 1)
|
||||
{
|
||||
m_proxy_addr.address(address_v4(read_uint32(p)));
|
||||
m_proxy_addr.port(read_uint16(p));
|
||||
}
|
||||
else
|
||||
{
|
||||
// in this case we need to read more data from the socket
|
||||
TORRENT_ASSERT(false && "not implemented yet!");
|
||||
}
|
||||
|
||||
m_tunnel_packets = true;
|
||||
}
|
||||
|
|
@ -62,8 +62,7 @@ namespace
|
|||
udp_connection_retries = 4,
|
||||
udp_announce_retries = 15,
|
||||
udp_connect_timeout = 15,
|
||||
udp_announce_timeout = 10,
|
||||
udp_buffer_size = 2048
|
||||
udp_announce_timeout = 10
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -74,36 +73,48 @@ namespace libtorrent
|
|||
{
|
||||
|
||||
udp_tracker_connection::udp_tracker_connection(
|
||||
asio::strand& str
|
||||
io_service& ios
|
||||
, connection_queue& cc
|
||||
, tracker_manager& man
|
||||
, tracker_request const& req
|
||||
, std::string const& hostname
|
||||
, unsigned short port
|
||||
, address bind_infc
|
||||
, boost::weak_ptr<request_callback> c
|
||||
, session_settings const& stn)
|
||||
: tracker_connection(man, req, str, bind_infc, c)
|
||||
, session_settings const& stn
|
||||
, proxy_settings const& proxy)
|
||||
: tracker_connection(man, req, ios, bind_infc, c)
|
||||
, m_man(man)
|
||||
, m_strand(str)
|
||||
, m_name_lookup(m_strand.io_service())
|
||||
, m_socket(m_strand.io_service())
|
||||
, m_name_lookup(ios)
|
||||
, m_socket(ios, boost::bind(&udp_tracker_connection::on_receive, this, _1, _2, _3, _4), cc)
|
||||
, m_transaction_id(0)
|
||||
, m_connection_id(0)
|
||||
, m_settings(stn)
|
||||
, m_attempts(0)
|
||||
, m_state(action_error)
|
||||
{
|
||||
m_socket.set_proxy_settings(proxy);
|
||||
|
||||
std::string hostname;
|
||||
int port;
|
||||
|
||||
using boost::tuples::ignore;
|
||||
boost::tie(ignore, ignore, hostname, port, ignore) = parse_url_components(req.url);
|
||||
|
||||
udp::resolver::query q(hostname, boost::lexical_cast<std::string>(port));
|
||||
m_name_lookup.async_resolve(q
|
||||
, m_strand.wrap(boost::bind(
|
||||
&udp_tracker_connection::name_lookup, self(), _1, _2)));
|
||||
, boost::bind(
|
||||
&udp_tracker_connection::name_lookup, self(), _1, _2));
|
||||
set_timeout(req.event == tracker_request::stopped
|
||||
? m_settings.stop_tracker_timeout
|
||||
: m_settings.tracker_completion_timeout
|
||||
, m_settings.tracker_receive_timeout);
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb) cb->debug_log(("*** UDP_TRACKER [ initiating name lookup: " + hostname + " ]").c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void udp_tracker_connection::name_lookup(asio::error_code const& error
|
||||
, udp::resolver::iterator i) try
|
||||
, udp::resolver::iterator i)
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (error || i == udp::resolver::iterator())
|
||||
|
@ -113,8 +124,8 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (cb) cb->debug_log("udp tracker name lookup successful");
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
if (cb) cb->debug_log("*** UDP_TRACKER [ name lookup successful ]");
|
||||
#endif
|
||||
restart_read_timeout();
|
||||
|
||||
|
@ -145,20 +156,23 @@ namespace libtorrent
|
|||
|
||||
if (cb) cb->m_tracker_address = tcp::endpoint(target_address.address(), target_address.port());
|
||||
m_target = target_address;
|
||||
m_socket.open(target_address.protocol());
|
||||
m_socket.bind(udp::endpoint(bind_interface(), 0));
|
||||
m_socket.connect(target_address);
|
||||
asio::error_code ec;
|
||||
m_socket.bind(udp::endpoint(bind_interface(), 0), ec);
|
||||
if (ec)
|
||||
{
|
||||
fail(-1, ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
send_udp_connect();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
fail(-1, e.what());
|
||||
};
|
||||
|
||||
void udp_tracker_connection::on_timeout()
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_socket.close(ec);
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb) cb->debug_log("*** UDP_TRACKER [ timed out ]");
|
||||
#endif
|
||||
m_socket.close();
|
||||
m_name_lookup.cancel();
|
||||
fail_timeout();
|
||||
}
|
||||
|
@ -166,14 +180,110 @@ namespace libtorrent
|
|||
void udp_tracker_connection::close()
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_socket.close(ec);
|
||||
m_socket.close();
|
||||
m_name_lookup.cancel();
|
||||
tracker_connection::close();
|
||||
}
|
||||
|
||||
void udp_tracker_connection::on_receive(asio::error_code const& e
|
||||
, udp::endpoint const& ep, char const* buf, int size)
|
||||
{
|
||||
// ignore resposes before we've sent any requests
|
||||
if (m_state == action_error) return;
|
||||
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
|
||||
// ignore packet not sent from the tracker
|
||||
if (m_target != ep) return;
|
||||
|
||||
if (e) fail(-1, e.message().c_str());
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "<== UDP_TRACKER_PACKET [ size: " << size << " ]";
|
||||
cb->debug_log(msg.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// ignore packets smaller than 8 bytes
|
||||
if (size < 8) return;
|
||||
|
||||
restart_read_timeout();
|
||||
|
||||
const char* ptr = buf;
|
||||
int action = detail::read_int32(ptr);
|
||||
int transaction = detail::read_int32(ptr);
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
if (cb)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "*** UDP_TRACKER_PACKET [ acton: " << action << " ]";
|
||||
cb->debug_log(msg.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// ignore packets with incorrect transaction id
|
||||
if (m_transaction_id != transaction) return;
|
||||
|
||||
if (action == action_error)
|
||||
{
|
||||
fail(-1, std::string(ptr, size - 8).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore packets that's not a response to our message
|
||||
if (action != m_state) return;
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
if (cb)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "*** UDP_TRACKER_RESPONSE [ cid: " << m_connection_id << " ]";
|
||||
cb->debug_log(msg.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case action_connect:
|
||||
on_connect_response(buf, size);
|
||||
break;
|
||||
case action_announce:
|
||||
on_announce_response(buf, size);
|
||||
break;
|
||||
case action_scrape:
|
||||
on_scrape_response(buf, size);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void udp_tracker_connection::on_connect_response(char const* buf, int size)
|
||||
{
|
||||
// ignore packets smaller than 16 bytes
|
||||
if (size < 16) return;
|
||||
|
||||
restart_read_timeout();
|
||||
buf += 8; // skip header
|
||||
|
||||
// reset transaction
|
||||
m_transaction_id = 0;
|
||||
m_attempts = 0;
|
||||
m_connection_id = detail::read_int64(buf);
|
||||
|
||||
if (tracker_req().kind == tracker_request::announce_request)
|
||||
send_udp_announce();
|
||||
else if (tracker_req().kind == tracker_request::scrape_request)
|
||||
send_udp_scrape();
|
||||
}
|
||||
|
||||
void udp_tracker_connection::send_udp_connect()
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb)
|
||||
{
|
||||
|
@ -183,170 +293,27 @@ namespace libtorrent
|
|||
#endif
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
|
||||
char send_buf[16];
|
||||
char* ptr = send_buf;
|
||||
char buf[16];
|
||||
char* ptr = buf;
|
||||
|
||||
if (m_transaction_id == 0)
|
||||
m_transaction_id = rand() ^ (rand() << 16);
|
||||
|
||||
// connection_id
|
||||
detail::write_uint32(0x417, ptr);
|
||||
detail::write_uint32(0x27101980, ptr);
|
||||
// action (connect)
|
||||
detail::write_int32(action_connect, ptr);
|
||||
// transaction_id
|
||||
detail::write_int32(m_transaction_id, ptr);
|
||||
detail::write_uint32(0x27101980, ptr); // connection_id
|
||||
detail::write_int32(action_connect, ptr); // action (connect)
|
||||
detail::write_int32(m_transaction_id, ptr); // transaction_id
|
||||
TORRENT_ASSERT(ptr - buf == sizeof(buf));
|
||||
|
||||
m_socket.send(asio::buffer((void*)send_buf, 16), 0);
|
||||
asio::error_code ec;
|
||||
m_socket.send(m_target, buf, 16, ec);
|
||||
m_state = action_connect;
|
||||
++m_attempts;
|
||||
m_buffer.resize(udp_buffer_size);
|
||||
m_socket.async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
}
|
||||
|
||||
void udp_tracker_connection::connect_response(asio::error_code const& error
|
||||
, std::size_t bytes_transferred) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
if (error)
|
||||
if (ec)
|
||||
{
|
||||
fail(-1, error.message().c_str());
|
||||
fail(-1, ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_target != m_sender)
|
||||
{
|
||||
// this packet was not received from the tracker
|
||||
m_socket.async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred >= udp_buffer_size)
|
||||
{
|
||||
fail(-1, "udp response too big");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 8)
|
||||
{
|
||||
fail(-1, "got a message with size < 8");
|
||||
return;
|
||||
}
|
||||
|
||||
restart_read_timeout();
|
||||
|
||||
const char* ptr = &m_buffer[0];
|
||||
int action = detail::read_int32(ptr);
|
||||
int transaction = detail::read_int32(ptr);
|
||||
|
||||
if (action == action_error)
|
||||
{
|
||||
fail(-1, std::string(ptr, bytes_transferred - 8).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (action != action_connect)
|
||||
{
|
||||
fail(-1, "invalid action in connect reply");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_transaction_id != transaction)
|
||||
{
|
||||
fail(-1, "incorrect transaction id");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 16)
|
||||
{
|
||||
fail(-1, "udp_tracker_connection: "
|
||||
"got a message with size < 16");
|
||||
return;
|
||||
}
|
||||
// reset transaction
|
||||
m_transaction_id = 0;
|
||||
m_attempts = 0;
|
||||
m_connection_id = detail::read_int64(ptr);
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb)
|
||||
{
|
||||
cb->debug_log("<== UDP_TRACKER_CONNECT_RESPONSE ["
|
||||
+ lexical_cast<std::string>(m_connection_id) + "]");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tracker_req().kind == tracker_request::announce_request)
|
||||
send_udp_announce();
|
||||
else if (tracker_req().kind == tracker_request::scrape_request)
|
||||
send_udp_scrape();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
fail(-1, e.what());
|
||||
}
|
||||
|
||||
void udp_tracker_connection::send_udp_announce()
|
||||
{
|
||||
if (m_transaction_id == 0)
|
||||
m_transaction_id = rand() ^ (rand() << 16);
|
||||
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
|
||||
std::vector<char> buf;
|
||||
std::back_insert_iterator<std::vector<char> > out(buf);
|
||||
|
||||
tracker_request const& req = tracker_req();
|
||||
|
||||
// connection_id
|
||||
detail::write_int64(m_connection_id, out);
|
||||
// action (announce)
|
||||
detail::write_int32(action_announce, out);
|
||||
// transaction_id
|
||||
detail::write_int32(m_transaction_id, out);
|
||||
// info_hash
|
||||
std::copy(req.info_hash.begin(), req.info_hash.end(), out);
|
||||
// peer_id
|
||||
std::copy(req.pid.begin(), req.pid.end(), out);
|
||||
// downloaded
|
||||
detail::write_int64(req.downloaded, out);
|
||||
// left
|
||||
detail::write_int64(req.left, out);
|
||||
// uploaded
|
||||
detail::write_int64(req.uploaded, out);
|
||||
// event
|
||||
detail::write_int32(req.event, out);
|
||||
// ip address
|
||||
if (m_settings.announce_ip != address() && m_settings.announce_ip.is_v4())
|
||||
detail::write_uint32(m_settings.announce_ip.to_v4().to_ulong(), out);
|
||||
else
|
||||
detail::write_int32(0, out);
|
||||
// key
|
||||
detail::write_int32(req.key, out);
|
||||
// num_want
|
||||
detail::write_int32(req.num_want, out);
|
||||
// port
|
||||
detail::write_uint16(req.listen_port, out);
|
||||
// extensions
|
||||
detail::write_uint16(0, out);
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb)
|
||||
{
|
||||
cb->debug_log("==> UDP_TRACKER_ANNOUNCE ["
|
||||
+ lexical_cast<std::string>(req.info_hash) + "]");
|
||||
}
|
||||
#endif
|
||||
|
||||
m_socket.send(asio::buffer(buf), 0);
|
||||
++m_attempts;
|
||||
|
||||
m_socket.async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::announce_response, self(), _1, _2));
|
||||
}
|
||||
|
||||
void udp_tracker_connection::send_udp_scrape()
|
||||
|
@ -356,97 +323,48 @@ namespace libtorrent
|
|||
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
|
||||
std::vector<char> buf;
|
||||
std::back_insert_iterator<std::vector<char> > out(buf);
|
||||
char buf[8 + 4 + 4 + 20];
|
||||
char* out = buf;
|
||||
|
||||
// connection_id
|
||||
detail::write_int64(m_connection_id, out);
|
||||
// action (scrape)
|
||||
detail::write_int32(action_scrape, out);
|
||||
// transaction_id
|
||||
detail::write_int32(m_transaction_id, out);
|
||||
detail::write_int64(m_connection_id, out); // connection_id
|
||||
detail::write_int32(action_scrape, out); // action (scrape)
|
||||
detail::write_int32(m_transaction_id, out); // transaction_id
|
||||
// info_hash
|
||||
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out);
|
||||
out += 20;
|
||||
TORRENT_ASSERT(out - buf == sizeof(buf));
|
||||
|
||||
m_socket.send(asio::buffer(&buf[0], buf.size()), 0);
|
||||
asio::error_code ec;
|
||||
m_socket.send(m_target, buf, sizeof(buf), ec);
|
||||
m_state = action_scrape;
|
||||
++m_attempts;
|
||||
|
||||
m_socket.async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::scrape_response, self(), _1, _2));
|
||||
if (ec)
|
||||
{
|
||||
fail(-1, ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void udp_tracker_connection::announce_response(asio::error_code const& error
|
||||
, std::size_t bytes_transferred) try
|
||||
void udp_tracker_connection::on_announce_response(char const* buf, int size)
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_target != m_sender)
|
||||
{
|
||||
// this packet was not received from the tracker
|
||||
m_socket.async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred >= udp_buffer_size)
|
||||
{
|
||||
fail(-1, "udp response too big");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 8)
|
||||
{
|
||||
fail(-1, "got a message with size < 8");
|
||||
return;
|
||||
}
|
||||
if (size < 20) return;
|
||||
|
||||
restart_read_timeout();
|
||||
char* buf = &m_buffer[0];
|
||||
int action = detail::read_int32(buf);
|
||||
int transaction = detail::read_int32(buf);
|
||||
|
||||
if (transaction != m_transaction_id)
|
||||
{
|
||||
fail(-1, "incorrect transaction id");
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == action_error)
|
||||
{
|
||||
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (action != action_announce)
|
||||
{
|
||||
fail(-1, "invalid action in announce response");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 20)
|
||||
{
|
||||
fail(-1, "got a message with size < 20");
|
||||
return;
|
||||
}
|
||||
|
||||
buf += 8; // skip header
|
||||
restart_read_timeout();
|
||||
int interval = detail::read_int32(buf);
|
||||
int incomplete = detail::read_int32(buf);
|
||||
int complete = detail::read_int32(buf);
|
||||
int num_peers = (bytes_transferred - 20) / 6;
|
||||
if ((bytes_transferred - 20) % 6 != 0)
|
||||
int num_peers = (size - 20) / 6;
|
||||
if ((size - 20) % 6 != 0)
|
||||
{
|
||||
fail(-1, "invalid udp tracker response length");
|
||||
return;
|
||||
}
|
||||
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
if (cb)
|
||||
{
|
||||
cb->debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE");
|
||||
|
@ -462,6 +380,7 @@ namespace libtorrent
|
|||
std::vector<peer_entry> peer_list;
|
||||
for (int i = 0; i < num_peers; ++i)
|
||||
{
|
||||
// TODO: don't use a string here
|
||||
peer_entry e;
|
||||
std::stringstream s;
|
||||
s << (int)detail::read_uint8(buf) << ".";
|
||||
|
@ -475,50 +394,17 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
cb->tracker_response(tracker_req(), peer_list, interval
|
||||
, complete, incomplete);
|
||||
, complete, incomplete, address());
|
||||
|
||||
m_man.remove_request(this);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
||||
void udp_tracker_connection::on_scrape_response(char const* buf, int size)
|
||||
{
|
||||
fail(-1, e.what());
|
||||
}; // msvc 7.1 seems to require this
|
||||
|
||||
void udp_tracker_connection::scrape_response(asio::error_code const& error
|
||||
, std::size_t bytes_transferred) try
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_target != m_sender)
|
||||
{
|
||||
// this packet was not received from the tracker
|
||||
m_socket.async_receive_from(asio::buffer(m_buffer), m_sender
|
||||
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred >= udp_buffer_size)
|
||||
{
|
||||
fail(-1, "udp response too big");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 8)
|
||||
{
|
||||
fail(-1, "got a message with size < 8");
|
||||
return;
|
||||
}
|
||||
buf += 8; // skip header
|
||||
|
||||
restart_read_timeout();
|
||||
char* buf = &m_buffer[0];
|
||||
int action = detail::read_int32(buf);
|
||||
int transaction = detail::read_int32(buf);
|
||||
|
||||
|
@ -530,7 +416,7 @@ namespace libtorrent
|
|||
|
||||
if (action == action_error)
|
||||
{
|
||||
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
|
||||
fail(-1, std::string(buf, size - 8).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -540,7 +426,7 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
|
||||
if (bytes_transferred < 20)
|
||||
if (size < 20)
|
||||
{
|
||||
fail(-1, "got a message with size < 20");
|
||||
return;
|
||||
|
@ -553,7 +439,6 @@ namespace libtorrent
|
|||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (!cb)
|
||||
{
|
||||
m_man.remove_request(this);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
@ -564,9 +449,60 @@ namespace libtorrent
|
|||
m_man.remove_request(this);
|
||||
close();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
||||
void udp_tracker_connection::send_udp_announce()
|
||||
{
|
||||
fail(-1, e.what());
|
||||
if (m_transaction_id == 0)
|
||||
m_transaction_id = rand() ^ (rand() << 16);
|
||||
|
||||
if (!m_socket.is_open()) return; // the operation was aborted
|
||||
|
||||
char buf[8 + 4 + 4 + 20 + 20 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 2 + 2];
|
||||
char* out = buf;
|
||||
|
||||
tracker_request const& req = tracker_req();
|
||||
|
||||
detail::write_int64(m_connection_id, out); // connection_id
|
||||
detail::write_int32(action_announce, out); // action (announce)
|
||||
detail::write_int32(m_transaction_id, out); // transaction_id
|
||||
std::copy(req.info_hash.begin(), req.info_hash.end(), out); // info_hash
|
||||
out += 20;
|
||||
std::copy(req.pid.begin(), req.pid.end(), out); // peer_id
|
||||
out += 20;
|
||||
detail::write_int64(req.downloaded, out); // downloaded
|
||||
detail::write_int64(req.left, out); // left
|
||||
detail::write_int64(req.uploaded, out); // uploaded
|
||||
detail::write_int32(req.event, out); // event
|
||||
// ip address
|
||||
if (m_settings.announce_ip != address() && m_settings.announce_ip.is_v4())
|
||||
detail::write_uint32(m_settings.announce_ip.to_v4().to_ulong(), out);
|
||||
else
|
||||
detail::write_int32(0, out);
|
||||
detail::write_int32(req.key, out); // key
|
||||
detail::write_int32(req.num_want, out); // num_want
|
||||
detail::write_uint16(req.listen_port, out); // port
|
||||
detail::write_uint16(0, out); // extensions
|
||||
|
||||
TORRENT_ASSERT(out - buf == sizeof(buf));
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb)
|
||||
{
|
||||
cb->debug_log("==> UDP_TRACKER_ANNOUNCE ["
|
||||
+ lexical_cast<std::string>(req.info_hash) + "]");
|
||||
}
|
||||
#endif
|
||||
|
||||
asio::error_code ec;
|
||||
m_socket.send(m_target, buf, sizeof(buf), ec);
|
||||
m_state = action_announce;
|
||||
++m_attempts;
|
||||
if (ec)
|
||||
{
|
||||
fail(-1, ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,15 +63,12 @@ namespace libtorrent
|
|||
upnp::upnp(io_service& ios, connection_queue& cc
|
||||
, address const& listen_interface, std::string const& user_agent
|
||||
, portmap_callback_t const& cb, bool ignore_nonrouters)
|
||||
: m_udp_local_port(0)
|
||||
, m_tcp_local_port(0)
|
||||
, m_user_agent(user_agent)
|
||||
: m_user_agent(user_agent)
|
||||
, m_callback(cb)
|
||||
, m_retry_count(0)
|
||||
, m_io_service(ios)
|
||||
, m_strand(ios)
|
||||
, m_socket(ios, udp::endpoint(address_v4::from_string("239.255.255.250"), 1900)
|
||||
, m_strand.wrap(bind(&upnp::on_reply, self(), _1, _2, _3)), false)
|
||||
, bind(&upnp::on_reply, self(), _1, _2, _3), false)
|
||||
, m_broadcast_timer(ios)
|
||||
, m_refresh_timer(ios)
|
||||
, m_disabled(false)
|
||||
|
@ -89,7 +86,14 @@ upnp::~upnp()
|
|||
{
|
||||
}
|
||||
|
||||
void upnp::discover_device() try
|
||||
void upnp::discover_device()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
discover_device_impl();
|
||||
}
|
||||
|
||||
void upnp::discover_device_impl()
|
||||
{
|
||||
const char msearch[] =
|
||||
"M-SEARCH * HTTP/1.1\r\n"
|
||||
|
@ -113,73 +117,114 @@ void upnp::discover_device() try
|
|||
<< " ==> Broadcast FAILED: " << ec.message() << std::endl
|
||||
<< "aborting" << std::endl;
|
||||
#endif
|
||||
disable();
|
||||
disable(ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
++m_retry_count;
|
||||
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
|
||||
m_broadcast_timer.async_wait(m_strand.wrap(bind(&upnp::resend_request
|
||||
, self(), _1)));
|
||||
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
|
||||
m_broadcast_timer.async_wait(bind(&upnp::resend_request
|
||||
, self(), _1));
|
||||
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " ==> Broadcasting search for rootdevice" << std::endl;
|
||||
#endif
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
disable();
|
||||
};
|
||||
|
||||
void upnp::set_mappings(int tcp, int udp)
|
||||
// returns a reference to a mapping or -1 on failure
|
||||
int upnp::add_mapping(upnp::protocol_type p, int external_port, int local_port)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " *** set mappings " << tcp << " " << udp;
|
||||
<< " *** add mapping [ proto: " << (p == tcp?"tcp":"udp")
|
||||
<< " ext_port: " << external_port
|
||||
<< " local_port :" << local_port << " ]";
|
||||
if (m_disabled) m_log << " DISABLED";
|
||||
m_log << std::endl;
|
||||
#endif
|
||||
if (m_disabled) return -1;
|
||||
|
||||
if (m_disabled) return;
|
||||
if (udp != 0) m_udp_local_port = udp;
|
||||
if (tcp != 0) m_tcp_local_port = tcp;
|
||||
std::vector<global_mapping_t>::iterator i = std::find_if(
|
||||
m_mappings.begin(), m_mappings.end()
|
||||
, boost::bind(&global_mapping_t::protocol, _1) == int(none));
|
||||
|
||||
if (i == m_mappings.end())
|
||||
{
|
||||
m_mappings.push_back(global_mapping_t());
|
||||
i = m_mappings.end() - 1;
|
||||
}
|
||||
|
||||
i->protocol = p;
|
||||
i->external_port = external_port;
|
||||
i->local_port = local_port;
|
||||
|
||||
int mapping_index = i - m_mappings.begin();
|
||||
|
||||
for (std::set<rootdevice>::iterator i = m_devices.begin()
|
||||
, end(m_devices.end()); i != end; ++i)
|
||||
{
|
||||
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
if (d.mapping[0].local_port != m_tcp_local_port)
|
||||
{
|
||||
if (d.mapping[0].external_port == 0)
|
||||
d.mapping[0].external_port = m_tcp_local_port;
|
||||
d.mapping[0].local_port = m_tcp_local_port;
|
||||
d.mapping[0].need_update = true;
|
||||
}
|
||||
if (d.mapping[1].local_port != m_udp_local_port)
|
||||
{
|
||||
if (d.mapping[1].external_port == 0)
|
||||
d.mapping[1].external_port = m_udp_local_port;
|
||||
d.mapping[1].local_port = m_udp_local_port;
|
||||
d.mapping[1].need_update = true;
|
||||
}
|
||||
if (d.service_namespace
|
||||
&& (d.mapping[0].need_update || d.mapping[1].need_update))
|
||||
map_port(d, 0);
|
||||
|
||||
if (int(d.mapping.size()) <= mapping_index)
|
||||
d.mapping.resize(mapping_index + 1);
|
||||
mapping_t& m = d.mapping[mapping_index];
|
||||
|
||||
m.action = mapping_t::action_add;
|
||||
m.protocol = p;
|
||||
m.external_port = external_port;
|
||||
m.local_port = local_port;
|
||||
|
||||
if (d.service_namespace) update_map(d, mapping_index);
|
||||
}
|
||||
|
||||
return mapping_index;
|
||||
}
|
||||
|
||||
void upnp::delete_mapping(int mapping)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
if (mapping <= int(m_mappings.size())) return;
|
||||
|
||||
global_mapping_t& m = m_mappings[mapping];
|
||||
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " *** delete mapping [ proto: " << (m.protocol == tcp?"tcp":"udp")
|
||||
<< " ext_port:" << m.external_port
|
||||
<< " local_port:" << m.local_port << " ]";
|
||||
m_log << std::endl;
|
||||
#endif
|
||||
|
||||
if (m.protocol == none) return;
|
||||
|
||||
for (std::set<rootdevice>::iterator i = m_devices.begin()
|
||||
, end(m_devices.end()); i != end; ++i)
|
||||
{
|
||||
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
|
||||
TORRENT_ASSERT(mapping < int(d.mapping.size()));
|
||||
d.mapping[mapping].action = mapping_t::action_delete;
|
||||
|
||||
if (d.service_namespace) update_map(d, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
void upnp::resend_request(asio::error_code const& e)
|
||||
#ifndef NDEBUG
|
||||
try
|
||||
#endif
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
if (m_retry_count < 9
|
||||
&& (m_devices.empty() || m_retry_count < 4))
|
||||
{
|
||||
discover_device();
|
||||
discover_device_impl();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -190,7 +235,7 @@ try
|
|||
<< " *** Got no response in 9 retries. Giving up, "
|
||||
"disabling UPnP." << std::endl;
|
||||
#endif
|
||||
disable();
|
||||
disable("no UPnP router found");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -210,9 +255,9 @@ try
|
|||
<< " ==> connecting to " << d.url << std::endl;
|
||||
#endif
|
||||
d.upnp_connection.reset(new http_connection(m_io_service
|
||||
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2
|
||||
, boost::ref(d)))));
|
||||
d.upnp_connection->get(d.url);
|
||||
, m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2
|
||||
, boost::ref(d))));
|
||||
d.upnp_connection->get(d.url, seconds(30), 1);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
@ -227,19 +272,12 @@ try
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
catch (std::exception&)
|
||||
{
|
||||
TORRENT_ASSERT(false);
|
||||
};
|
||||
#endif
|
||||
|
||||
void upnp::on_reply(udp::endpoint const& from, char* buffer
|
||||
, std::size_t bytes_transferred)
|
||||
#ifndef NDEBUG
|
||||
try
|
||||
#endif
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
using namespace libtorrent::detail;
|
||||
|
||||
// parse out the url for the device
|
||||
|
@ -296,17 +334,14 @@ try
|
|||
}
|
||||
|
||||
http_parser p;
|
||||
try
|
||||
bool error = false;
|
||||
p.incoming(buffer::const_interval(buffer
|
||||
, buffer + bytes_transferred), error);
|
||||
if (error)
|
||||
{
|
||||
p.incoming(buffer::const_interval(buffer
|
||||
, buffer + bytes_transferred));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
(void)e;
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " <== (" << from << ") Rootdevice responded with incorrect HTTP packet. Ignoring device (" << e.what() << ")" << std::endl;
|
||||
m_log << time_now_string() << " <== (" << from << ") Rootdevice "
|
||||
"responded with incorrect HTTP packet. Ignoring device" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -399,25 +434,16 @@ try
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_tcp_local_port != 0)
|
||||
TORRENT_ASSERT(d.mapping.empty());
|
||||
for (std::vector<global_mapping_t>::iterator j = m_mappings.begin()
|
||||
, end(m_mappings.end()); j != end; ++j)
|
||||
{
|
||||
d.mapping[0].need_update = true;
|
||||
d.mapping[0].local_port = m_tcp_local_port;
|
||||
if (d.mapping[0].external_port == 0)
|
||||
d.mapping[0].external_port = d.mapping[0].local_port;
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string() << " *** Mapping 0 will be updated" << std::endl;
|
||||
#endif
|
||||
}
|
||||
if (m_udp_local_port != 0)
|
||||
{
|
||||
d.mapping[1].need_update = true;
|
||||
d.mapping[1].local_port = m_udp_local_port;
|
||||
if (d.mapping[1].external_port == 0)
|
||||
d.mapping[1].external_port = d.mapping[1].local_port;
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string() << " *** Mapping 1 will be updated" << std::endl;
|
||||
#endif
|
||||
mapping_t m;
|
||||
m.action = mapping_t::action_add;
|
||||
m.local_port = j->local_port;
|
||||
m.external_port = j->external_port;
|
||||
m.protocol = j->protocol;
|
||||
d.mapping.push_back(m);
|
||||
}
|
||||
boost::tie(i, boost::tuples::ignore) = m_devices.insert(d);
|
||||
}
|
||||
|
@ -427,7 +453,8 @@ try
|
|||
// just to make sure we find all devices
|
||||
if (m_retry_count >= 4 && !m_devices.empty())
|
||||
{
|
||||
m_broadcast_timer.cancel();
|
||||
asio::error_code ec;
|
||||
m_broadcast_timer.cancel(ec);
|
||||
|
||||
for (std::set<rootdevice>::iterator i = m_devices.begin()
|
||||
, end(m_devices.end()); i != end; ++i)
|
||||
|
@ -438,16 +465,19 @@ try
|
|||
// ask for it
|
||||
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
#endif
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " ==> connecting to " << d.url << std::endl;
|
||||
#endif
|
||||
d.upnp_connection.reset(new http_connection(m_io_service
|
||||
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2
|
||||
, boost::ref(d)))));
|
||||
d.upnp_connection->get(d.url);
|
||||
, m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2
|
||||
, boost::ref(d))));
|
||||
d.upnp_connection->get(d.url, seconds(30), 1);
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
@ -459,16 +489,11 @@ try
|
|||
#endif
|
||||
d.disabled = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
catch (std::exception&)
|
||||
{
|
||||
TORRENT_ASSERT(false);
|
||||
};
|
||||
#endif
|
||||
|
||||
void upnp::post(upnp::rootdevice const& d, std::string const& soap
|
||||
, std::string const& soap_action)
|
||||
|
@ -495,6 +520,8 @@ void upnp::post(upnp::rootdevice const& d, std::string const& soap
|
|||
|
||||
void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
|
||||
if (!d.upnp_connection)
|
||||
|
@ -516,11 +543,12 @@ void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
|
|||
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
"<s:Body><u:" << soap_action << " xmlns:u=\"" << d.service_namespace << "\">";
|
||||
|
||||
asio::error_code ec;
|
||||
soap << "<NewRemoteHost></NewRemoteHost>"
|
||||
"<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>"
|
||||
"<NewProtocol>" << (d.mapping[i].protocol ? "UDP" : "TCP") << "</NewProtocol>"
|
||||
"<NewInternalPort>" << d.mapping[i].local_port << "</NewInternalPort>"
|
||||
"<NewInternalClient>" << c.socket().local_endpoint().address().to_string() << "</NewInternalClient>"
|
||||
"<NewInternalClient>" << c.socket().local_endpoint(ec).address() << "</NewInternalClient>"
|
||||
"<NewEnabled>1</NewEnabled>"
|
||||
"<NewPortMappingDescription>" << m_user_agent << "</NewPortMappingDescription>"
|
||||
"<NewLeaseDuration>" << d.lease_duration << "</NewLeaseDuration>";
|
||||
|
@ -529,22 +557,45 @@ void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
|
|||
post(d, soap.str(), soap_action);
|
||||
}
|
||||
|
||||
void upnp::map_port(rootdevice& d, int i)
|
||||
void upnp::next(rootdevice& d, int i)
|
||||
{
|
||||
if (i < num_mappings() - 1)
|
||||
{
|
||||
update_map(d, i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<mapping_t>::iterator i
|
||||
= std::find_if(d.mapping.begin(), d.mapping.end()
|
||||
, boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none));
|
||||
if (i == d.mapping.end()) return;
|
||||
|
||||
update_map(d, i - d.mapping.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void upnp::update_map(rootdevice& d, int i)
|
||||
{
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
TORRENT_ASSERT(i < int(d.mapping.size()));
|
||||
TORRENT_ASSERT(d.mapping.size() == m_mappings.size());
|
||||
|
||||
if (d.upnp_connection) return;
|
||||
|
||||
if (!d.mapping[i].need_update)
|
||||
mapping_t& m = d.mapping[i];
|
||||
|
||||
if (m.action == mapping_t::action_none
|
||||
|| m.protocol == none)
|
||||
{
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string() << " *** mapping (" << i
|
||||
<< ") does not need update, skipping" << std::endl;
|
||||
if (m.protocol != none)
|
||||
m_log << time_now_string() << " *** mapping (" << i
|
||||
<< ") does not need update, skipping" << std::endl;
|
||||
#endif
|
||||
if (i < num_mappings - 1)
|
||||
map_port(d, i + 1);
|
||||
next(d, i);
|
||||
return;
|
||||
}
|
||||
d.mapping[i].need_update = false;
|
||||
|
||||
TORRENT_ASSERT(!d.upnp_connection);
|
||||
TORRENT_ASSERT(d.service_namespace);
|
||||
|
||||
|
@ -552,17 +603,40 @@ void upnp::map_port(rootdevice& d, int i)
|
|||
m_log << time_now_string()
|
||||
<< " ==> connecting to " << d.hostname << std::endl;
|
||||
#endif
|
||||
d.upnp_connection.reset(new http_connection(m_io_service
|
||||
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, self(), _1, _2
|
||||
, boost::ref(d), i)), true
|
||||
, bind(&upnp::create_port_mapping, self(), _1, boost::ref(d), i)));
|
||||
if (m.action == mapping_t::action_add)
|
||||
{
|
||||
if (m.failcount > 5)
|
||||
{
|
||||
// giving up
|
||||
next(d, i);
|
||||
return;
|
||||
}
|
||||
|
||||
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
|
||||
, seconds(10));
|
||||
d.upnp_connection.reset(new http_connection(m_io_service
|
||||
, m_cc, bind(&upnp::on_upnp_map_response, self(), _1, _2
|
||||
, boost::ref(d), i), true
|
||||
, bind(&upnp::create_port_mapping, self(), _1, boost::ref(d), i)));
|
||||
|
||||
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
|
||||
, seconds(10), 1);
|
||||
}
|
||||
else if (m.action == mapping_t::action_delete)
|
||||
{
|
||||
d.upnp_connection.reset(new http_connection(m_io_service
|
||||
, m_cc, bind(&upnp::on_upnp_unmap_response, self(), _1, _2
|
||||
, boost::ref(d), i), true
|
||||
, bind(&upnp::delete_port_mapping, self(), boost::ref(d), i)));
|
||||
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
|
||||
, seconds(10), 1);
|
||||
}
|
||||
|
||||
m.action = mapping_t::action_none;
|
||||
}
|
||||
|
||||
void upnp::delete_port_mapping(rootdevice& d, int i)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
|
||||
if (!d.upnp_connection)
|
||||
|
@ -592,31 +666,6 @@ void upnp::delete_port_mapping(rootdevice& d, int i)
|
|||
post(d, soap.str(), soap_action);
|
||||
}
|
||||
|
||||
// requires the mutex to be locked
|
||||
void upnp::unmap_port(rootdevice& d, int i)
|
||||
{
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
if (d.mapping[i].external_port == 0
|
||||
|| d.disabled)
|
||||
{
|
||||
if (i < num_mappings - 1)
|
||||
{
|
||||
unmap_port(d, i + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " ==> connecting to " << d.hostname << std::endl;
|
||||
#endif
|
||||
d.upnp_connection.reset(new http_connection(m_io_service
|
||||
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, self(), _1, _2
|
||||
, boost::ref(d), i)), true
|
||||
, bind(&upnp::delete_port_mapping, self(), boost::ref(d), i)));
|
||||
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
|
||||
, seconds(10));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct parse_state
|
||||
|
@ -633,6 +682,7 @@ namespace
|
|||
std::string top_tag;
|
||||
std::string control_url;
|
||||
char const* service_type;
|
||||
std::string model;
|
||||
};
|
||||
|
||||
void find_control_url(int type, char const* string, parse_state& state)
|
||||
|
@ -646,6 +696,10 @@ namespace
|
|||
{
|
||||
state.top_tag = string;
|
||||
}
|
||||
else if (!strcmp(string, "modelName"))
|
||||
{
|
||||
state.top_tag = string;
|
||||
}
|
||||
}
|
||||
else if (type == xml_end_tag)
|
||||
{
|
||||
|
@ -669,14 +723,20 @@ namespace
|
|||
state.control_url = string;
|
||||
if (state.found_service) state.exit = true;
|
||||
}
|
||||
else if (state.top_tag == "modelName")
|
||||
{
|
||||
state.model = string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void upnp::on_upnp_xml(asio::error_code const& e
|
||||
, libtorrent::http_parser const& p, rootdevice& d) try
|
||||
, libtorrent::http_parser const& p, rootdevice& d)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
if (d.upnp_connection)
|
||||
{
|
||||
|
@ -722,6 +782,7 @@ void upnp::on_upnp_xml(asio::error_code const& e
|
|||
if (s.found_service)
|
||||
{
|
||||
d.service_namespace = s.service_type;
|
||||
if (!s.model.empty()) m_model = s.model;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -733,6 +794,7 @@ void upnp::on_upnp_xml(asio::error_code const& e
|
|||
if (s.found_service)
|
||||
{
|
||||
d.service_namespace = s.service_type;
|
||||
if (!s.model.empty()) m_model = s.model;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -754,19 +816,26 @@ void upnp::on_upnp_xml(asio::error_code const& e
|
|||
|
||||
d.control_url = s.control_url;
|
||||
|
||||
map_port(d, 0);
|
||||
update_map(d, 0);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
disable();
|
||||
};
|
||||
|
||||
void upnp::disable()
|
||||
void upnp::disable(char const* msg)
|
||||
{
|
||||
m_disabled = true;
|
||||
|
||||
// kill all mappings
|
||||
for (std::vector<global_mapping_t>::iterator i = m_mappings.begin()
|
||||
, end(m_mappings.end()); i != end; ++i)
|
||||
{
|
||||
if (i->protocol == none) continue;
|
||||
i->protocol = none;
|
||||
m_callback(i - m_mappings.begin(), 0, msg);
|
||||
}
|
||||
|
||||
m_devices.clear();
|
||||
m_broadcast_timer.cancel();
|
||||
m_refresh_timer.cancel();
|
||||
asio::error_code ec;
|
||||
m_broadcast_timer.cancel(ec);
|
||||
m_refresh_timer.cancel(ec);
|
||||
m_socket.close();
|
||||
}
|
||||
|
||||
|
@ -823,8 +892,10 @@ namespace
|
|||
}
|
||||
|
||||
void upnp::on_upnp_map_response(asio::error_code const& e
|
||||
, libtorrent::http_parser const& p, rootdevice& d, int mapping) try
|
||||
, libtorrent::http_parser const& p, rootdevice& d, int mapping)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
if (d.upnp_connection)
|
||||
{
|
||||
|
@ -867,7 +938,7 @@ void upnp::on_upnp_map_response(asio::error_code const& e
|
|||
m_log << time_now_string()
|
||||
<< " <== error while adding portmap: incomplete http message" << std::endl;
|
||||
#endif
|
||||
d.disabled = true;
|
||||
next(d, mapping);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -886,37 +957,44 @@ void upnp::on_upnp_map_response(asio::error_code const& e
|
|||
}
|
||||
#endif
|
||||
|
||||
mapping_t& m = d.mapping[mapping];
|
||||
|
||||
if (s.error_code == 725)
|
||||
{
|
||||
// only permanent leases supported
|
||||
d.lease_duration = 0;
|
||||
d.mapping[mapping].need_update = true;
|
||||
map_port(d, mapping);
|
||||
m.action = mapping_t::action_add;
|
||||
++m.failcount;
|
||||
update_map(d, mapping);
|
||||
return;
|
||||
}
|
||||
else if (s.error_code == 718)
|
||||
else if (s.error_code == 718 || s.error_code == 727)
|
||||
{
|
||||
// conflict in mapping, try next external port
|
||||
++d.mapping[mapping].external_port;
|
||||
d.mapping[mapping].need_update = true;
|
||||
map_port(d, mapping);
|
||||
if (m.external_port != 0)
|
||||
{
|
||||
// conflict in mapping, set port to wildcard
|
||||
// and let the router decide
|
||||
m.external_port = 0;
|
||||
m.action = mapping_t::action_add;
|
||||
++m.failcount;
|
||||
update_map(d, mapping);
|
||||
return;
|
||||
}
|
||||
return_error(mapping, s.error_code);
|
||||
}
|
||||
else if (s.error_code == 716)
|
||||
{
|
||||
// The external port cannot be wildcarder
|
||||
// pick a random port
|
||||
m.external_port = 40000 + (rand() % 10000);
|
||||
m.action = mapping_t::action_add;
|
||||
++m.failcount;
|
||||
update_map(d, mapping);
|
||||
return;
|
||||
}
|
||||
else if (s.error_code != -1)
|
||||
{
|
||||
int num_errors = sizeof(error_codes) / sizeof(error_codes[0]);
|
||||
error_code_t* end = error_codes + num_errors;
|
||||
error_code_t tmp = {s.error_code, 0};
|
||||
error_code_t* e = std::lower_bound(error_codes, end, tmp
|
||||
, bind(&error_code_t::code, _1) < bind(&error_code_t::code, _2));
|
||||
std::string error_string = "UPnP mapping error ";
|
||||
error_string += boost::lexical_cast<std::string>(s.error_code);
|
||||
if (e != end && e->code == s.error_code)
|
||||
{
|
||||
error_string += ": ";
|
||||
error_string += e->msg;
|
||||
}
|
||||
m_callback(0, 0, error_string);
|
||||
return_error(mapping, s.error_code);
|
||||
}
|
||||
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
|
@ -927,50 +1005,52 @@ void upnp::on_upnp_map_response(asio::error_code const& e
|
|||
|
||||
if (s.error_code == -1)
|
||||
{
|
||||
int tcp = 0;
|
||||
int udp = 0;
|
||||
|
||||
if (mapping == 0)
|
||||
tcp = d.mapping[mapping].external_port;
|
||||
else
|
||||
udp = d.mapping[mapping].external_port;
|
||||
|
||||
m_callback(tcp, udp, "");
|
||||
m_callback(mapping, m.external_port, "");
|
||||
if (d.lease_duration > 0)
|
||||
{
|
||||
d.mapping[mapping].expires = time_now()
|
||||
m.expires = time_now()
|
||||
+ seconds(int(d.lease_duration * 0.75f));
|
||||
ptime next_expire = m_refresh_timer.expires_at();
|
||||
if (next_expire < time_now()
|
||||
|| next_expire > d.mapping[mapping].expires)
|
||||
|| next_expire > m.expires)
|
||||
{
|
||||
m_refresh_timer.expires_at(d.mapping[mapping].expires);
|
||||
m_refresh_timer.async_wait(m_strand.wrap(bind(&upnp::on_expire, self(), _1)));
|
||||
asio::error_code ec;
|
||||
m_refresh_timer.expires_at(m.expires, ec);
|
||||
m_refresh_timer.async_wait(bind(&upnp::on_expire, self(), _1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
d.mapping[mapping].expires = max_time();
|
||||
m.expires = max_time();
|
||||
}
|
||||
m.failcount = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_mappings; ++i)
|
||||
{
|
||||
if (d.mapping[i].need_update)
|
||||
{
|
||||
map_port(d, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
next(d, mapping);
|
||||
}
|
||||
catch (std::exception&)
|
||||
|
||||
void upnp::return_error(int mapping, int code)
|
||||
{
|
||||
disable();
|
||||
};
|
||||
int num_errors = sizeof(error_codes) / sizeof(error_codes[0]);
|
||||
error_code_t* end = error_codes + num_errors;
|
||||
error_code_t tmp = {code, 0};
|
||||
error_code_t* e = std::lower_bound(error_codes, end, tmp
|
||||
, bind(&error_code_t::code, _1) < bind(&error_code_t::code, _2));
|
||||
std::string error_string = "UPnP mapping error ";
|
||||
error_string += boost::lexical_cast<std::string>(code);
|
||||
if (e != end && e->code == code)
|
||||
{
|
||||
error_string += ": ";
|
||||
error_string += e->msg;
|
||||
}
|
||||
m_callback(mapping, 0, error_string);
|
||||
}
|
||||
|
||||
void upnp::on_upnp_unmap_response(asio::error_code const& e
|
||||
, libtorrent::http_parser const& p, rootdevice& d, int mapping) try
|
||||
, libtorrent::http_parser const& p, rootdevice& d, int mapping)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
if (d.upnp_connection)
|
||||
{
|
||||
|
@ -984,58 +1064,50 @@ void upnp::on_upnp_unmap_response(asio::error_code const& e
|
|||
m_log << time_now_string()
|
||||
<< " <== error while deleting portmap: " << e.message() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!p.header_finished())
|
||||
} else if (!p.header_finished())
|
||||
{
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " <== error while deleting portmap: incomplete http message" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (p.status_code() != 200)
|
||||
else if (p.status_code() != 200)
|
||||
{
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " <== error while deleting portmap: " << p.message() << std::endl;
|
||||
#endif
|
||||
d.disabled = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#ifdef TORRENT_UPNP_LOGGING
|
||||
m_log << time_now_string()
|
||||
<< " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end)
|
||||
<< std::endl;
|
||||
m_log << time_now_string()
|
||||
<< " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end)
|
||||
<< std::endl;
|
||||
#endif
|
||||
|
||||
// ignore errors and continue with the next mapping for this device
|
||||
if (mapping < num_mappings - 1)
|
||||
{
|
||||
unmap_port(d, mapping + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
disable();
|
||||
};
|
||||
|
||||
void upnp::on_expire(asio::error_code const& e) try
|
||||
d.mapping[mapping].protocol = none;
|
||||
|
||||
next(d, mapping);
|
||||
}
|
||||
|
||||
void upnp::on_expire(asio::error_code const& e)
|
||||
{
|
||||
if (e) return;
|
||||
|
||||
ptime now = time_now();
|
||||
ptime next_expire = max_time();
|
||||
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
for (std::set<rootdevice>::iterator i = m_devices.begin()
|
||||
, end(m_devices.end()); i != end; ++i)
|
||||
{
|
||||
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
for (int m = 0; m < num_mappings; ++m)
|
||||
for (int m = 0; m < num_mappings(); ++m)
|
||||
{
|
||||
if (d.mapping[m].expires != max_time())
|
||||
continue;
|
||||
|
@ -1043,7 +1115,7 @@ void upnp::on_expire(asio::error_code const& e) try
|
|||
if (d.mapping[m].expires < now)
|
||||
{
|
||||
d.mapping[m].expires = max_time();
|
||||
map_port(d, m);
|
||||
update_map(d, m);
|
||||
}
|
||||
else if (d.mapping[m].expires < next_expire)
|
||||
{
|
||||
|
@ -1053,35 +1125,41 @@ void upnp::on_expire(asio::error_code const& e) try
|
|||
}
|
||||
if (next_expire != max_time())
|
||||
{
|
||||
m_refresh_timer.expires_at(next_expire);
|
||||
m_refresh_timer.async_wait(m_strand.wrap(bind(&upnp::on_expire, self(), _1)));
|
||||
asio::error_code ec;
|
||||
m_refresh_timer.expires_at(next_expire, ec);
|
||||
m_refresh_timer.async_wait(bind(&upnp::on_expire, self(), _1));
|
||||
}
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
disable();
|
||||
};
|
||||
|
||||
void upnp::close()
|
||||
{
|
||||
m_refresh_timer.cancel();
|
||||
m_broadcast_timer.cancel();
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
asio::error_code ec;
|
||||
m_refresh_timer.cancel(ec);
|
||||
m_broadcast_timer.cancel(ec);
|
||||
m_closing = true;
|
||||
m_socket.close();
|
||||
|
||||
if (m_disabled)
|
||||
{
|
||||
m_devices.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::set<rootdevice>::iterator i = m_devices.begin()
|
||||
, end(m_devices.end()); i != end; ++i)
|
||||
{
|
||||
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||
TORRENT_ASSERT(d.magic == 1337);
|
||||
if (d.control_url.empty()) continue;
|
||||
unmap_port(d, 0);
|
||||
for (std::vector<mapping_t>::iterator j = d.mapping.begin()
|
||||
, end(d.mapping.end()); j != end; ++j)
|
||||
{
|
||||
if (j->protocol == none) continue;
|
||||
if (j->action == mapping_t::action_add)
|
||||
{
|
||||
j->action = mapping_t::action_none;
|
||||
continue;
|
||||
}
|
||||
j->action = mapping_t::action_delete;
|
||||
m_mappings[j - d.mapping.begin()].protocol = none;
|
||||
}
|
||||
update_map(d, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue