Update libtorrent to 0.14 trunk.

A few minor touch ups regarding torrent state and queue.
This commit is contained in:
Andrew Resch 2008-04-16 01:15:06 +00:00
parent 1861ef439f
commit 70bb78b833
104 changed files with 13220 additions and 6722 deletions

View File

@ -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

View File

@ -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)

View File

@ -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"]

View File

@ -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
);

View File

@ -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"

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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)
;

View File

@ -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))
;
}

View File

@ -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)); }
};

View File

@ -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)

View File

@ -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());
}

View File

@ -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
};
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -44,6 +44,11 @@ class buffer
public:
struct interval
{
interval()
: begin(0)
, end(0)
{}
interval(char* begin, char* end)
: begin(begin)
, end(end)

View File

@ -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
};
}

View File

@ -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>

View File

@ -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;
};

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;
};
}

View File

@ -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;
};
}

View File

@ -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"

View File

@ -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;

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;
};
}

View File

@ -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:

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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
};
}

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;

View File

@ -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

View File

@ -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;
};
}

View File

@ -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

View File

@ -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;
};

View File

@ -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&) {}

1038
libtorrent/src/GeoIP.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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();
}
}

212
libtorrent/src/gzip.cpp Normal file
View File

@ -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;
}
}

View File

@ -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));
}

238
libtorrent/src/http_parser.cpp Executable file
View File

@ -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();
}
}

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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"

View File

@ -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

View File

@ -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()

View File

@ -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;

View File

@ -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;
}
};

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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;
}

224
libtorrent/src/memdebug.cpp Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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());
}

View File

@ -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

View File

@ -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));
}
}

View File

@ -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

View File

@ -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;

View File

@ -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
}
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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