added I2P support

This commit is contained in:
Arvid Norberg 2009-08-20 03:19:12 +00:00
parent 481f19c733
commit 45fd696bc6
34 changed files with 1411 additions and 136 deletions

View File

@ -18,6 +18,7 @@ set(sources
http_connection
http_stream
http_parser
i2p_stream
identify_client
ip_filter
peer_connection

View File

@ -1,3 +1,5 @@
* added support for i2p torrents
0.15 release
* reduced the number of floating point operations (to better support

View File

@ -358,6 +358,7 @@ SOURCES =
bt_peer_connection
web_peer_connection
http_seed_connection
i2p_stream
instantiate_connection
natpmp
piece_picker

View File

@ -1042,6 +1042,21 @@ These functions returns references to their respective current settings.
The ``dht_proxy`` is not available when DHT is disabled.
set_i2p_proxy() i2p_proxy()
---------------------------
::
void set_i2p_proxy(proxy_settings const&);
proxy_settings const& i2p_proxy();
``set_i2p_proxy`` sets the i2p_ proxy, and tries to open a persistant
connection to it. The only used fields in the proxy settings structs
are ``hostname`` and ``port``.
``i2p_proxy`` returns the current i2p proxy in use.
start_dht() stop_dht() set_dht_settings() dht_state()
-----------------------------------------------------

View File

@ -747,6 +747,9 @@ int main(int argc, char* argv[])
" -t <seconds> sets the scan interval of the monitor dir\n"
" -x <file> loads an emule IP-filter file\n"
" -c <limit> sets the max number of connections\n"
#if TORRENT_USE_I2P
" -i <i2p-host> the hostname to an I2P SAM bridge to use\n"
#endif
" -C <limit> sets the max cache size. Specified in 16kB blocks\n"
" -F <seconds> sets the UI refresh rate. This is the number of\n"
" seconds between screen refreshes.\n"
@ -759,7 +762,6 @@ int main(int argc, char* argv[])
using namespace libtorrent;
session_settings settings;
proxy_settings ps;
settings.user_agent = "client_test/" LIBTORRENT_VERSION;
settings.auto_upload_slots_rate_based = true;
@ -780,7 +782,8 @@ int main(int argc, char* argv[])
// monitor when they're not in the directory anymore.
handles_t handles;
session ses(fingerprint("LT", LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0)
, session::start_default_features | session::add_default_plugins, alert::all_categories);
, session::add_default_plugins
, alert::all_categories & (~alert::dht_notification));
std::vector<char> in;
if (load_file(".ses_state", in) == 0)
@ -950,6 +953,17 @@ int main(int argc, char* argv[])
}
break;
case 'c': ses.set_max_connections(atoi(arg)); break;
#if TORRENT_USE_I2P
case 'i':
{
proxy_settings ps;
ps.hostname = arg;
ps.port = 7656; // default SAM port
ps.type = proxy_settings::i2p_proxy;
ses.set_i2p_proxy(ps);
break;
}
#endif // TORRENT_USE_I2P
case 'C': settings.cache_size = atoi(arg); break;
}
++i; // skip the argument

View File

@ -33,6 +33,7 @@ libtorrent/http_connection.hpp \
libtorrent/http_stream.hpp \
libtorrent/http_parser.hpp \
libtorrent/http_tracker_connection.hpp \
libtorrent/i2p_stream.hpp \
libtorrent/identify_client.hpp \
libtorrent/instantiate_connection.hpp \
libtorrent/intrusive_ptr_base.hpp \

View File

@ -294,6 +294,20 @@ namespace libtorrent
{ return m_dht_proxy; }
#endif
#if TORRENT_USE_I2P
void set_i2p_proxy(proxy_settings const& s)
{
m_i2p_conn.open(s, boost::bind(&session_impl::on_i2p_open, this, _1));
open_new_incoming_i2p_connection();
}
void on_i2p_open(error_code const& ec);
proxy_settings const& i2p_proxy() const
{ return m_i2p_conn.proxy(); }
void open_new_incoming_i2p_connection();
void on_i2p_accept(boost::shared_ptr<socket_type> const& s
, error_code const& e);
#endif
#ifndef TORRENT_DISABLE_GEO_IP
std::string as_name_for_ip(address const& a);
int as_for_ip(address const& a);
@ -380,15 +394,22 @@ namespace libtorrent
};
boost::object_pool<
policy::ipv4_peer, logging_allocator> m_ipv4_peer_pool;
# if TORRENT_USE_IPV6
#if TORRENT_USE_IPV6
boost::object_pool<
policy::ipv6_peer, logging_allocator> m_ipv6_peer_pool;
# endif
#endif
#if TORRENT_USE_I2P
boost::object_pool<
policy::i2p_peer, logging_allocator> m_i2p_peer_pool;
#endif
#else
boost::object_pool<policy::ipv4_peer> m_ipv4_peer_pool;
# if TORRENT_USE_IPV6
#if TORRENT_USE_IPV6
boost::object_pool<policy::ipv6_peer> m_ipv6_peer_pool;
# endif
#endif
#if TORRENT_USE_I2P
boost::object_pool<policy::i2p_peer> m_i2p_peer_pool;
#endif
#endif
// this vector is used to store the block_info
@ -523,6 +544,11 @@ namespace libtorrent
void open_new_incoming_socks_connection();
#if TORRENT_USE_I2P
i2p_connection m_i2p_conn;
boost::shared_ptr<socket_type> m_i2p_listen_socket;
#endif
listen_socket_t setup_listener(tcp::endpoint ep, int retries, bool v6_only = false);
// the settings for the client

View File

@ -115,6 +115,8 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_USE_WRITEV 1
#define TORRENT_USE_IOSTREAM 1
#define TORRENT_USE_I2P 1
// should wpath or path be used?
#if defined UNICODE && !defined BOOST_FILESYSTEM_NARROW_ONLY \
&& BOOST_VERSION >= 103400 && !defined __APPLE__

View File

@ -189,6 +189,8 @@ namespace libtorrent
reserved6,
reserved7,
reserved8,
no_i2p_router,
};
}

View File

@ -63,6 +63,7 @@ namespace libtorrent
// encodes a string using the base64 scheme
TORRENT_EXPORT std::string base64encode(std::string const& s);
TORRENT_EXPORT std::string base64decode(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);

View File

@ -54,6 +54,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/variant_stream.hpp"
#endif
#include "libtorrent/i2p_stream.hpp"
namespace libtorrent
{
@ -108,12 +110,20 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
void get(std::string const& url, time_duration timeout = seconds(30)
, int prio = 0, proxy_settings const* ps = 0, int handle_redirects = 5
, std::string const& user_agent = "", address const& bind_addr = address_v4::any());
, std::string const& user_agent = "", address const& bind_addr = address_v4::any()
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn = 0
#endif
);
void start(std::string const& hostname, std::string const& port
, 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());
, address const& bind_addr = address_v4::any()
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn = 0
#endif
);
void close();
@ -127,6 +137,10 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
private:
#if TORRENT_USE_I2P
void on_i2p_resolve(error_code const& e
, char const* destination);
#endif
void on_resolve(error_code const& e
, tcp::resolver::iterator i);
void queue_connect();
@ -146,6 +160,9 @@ private:
variant_stream<socket_type, ssl_stream<socket_type> > m_sock;
#else
socket_type m_sock;
#endif
#if TORRENT_USE_I2P
i2p_connection* m_i2p_conn;
#endif
int m_read_pos;
tcp::resolver m_resolver;

View File

@ -48,6 +48,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/peer_id.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/i2p_stream.hpp"
namespace libtorrent
{
@ -73,7 +74,11 @@ namespace libtorrent
, boost::weak_ptr<request_callback> c
, aux::session_impl const& ses
, proxy_settings const& ps
, std::string const& password = "");
, std::string const& password = ""
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn = 0
#endif
);
void start();
void close();
@ -100,6 +105,9 @@ namespace libtorrent
proxy_settings const& m_ps;
connection_queue& m_cc;
io_service& m_ios;
#if TORRENT_USE_I2P
i2p_connection* m_i2p_conn;
#endif
};
}

View File

@ -0,0 +1,217 @@
/*
Copyright (c) 2009, 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.
*/
#ifndef TORRENT_I2P_STREAM_HPP_INCLUDED
#define TORRENT_I2P_STREAM_HPP_INCLUDED
#include "libtorrent/config.hpp"
#if TORRENT_USE_I2P
#include "libtorrent/proxy_base.hpp"
#include "libtorrent/session_settings.hpp"
namespace libtorrent {
namespace i2p_error {
enum i2p_error_code
{
no_error = 0,
parse_failed,
cant_reach_peer,
i2p_error,
invalid_key,
invalid_id,
timeout,
key_not_found,
num_errors
};
}
struct TORRENT_EXPORT i2p_error_category : boost::system::error_category
{
virtual const char* name() const;
virtual std::string message(int ev) const;
virtual boost::system::error_condition default_error_condition(int ev) const
{ return boost::system::error_condition(ev, *this); }
};
extern i2p_error_category i2p_category;
class i2p_stream : public proxy_base
{
public:
explicit i2p_stream(io_service& io_service)
: proxy_base(io_service)
, m_id(0)
, m_command(cmd_create_session)
, m_state(0)
{}
enum command_t
{
cmd_none,
cmd_create_session,
cmd_connect,
cmd_accept,
cmd_name_lookup,
cmd_incoming
};
void set_command(command_t c) { m_command = c; }
void set_session_id(char const* id) { m_id = id; }
void set_destination(std::string const& d) { m_dest = d; }
std::string const& destination() { return m_dest; }
typedef boost::function<void(error_code const&)> handler_type;
template <class Handler>
void async_connect(endpoint_type const& endpoint, Handler const& handler)
{
// since we don't support regular endpoints, just ignore the one
// provided and use m_dest.
// the connect is split up in the following steps:
// 1. resolve name of proxy server
// 2. connect to SAM bridge
// 4 send command message (CONNECT/ACCEPT)
// to avoid unnecessary copying of the handler,
// store it in a shaed_ptr
boost::shared_ptr<handler_type> h(new handler_type(handler));
tcp::resolver::query q(m_hostname
, boost::lexical_cast<std::string>(m_port));
m_resolver.async_resolve(q, boost::bind(
&i2p_stream::do_connect, this, _1, _2, h));
}
std::string name_lookup() const { return m_name_lookup; }
void set_name_lookup(char const* name) { m_name_lookup = name; }
void send_name_lookup(boost::shared_ptr<handler_type> h);
private:
bool handle_error(error_code const& e, boost::shared_ptr<handler_type> const& h);
void do_connect(error_code const& e, tcp::resolver::iterator i
, boost::shared_ptr<handler_type> h);
void connected(error_code const& e, boost::shared_ptr<handler_type> h);
void start_read_line(error_code const& e, boost::shared_ptr<handler_type> h);
void read_line(error_code const& e, boost::shared_ptr<handler_type> h);
void send_connect(boost::shared_ptr<handler_type> h);
void send_accept(boost::shared_ptr<handler_type> h);
void send_session_create(boost::shared_ptr<handler_type> h);
// send and receive buffer
std::vector<char> m_buffer;
char const* m_id;
int m_command; // 0 = connect, 1 = accept
std::string m_dest;
std::string m_name_lookup;
enum state_t
{
read_hello_response,
read_connect_response,
read_accept_response,
read_session_create_response,
read_name_lookup_response,
};
int m_state;
};
class i2p_connection
{
public:
i2p_connection(io_service& ios);
~i2p_connection();
proxy_settings const& proxy() const { return m_sam_router; }
bool is_open() const
{
return m_sam_socket
&& m_sam_socket->is_open()
&& m_state != sam_connecting;
}
void open(proxy_settings const& s, i2p_stream::handler_type const& h);
void close();
char const* session_id() const { return m_session_id.c_str(); }
std::string const& local_endpoint() const { return m_i2p_local_endpoint; }
typedef boost::function<void(error_code const&, char const*)> name_lookup_handler;
void async_name_lookup(char const* name, name_lookup_handler handler);
private:
void on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h);
void do_name_lookup(std::string const& name
, name_lookup_handler const& h);
void on_name_lookup(error_code const& ec
, name_lookup_handler handler);
void set_local_endpoint(error_code const& ec, char const* dest);
// to talk to i2p SAM bridge
boost::shared_ptr<i2p_stream> m_sam_socket;
proxy_settings m_sam_router;
// our i2p endpoint key
std::string m_i2p_local_endpoint;
std::string m_session_id;
std::list<std::pair<std::string, name_lookup_handler> > m_name_lookup;
enum state_t
{
sam_connecting,
sam_name_lookup,
sam_idle
};
state_t m_state;
io_service& m_io_service;
};
}
#endif // TORRENT_USE_I2P
#endif

View File

@ -73,6 +73,11 @@ namespace libtorrent
void pulse();
struct peer;
#if TORRENT_USE_I2P
policy::peer* add_i2p_peer(char const* destination, int source, char flags);
#endif
// this is called once for every peer we get from
// the tracker, pex, lsd or dht.
policy::peer* add_peer(const tcp::endpoint& remote, const peer_id& pid
@ -129,6 +134,7 @@ namespace libtorrent
size_type total_upload() const;
libtorrent::address address() const;
char const* dest() const;
tcp::endpoint ip() const { return tcp::endpoint(address(), port); }
@ -172,7 +178,6 @@ namespace libtorrent
// in number of seconds since session was created
boost::uint16_t last_connected;
// the port this peer is or was connected on
boost::uint16_t port;
@ -233,6 +238,10 @@ namespace libtorrent
// the one to use, false if it's the v4 one
bool is_v6_addr:1;
#endif
#if TORRENT_USE_I2P
// set if the i2p_destination is in use in the addr union
bool is_i2p_addr:1;
#endif
// if this is true, the peer has previously
// participated in a piece that failed the piece
@ -261,6 +270,17 @@ namespace libtorrent
address_v4 addr;
};
#if TORRENT_USE_I2P
struct i2p_peer : peer
{
i2p_peer(char const* destination, bool connectable, int src);
i2p_peer(char const* destination);
~i2p_peer();
char* destination;
};
#endif
#if TORRENT_USE_IPV6
struct ipv6_peer : peer
{
@ -287,9 +307,27 @@ namespace libtorrent
return lhs < rhs->address();
}
#if TORRENT_USE_I2P
bool operator()(
peer const* lhs, char const* rhs) const
{
return strcmp(lhs->dest(), rhs) < 0;
}
bool operator()(
char const* lhs, peer const* rhs) const
{
return strcmp(lhs, rhs->dest()) < 0;
}
#endif
bool operator()(
peer const* lhs, peer const* rhs) const
{
#if TORRENT_USE_I2P
if (rhs->is_i2p_addr == lhs->is_i2p_addr)
return strcmp(lhs->dest(), rhs->dest()) < 0;
#endif
return lhs->address() < rhs->address();
}
};
@ -328,6 +366,10 @@ namespace libtorrent
private:
void update_peer(policy::peer* p, int src, int flags
, tcp::endpoint const& remote, char const* destination);
bool insert_peer(policy::peer* p, iterator iter, int flags);
bool compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const;
bool compare_peer(policy::peer const& lhs, policy::peer const& rhs
, address const& external_ip) const;
@ -376,15 +418,48 @@ namespace libtorrent
: peer(ip.port(), connectable, src)
, addr(ip.address().to_v4())
{
#if TORRENT_USE_IPV6
is_v6_addr = false;
#endif
#if TORRENT_USE_I2P
is_i2p_addr = false;
#endif
}
inline policy::ipv4_peer::ipv4_peer(libtorrent::address const& a)
: addr(a.to_v4())
{
#if TORRENT_USE_IPV6
is_v6_addr = false;
#endif
#if TORRENT_USE_I2P
is_i2p_addr = false;
#endif
}
#if TORRENT_USE_I2P
inline policy::i2p_peer::i2p_peer(char const* dest, bool connectable, int src)
: peer(0, connectable, src), destination(strdup(dest))
{
#if TORRENT_USE_IPV6
is_v6_addr = false;
#endif
is_i2p_addr = true;
}
inline policy::i2p_peer::i2p_peer(char const* dest)
: destination(strdup(dest))
{
#if TORRENT_USE_IPV6
is_v6_addr = false;
#endif
is_i2p_addr = true;
}
inline policy::i2p_peer::~i2p_peer()
{ free(destination); }
#endif // TORRENT_USE_I2P
#if TORRENT_USE_IPV6
inline policy::ipv6_peer::ipv6_peer(
tcp::endpoint const& ip, bool connectable, int src
)
@ -392,20 +467,41 @@ namespace libtorrent
, addr(ip.address().to_v6().to_bytes())
{
is_v6_addr = true;
#if TORRENT_USE_I2P
is_i2p_addr = false;
#endif
}
inline policy::ipv6_peer::ipv6_peer(libtorrent::address const& a)
: addr(a.to_v6().to_bytes())
{
is_v6_addr = true;
#if TORRENT_USE_I2P
is_i2p_addr = false;
#endif
}
#endif // TORRENT_USE_IPV6
#if TORRENT_USE_I2P
inline char const* policy::peer::dest() const
{
if (is_i2p_addr)
return static_cast<policy::i2p_peer const*>(this)->destination;
return "";
}
#endif
inline libtorrent::address policy::peer::address() const
{
#if TORRENT_USE_IPV6
if (is_v6_addr)
return libtorrent::address_v6(
static_cast<policy::ipv6_peer const*>(this)->addr);
else
#endif
#if TORRENT_USE_I2P
if (is_i2p_addr) return libtorrent::address();
else
#endif
return static_cast<policy::ipv4_peer const*>(this)->addr;
}

View File

@ -375,6 +375,11 @@ namespace libtorrent
proxy_settings const& dht_proxy() const;
#endif
#if TORRENT_USE_I2P
void set_i2p_proxy(proxy_settings const& s);
proxy_settings const& i2p_proxy() const;
#endif
int upload_rate_limit() const;
int download_rate_limit() const;
int local_upload_rate_limit() const;

View File

@ -71,7 +71,9 @@ namespace libtorrent
http,
// http proxy with basic authentication
// uses username and password
http_pw
http_pw,
// route through a i2p SAM proxy
i2p_proxy
};
proxy_type type;
@ -176,6 +178,7 @@ namespace libtorrent
, write_cache_line_size(32)
, optimistic_disk_retry(10 * 60)
, disable_hash_checks(false)
, allow_i2p_mixed(false)
{}
// this is the user agent that will be sent to the tracker
@ -621,6 +624,15 @@ namespace libtorrent
// testing purposes (typically combined with
// disabled_storage)
bool disable_hash_checks;
// if this is true, i2p torrents are allowed
// to also get peers from other sources than
// the tracker, and connect to regular IPs,
// not providing any anonymization. This may
// be useful if the user is not interested in
// the anonymization of i2p, but still wants to
// be able to connect to i2p peers.
bool allow_i2p_mixed;
};
#ifndef TORRENT_DISABLE_DHT

View File

@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socks5_stream.hpp"
#include "libtorrent/http_stream.hpp"
#include "libtorrent/i2p_stream.hpp"
#include "libtorrent/variant_stream.hpp"
namespace libtorrent
@ -42,7 +43,11 @@ namespace libtorrent
typedef variant_stream<
stream_socket
, socks5_stream
, http_stream> socket_type;
, http_stream
#if TORRENT_USE_I2P
, i2p_stream
#endif
> socket_type;
}
#endif

View File

@ -105,6 +105,7 @@ namespace libtorrent
explicit time_duration(boost::int64_t d) : diff(d) {}
time_duration& operator-=(time_duration const& c) { diff -= c.diff; return *this; }
time_duration& operator+=(time_duration const& c) { diff += c.diff; return *this; }
time_duration& operator*=(int v) { diff *= v; return *this; }
time_duration operator+(time_duration const& c) { return time_duration(diff + c.diff); }
time_duration operator-(time_duration const& c) { return time_duration(diff - c.diff); }
boost::int64_t diff;

View File

@ -513,6 +513,10 @@ namespace libtorrent
// all seeds and let the tracker know we're finished.
void completed();
#if TORRENT_USE_I2P
void on_i2p_resolve(error_code const& ec, char const* dest);
#endif
// this is the asio callback that is called when a name
// lookup for a PEER is completed.
void on_peer_name_lookup(error_code const& e, tcp::resolver::iterator i

View File

@ -256,6 +256,8 @@ namespace libtorrent
bool priv() const { return m_private; }
bool is_i2p() const { return m_i2p; }
int piece_size(int index) const { return m_files.piece_size(index); }
sha1_hash hash_for_piece(int index) const
@ -367,6 +369,12 @@ namespace libtorrent
// be announced on the dht
bool m_private;
// this is true if one of the trackers has an .i2p top
// domain in its hostname. This means the DHT and LSD
// features are disabled for this torrent (unless the
// settings allows mixing i2p peers with regular peers)
bool m_i2p;
// this is a copy of the info section from the torrent.
// it use maintained in this flat format in order to
// make it available through the metadata extension

View File

@ -26,7 +26,7 @@ socks5_stream.cpp http_stream.cpp connection_queue.cpp \
disk_io_thread.cpp ut_metadata.cpp lt_trackers.cpp magnet_uri.cpp udp_socket.cpp smart_ban.cpp \
http_parser.cpp gzip.cpp disk_buffer_holder.cpp create_torrent.cpp GeoIP.c \
parse_url.cpp file_storage.cpp error_code.cpp ConvertUTF.cpp \
allocator.cpp \
allocator.cpp i2p_stream.cpp \
$(kademlia_sources)
noinst_HEADERS = \
@ -67,6 +67,7 @@ $(top_srcdir)/include/libtorrent/http_stream.hpp \
$(top_srcdir)/include/libtorrent/http_parser.hpp \
$(top_srcdir)/include/libtorrent/session_settings.hpp \
$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \
$(top_srcdir)/include/libtorrent/i2p_stream.hpp \
$(top_srcdir)/include/libtorrent/identify_client.hpp \
$(top_srcdir)/include/libtorrent/instantiate_connection.hpp \
$(top_srcdir)/include/libtorrent/intrusive_ptr_base.hpp \

View File

@ -184,6 +184,7 @@ namespace libtorrent
"",
"",
"",
"no i2p router is set up",
};
if (ev < 0 || ev >= sizeof(msgs)/sizeof(msgs[0]))
return "Unknown error";

View File

@ -51,7 +51,11 @@ enum { max_bottled_buffer = 1024 * 1024 };
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)
, address const& bind_addr
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn
#endif
)
{
std::string protocol;
std::string auth;
@ -137,12 +141,20 @@ void http_connection::get(std::string const& url, time_duration timeout, int pri
sendbuffer.assign(request);
m_url = url;
start(hostname, to_string(port).elems, timeout, prio
, ps, ssl, handle_redirects, bind_addr);
, ps, ssl, handle_redirects, bind_addr
#if TORRENT_USE_I2P
, i2p_conn
#endif
);
}
void http_connection::start(std::string const& hostname, std::string const& port
, time_duration timeout, int prio, proxy_settings const* ps, bool ssl, int handle_redirects
, address const& bind_addr)
, address const& bind_addr
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn
#endif
)
{
TORRENT_ASSERT(prio >= 0 && prio < 2);
@ -184,20 +196,69 @@ void http_connection::start(std::string const& hostname, std::string const& port
error_code ec;
m_sock.close(ec);
#if TORRENT_USE_I2P
bool is_i2p = false;
char const* top_domain = strrchr(hostname.c_str(), '.');
if (top_domain && strcmp(top_domain, ".i2p") == 0 && i2p_conn)
{
// this is an i2p name, we need to use the sam connection
// to do the name lookup
is_i2p = true;
m_i2p_conn = i2p_conn;
// quadruple the timeout for i2p destinations
// because i2p is sloooooow
m_timeout *= 4;
}
#endif
#if TORRENT_USE_I2P
if (is_i2p && i2p_conn->proxy().type != proxy_settings::i2p_proxy)
{
m_resolver.get_io_service().post(boost::bind(&http_connection::callback
, this, error_code(errors::no_i2p_router, libtorrent_category), (char*)0, 0));
return;
}
#endif
#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> >();
TORRENT_ASSERT(s);
bool ret = instantiate_connection(m_resolver.get_io_service(), m_proxy, s->next_layer());
bool ret = false;
#if TORRENT_USE_I2P
if (is_i2p)
{
ret = instantiate_connection(m_resolver.get_io_service(), i2p_conn->proxy()
, s->next_layer());
}
else
#endif
{
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>());
bool ret = false;
#if TORRENT_USE_I2P
if (is_i2p)
{
ret = instantiate_connection(m_resolver.get_io_service(), i2p_conn->proxy()
, *m_sock.get<socket_type>());
TORRENT_ASSERT(m_sock.get<socket_type>());
TORRENT_ASSERT(m_sock.get<socket_type>()->get<i2p_stream>());
}
else
#endif
{
ret = instantiate_connection(m_resolver.get_io_service()
, m_proxy, *m_sock.get<socket_type>());
}
TORRENT_ASSERT(ret);
}
#else
@ -217,9 +278,19 @@ void http_connection::start(std::string const& hostname, std::string const& port
}
}
tcp::resolver::query query(hostname, port);
m_resolver.async_resolve(query, bind(&http_connection::on_resolve
, shared_from_this(), _1, _2));
#if TORRENT_USE_I2P
if (is_i2p)
{
i2p_conn->async_name_lookup(hostname.c_str(), bind(&http_connection::on_i2p_resolve
, shared_from_this(), _1, _2));
}
else
#endif
{
tcp::resolver::query query(hostname, port);
m_resolver.async_resolve(query, bind(&http_connection::on_resolve
, shared_from_this(), _1, _2));
}
m_hostname = hostname;
m_port = port;
}
@ -290,6 +361,34 @@ void http_connection::close()
m_abort = true;
}
#if TORRENT_USE_I2P
void http_connection::on_i2p_resolve(error_code const& e
, char const* destination)
{
if (e)
{
callback(e);
close();
return;
}
#ifdef TORRENT_USE_OPENSSL
TORRENT_ASSERT(m_ssl == false);
TORRENT_ASSERT(m_sock.get<socket_type>());
TORRENT_ASSERT(m_sock.get<socket_type>()->get<i2p_stream>());
m_sock.get<socket_type>()->get<i2p_stream>()->set_destination(destination);
m_sock.get<socket_type>()->get<i2p_stream>()->set_command(i2p_stream::cmd_connect);
m_sock.get<socket_type>()->get<i2p_stream>()->set_session_id(m_i2p_conn->session_id());
#else
m_sock.get<i2p_stream>()->set_destination(destination);
m_sock.get<i2p_stream>()->set_command(i2p_stream::cmd_connect);
m_sock.get<i2p_stream>()->set_session_id(m_i2p_conn->session_id());
#endif
m_sock.async_connect(tcp::endpoint(), boost::bind(&http_connection::on_connect
, shared_from_this(), _1));
}
#endif
void http_connection::on_resolve(error_code const& e
, tcp::resolver::iterator i)
{

View File

@ -73,13 +73,20 @@ namespace libtorrent
, boost::weak_ptr<request_callback> c
, aux::session_impl const& ses
, proxy_settings const& ps
, std::string const& auth)
, std::string const& auth
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn
#endif
)
: tracker_connection(man, req, ios, c)
, m_man(man)
, m_ses(ses)
, m_ps(ps)
, m_cc(cc)
, m_ios(ios)
#if TORRENT_USE_I2P
, m_i2p_conn(i2p_conn)
#endif
{}
void http_tracker_connection::start()
@ -102,6 +109,14 @@ namespace libtorrent
url.replace(pos, 8, "scrape");
}
#if TORRENT_USE_I2P
// defined in torrent_info.cpp
bool is_i2p_url(std::string const& url);
bool i2p = is_i2p_url(url);
#else
static const bool i2p = false;
#endif
session_settings const& settings = m_ses.settings();
// if request-string already contains
@ -125,7 +140,9 @@ namespace libtorrent
"%s"
#endif
, escape_string((const char*)&tracker_req().pid[0], 20).c_str()
, tracker_req().listen_port
// the i2p tracker seems to verify that the port is not 0,
// even though it ignores it otherwise
, i2p ? 1 : tracker_req().listen_port
, tracker_req().uploaded
, tracker_req().downloaded
, tracker_req().left
@ -145,6 +162,16 @@ namespace libtorrent
url += event_string[tracker_req().event - 1];
}
#if TORRENT_USE_I2P
if (i2p)
{
url += "&ip=";
url += escape_string(m_i2p_conn->local_endpoint().c_str()
, m_i2p_conn->local_endpoint().size());
url += ".i2p";
}
else
#endif
if (settings.announce_ip != address())
{
error_code ec;
@ -152,13 +179,13 @@ namespace libtorrent
if (!ec) url += "&ip=" + ip;
}
if (!tracker_req().ipv6.empty())
if (!tracker_req().ipv6.empty() && !i2p)
{
url += "&ipv6=";
url += tracker_req().ipv6;
}
if (!tracker_req().ipv4.empty())
if (!tracker_req().ipv4.empty() && !i2p)
{
url += "&ipv4=";
url += tracker_req().ipv4;
@ -176,7 +203,11 @@ namespace libtorrent
:settings.tracker_completion_timeout;
m_tracker_connection->get(url, seconds(timeout)
, 1, &m_ps, 5, settings.user_agent, bind_interface());
, 1, &m_ps, 5, settings.user_agent, bind_interface()
#if TORRENT_USE_I2P
, m_i2p_conn
#endif
);
// the url + 100 estimated header size
sent_bytes(url.size() + 100);

430
src/i2p_stream.cpp Normal file
View File

@ -0,0 +1,430 @@
/*
Copyright (c) 2009, 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 "libtorrent/i2p_stream.hpp"
#include "libtorrent/assert.hpp"
#include <boost/bind.hpp>
#if TORRENT_USE_I2P
namespace libtorrent
{
i2p_error_category i2p_category;
const char* i2p_error_category::name() const
{
return "i2p error";
}
std::string i2p_error_category::message(int ev) const
{
static char const* messages[] =
{
"no error",
"parse failed",
"cannot reach peer",
"i2p error",
"invalid key",
"invalid id",
"timeout",
"key not found"
};
if (ev < 0 || ev > i2p_error::num_errors) return "unknown error";
return messages[ev];
}
i2p_connection::i2p_connection(io_service& ios)
: m_io_service(ios)
{}
i2p_connection::~i2p_connection()
{}
void i2p_connection::close()
{
m_sam_socket->close();
}
void i2p_connection::open(proxy_settings const& s, i2p_stream::handler_type const& handler)
{
// we already seem to have a session to this SAM router
if (m_sam_router.hostname == s.hostname
&& m_sam_router.port == s.port
&& is_open()) return;
m_sam_router = s;
m_sam_router.type = proxy_settings::i2p_proxy;
m_state = sam_connecting;
char tmp[20];
std::generate(tmp, tmp + sizeof(tmp), &std::rand);
m_session_id.resize(sizeof(tmp)*2);
to_hex(tmp, 20, &m_session_id[0]);
m_sam_socket.reset(new i2p_stream(m_io_service));
m_sam_socket->set_proxy(m_sam_router.hostname, m_sam_router.port);
m_sam_socket->set_command(i2p_stream::cmd_create_session);
m_sam_socket->set_session_id(m_session_id.c_str());
m_sam_socket->async_connect(tcp::endpoint()
, boost::bind(&i2p_connection::on_sam_connect, this, _1, handler));
}
void i2p_connection::on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h)
{
m_state = sam_idle;
do_name_lookup("ME", boost::bind(&i2p_connection::set_local_endpoint, this, _1, _2));
h(ec);
}
void i2p_connection::set_local_endpoint(error_code const& ec, char const* dest)
{
if (ec || dest == 0)
{
m_i2p_local_endpoint.clear();
return;
}
m_i2p_local_endpoint = dest;
}
void i2p_connection::async_name_lookup(char const* name
, i2p_connection::name_lookup_handler handler)
{
if (m_state == sam_idle && m_name_lookup.empty())
do_name_lookup(name, handler);
else
m_name_lookup.push_back(std::make_pair(std::string(name), handler));
}
void i2p_connection::do_name_lookup(std::string const& name
, name_lookup_handler const& handler)
{
TORRENT_ASSERT(m_state == sam_idle);
m_state = sam_name_lookup;
m_sam_socket->set_name_lookup(name.c_str());
boost::shared_ptr<i2p_stream::handler_type> h(new i2p_stream::handler_type(
boost::bind(&i2p_connection::on_name_lookup, this, _1, handler)));
m_sam_socket->send_name_lookup(h);
}
void i2p_connection::on_name_lookup(error_code const& ec
, name_lookup_handler handler)
{
m_state = sam_idle;
std::string name = m_sam_socket->name_lookup();
if (!m_name_lookup.empty())
{
std::pair<std::string, name_lookup_handler>& nl = m_name_lookup.front();
do_name_lookup(nl.first, nl.second);
m_name_lookup.pop_front();
}
if (ec)
{
handler(ec, 0);
return;
}
handler(ec, name.c_str());
}
// TODO: move this to proxy_base and use it in all proxies
bool i2p_stream::handle_error(error_code const& e, boost::shared_ptr<handler_type> const& h)
{
if (!e) return false;
// fprintf(stderr, "i2p error \"%s\"\n", e.message().c_str());
(*h)(e);
error_code ec;
close(ec);
return true;
}
void i2p_stream::do_connect(error_code const& e, tcp::resolver::iterator i
, boost::shared_ptr<handler_type> h)
{
if (e || i == tcp::resolver::iterator())
{
(*h)(e);
error_code ec;
close(ec);
return;
}
m_sock.async_connect(i->endpoint(), boost::bind(
&i2p_stream::connected, this, _1, h));
}
void i2p_stream::connected(error_code const& e, boost::shared_ptr<handler_type> h)
{
if (handle_error(e, h)) return;
// send hello command
m_state = read_hello_response;
static const char cmd[] = "HELLO VERSION MIN=3.0 MAX=3.0\n";
async_write(m_sock, asio::buffer(cmd, sizeof(cmd) - 1)
, boost::bind(&i2p_stream::start_read_line, this, _1, h));
// fputs(cmd, stderr);
}
void i2p_stream::start_read_line(error_code const& e, boost::shared_ptr<handler_type> h)
{
if (handle_error(e, h)) return;
m_buffer.resize(1);
async_read(m_sock, asio::buffer(m_buffer)
, boost::bind(&i2p_stream::read_line, this, _1, h));
}
char* string_tokenize(char* last, char sep, char** next)
{
if (last == 0) return 0;
*next = strchr(last, sep);
if (*next == 0) return last;
**next = 0;
++(*next);
while (**next == sep && **next) ++(*next);
return last;
}
void i2p_stream::read_line(error_code const& e, boost::shared_ptr<handler_type> h)
{
if (handle_error(e, h)) return;
int read_pos = m_buffer.size();
// fprintf(stderr, "%c", m_buffer[read_pos - 1]);
// look for \n which means end of the response
if (m_buffer[read_pos - 1] != '\n')
{
// read another byte from the socket
m_buffer.resize(read_pos + 1);
async_read(m_sock, asio::buffer(&m_buffer[read_pos], 1)
, boost::bind(&i2p_stream::read_line, this, _1, h));
return;
}
m_buffer[read_pos - 1] = 0;
if (m_command == cmd_incoming)
{
// this is the line containing the destination
// of the incoming connection in an accept call
m_dest = &m_buffer[0];
(*h)(e);
std::vector<char>().swap(m_buffer);
return;
}
error_code invalid_response(i2p_error::parse_failed
, i2p_category);
// null-terminate the string and parse it
m_buffer.push_back(0);
char* ptr = &m_buffer[0];
char* next = ptr;
char const* expect1 = 0;
char const* expect2 = 0;
switch (m_state)
{
case read_hello_response:
expect1 = "HELLO";
expect2 = "REPLY";
break;
case read_connect_response:
case read_accept_response:
expect1 = "STREAM";
expect2 = "STATUS";
break;
case read_session_create_response:
expect1 = "SESSION";
expect2 = "STATUS";
break;
case read_name_lookup_response:
expect1 = "NAMING";
expect2 = "REPLY";
break;
}
ptr = string_tokenize(next, ' ', &next);
if (ptr == 0 || strcmp(expect1, ptr)) { handle_error(invalid_response, h); return; }
ptr = string_tokenize(next, ' ', &next);
if (ptr == 0 || strcmp(expect2, ptr)) { handle_error(invalid_response, h); return; }
int result = 0;
char const* message = 0;
float version = 3.0f;
for(;;)
{
char* name = string_tokenize(next, '=', &next);
if (name == 0) break;
char* ptr = string_tokenize(next, ' ', &next);
if (ptr == 0) { handle_error(invalid_response, h); return; }
if (strcmp("RESULT", name) == 0)
{
if (strcmp("OK", ptr) == 0)
result = i2p_error::no_error;
else if (strcmp("CANT_REACH_PEER", ptr) == 0)
result = i2p_error::cant_reach_peer;
else if (strcmp("I2P_ERROR", ptr) == 0)
result = i2p_error::i2p_error;
else if (strcmp("INVALID_KEY", ptr) == 0)
result = i2p_error::invalid_key;
else if (strcmp("INVALID_ID", ptr) == 0)
result = i2p_error::invalid_id;
else if (strcmp("TIMEOUT", ptr) == 0)
result = i2p_error::timeout;
else if (strcmp("KEY_NOT_FOUND", ptr) == 0)
result = i2p_error::key_not_found;
else
result = i2p_error::num_errors; // unknown error
}
else if (strcmp("MESSAGE", name) == 0)
{
message = ptr;
}
else if (strcmp("VERSION", name) == 0)
{
version = atof(ptr);
}
else if (strcmp("VALUE", name) == 0)
{
m_name_lookup = ptr;
}
else if (strcmp("DESTINATION", name) == 0)
{
m_dest = ptr;
}
}
if (result != i2p_error::no_error)
{
error_code ec(result, i2p_category);
handle_error(ec, h);
return;
}
switch (m_state)
{
case read_hello_response:
switch (m_command)
{
case cmd_create_session:
send_session_create(h);
break;
case cmd_accept:
send_accept(h);
break;
case cmd_connect:
send_connect(h);
break;
default:
(*h)(e);
std::vector<char>().swap(m_buffer);
}
break;
case read_connect_response:
case read_session_create_response:
case read_name_lookup_response:
(*h)(e);
std::vector<char>().swap(m_buffer);
break;
case read_accept_response:
// the SAM bridge is waiting for an incoming
// connection.
// wait for one more line containing
// the destination of the remote peer
m_command = cmd_incoming;
m_buffer.resize(1);
async_read(m_sock, asio::buffer(m_buffer)
, boost::bind(&i2p_stream::read_line, this, _1, h));
break;
}
return;
}
void i2p_stream::send_connect(boost::shared_ptr<handler_type> h)
{
m_state = read_connect_response;
char cmd[1024];
int size = snprintf(cmd, sizeof(cmd), "STREAM CONNECT ID=%s DESTINATION=%s\n"
, m_id, m_dest.c_str());
// fputs(cmd, stderr);
async_write(m_sock, asio::buffer(cmd, size)
, boost::bind(&i2p_stream::start_read_line, this, _1, h));
}
void i2p_stream::send_accept(boost::shared_ptr<handler_type> h)
{
m_state = read_accept_response;
char cmd[400];
int size = snprintf(cmd, sizeof(cmd), "STREAM ACCEPT ID=%s\n", m_id);
// fputs(cmd, stderr);
async_write(m_sock, asio::buffer(cmd, size)
, boost::bind(&i2p_stream::start_read_line, this, _1, h));
}
void i2p_stream::send_session_create(boost::shared_ptr<handler_type> h)
{
m_state = read_session_create_response;
char cmd[400];
int size = snprintf(cmd, sizeof(cmd), "SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT\n"
, m_id);
// fputs(cmd, stderr);
async_write(m_sock, asio::buffer(cmd, size)
, boost::bind(&i2p_stream::start_read_line, this, _1, h));
}
void i2p_stream::send_name_lookup(boost::shared_ptr<handler_type> h)
{
m_state = read_name_lookup_response;
char cmd[1024];
int size = snprintf(cmd, sizeof(cmd), "NAMING LOOKUP NAME=%s\n", m_name_lookup.c_str());
// fputs(cmd, stderr);
async_write(m_sock, asio::buffer(cmd, size)
, boost::bind(&i2p_stream::start_read_line, this, _1, h));
}
}
#endif

View File

@ -67,6 +67,13 @@ namespace libtorrent
if (ps.type == proxy_settings::socks4)
s.get<socks5_stream>()->set_version(4);
}
#if TORRENT_USE_I2P
else if (ps.type == proxy_settings::i2p_proxy)
{
s.instantiate<i2p_stream>(ios);
s.get<i2p_stream>()->set_proxy(ps.hostname, ps.port);
}
#endif
else
{
return false;

View File

@ -150,6 +150,14 @@ namespace libtorrent
, m_received_in_piece(0)
#endif
{
#if TORRENT_USE_I2P
if (peerinfo && peerinfo->is_i2p_addr)
{
// quadruple the timeout for i2p peers
m_timeout *= 4;
}
#endif
m_channel_state[upload_channel] = peer_info::bw_idle;
m_channel_state[download_channel] = peer_info::bw_idle;
@ -271,6 +279,14 @@ namespace libtorrent
, m_received_in_piece(0)
#endif
{
#if TORRENT_USE_I2P
if (peerinfo && peerinfo->is_i2p_addr)
{
// quadruple the timeout for i2p peers
m_timeout *= 4;
}
#endif
m_channel_state[upload_channel] = peer_info::bw_idle;
m_channel_state[download_channel] = peer_info::bw_idle;
@ -938,6 +954,18 @@ namespace libtorrent
return;
}
i2p_stream* i2ps = m_socket->get<i2p_stream>();
if (!i2ps && t->torrent_file().is_i2p() && !m_ses.m_settings.allow_i2p_mixed)
{
// the torrent is an i2p torrent, the peer is a regular peer
// and we don't allow mixed mode. Disconnect the peer.
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_logger) << " rejected regular connection to i2p torrent\n";
#endif
disconnect(error_code(errors::peer_banned, libtorrent_category), 2);
return;
}
TORRENT_ASSERT(m_torrent.expired());
// check to make sure we don't have another connection with the same
// info_hash and peer_id. If we do. close this connection.

View File

@ -355,6 +355,12 @@ namespace libtorrent
m_torrent->session().m_ipv6_peer_pool.destroy(
static_cast<ipv6_peer*>(*i));
else
#endif
#if TORRENT_USE_I2P
if ((*i)->is_i2p_addr)
m_torrent->session().m_i2p_peer_pool.destroy(
static_cast<i2p_peer*>(*i));
else
#endif
m_torrent->session().m_ipv4_peer_pool.destroy(
static_cast<ipv4_peer*>(*i));
@ -512,7 +518,7 @@ namespace libtorrent
for (int iterations = (std::min)(int(m_peers.size()), 300);
iterations > 0; --iterations)
{
if (m_round_robin == m_peers.size()) m_round_robin = 0;
if (m_round_robin == int(m_peers.size())) m_round_robin = 0;
peer& pe = *m_peers[m_round_robin];
int current = m_round_robin;
@ -878,6 +884,156 @@ namespace libtorrent
TORRENT_ASSERT(m_num_seeds <= m_peers.size());
}
bool policy::insert_peer(policy::peer* p, iterator iter, int flags)
{
TORRENT_ASSERT(p);
int max_peerlist_size = m_torrent->is_paused()
?m_torrent->settings().max_paused_peerlist_size
:m_torrent->settings().max_peerlist_size;
if (max_peerlist_size
&& int(m_peers.size()) >= max_peerlist_size)
{
if (p->source == peer_info::resume_data) return false;
erase_peers();
if (int(m_peers.size()) >= max_peerlist_size)
return 0;
// since some peers were removed, we need to
// update the iterator to make it valid again
#if TORRENT_USE_I2P
if (p->is_i2p_addr)
{
iter = std::lower_bound(
m_peers.begin(), m_peers.end()
, p->dest(), peer_address_compare());
}
else
#endif
iter = std::lower_bound(
m_peers.begin(), m_peers.end()
, p->address(), peer_address_compare());
}
if (m_round_robin > iter - m_peers.begin()) ++m_round_robin;
iter = m_peers.insert(iter, p);
#ifndef TORRENT_DISABLE_ENCRYPTION
if (flags & 0x01) p->pe_support = true;
#endif
if (flags & 0x02)
{
p->seed = true;
++m_num_seeds;
}
#ifndef TORRENT_DISABLE_GEO_IP
int as = ses.as_for_ip(remote.address());
#ifdef TORRENT_DEBUG
p->inet_as_num = as;
#endif
p->inet_as = ses.lookup_as(as);
#endif
if (is_connect_candidate(*p, m_finished))
++m_num_connect_candidates;
return true;
}
void policy::update_peer(policy::peer* p, int src, int flags
, tcp::endpoint const& remote, char const* destination)
{
bool was_conn_cand = is_connect_candidate(*p, m_finished);
p->connectable = true;
TORRENT_ASSERT(p->address() == remote.address());
p->port = remote.port();
p->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 (p->failcount > 0 && src == peer_info::tracker)
--p->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) && !p->connection)
{
if (!p->seed) ++m_num_seeds;
p->seed = true;
}
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
if (p->connection)
{
// this means we're already connected
// to this peer. don't connect to
// it again.
error_code ec;
char hex_pid[41];
to_hex((char*)&p->connection->pid()[0], 20, hex_pid);
char msg[200];
snprintf(msg, 200, "already connected to peer: %s %s"
, print_endpoint(remote).c_str(), hex_pid);
m_torrent->debug_log(msg);
TORRENT_ASSERT(p->connection->associated_torrent().lock().get() == m_torrent);
}
#endif
if (was_conn_cand != is_connect_candidate(*p, m_finished))
{
m_num_connect_candidates += was_conn_cand ? -1 : 1;
if (m_num_connect_candidates < 0) m_num_connect_candidates = 0;
}
}
policy::peer* policy::add_i2p_peer(char const* destination, int src, char flags)
{
INVARIANT_CHECK;
aux::session_impl& ses = m_torrent->session();
bool found = false;
iterator iter = std::lower_bound(
m_peers.begin(), m_peers.end()
, destination, peer_address_compare()
);
if (iter != m_peers.end() && strcmp((*iter)->dest(), destination) == 0)
found = true;
peer* p = 0;
if (!found)
{
// we don't have any info about this peer.
// add a new entry
p = (peer*)m_torrent->session().m_i2p_peer_pool.malloc();
if (p == 0) return 0;
m_torrent->session().m_i2p_peer_pool.set_next_size(500);
new (p) i2p_peer(destination, true, src);
if (!insert_peer(p, iter, flags))
{
m_torrent->session().m_i2p_peer_pool.free((i2p_peer*)p);
return 0;
}
}
else
{
p = *iter;
update_peer(p, src, flags, tcp::endpoint(), destination);
}
return p;
}
policy::peer* policy::add_peer(tcp::endpoint const& remote, peer_id const& pid
, int src, char flags)
{
@ -889,6 +1045,11 @@ namespace libtorrent
aux::session_impl& ses = m_torrent->session();
// if this is an i2p torrent, and we don't allow mixed mode
// no regular peers should ever be added!
if (!ses.m_settings.allow_i2p_mixed && m_torrent->torrent_file().is_i2p())
return 0;
port_filter const& pf = ses.m_port_filter;
if (pf.access(remote.port()) & port_filter::blocked)
{
@ -901,14 +1062,12 @@ namespace libtorrent
if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked)
{
if (ses.m_alerts.should_post<peer_blocked_alert>())
{
ses.m_alerts.post_alert(peer_blocked_alert(remote.address()));
}
return 0;
}
iterator iter;
peer* i = 0;
peer* p = 0;
int max_peerlist_size = m_torrent->is_paused()
?m_torrent->settings().max_paused_peerlist_size
@ -933,31 +1092,13 @@ namespace libtorrent
if (!found)
{
if (max_peerlist_size
&& int(m_peers.size()) >= max_peerlist_size)
{
if (src == peer_info::resume_data) return 0;
erase_peers();
if (int(m_peers.size()) >= max_peerlist_size)
return 0;
// since some peers were removed, we need to
// update the iterator to make it valid again
iter = std::lower_bound(
m_peers.begin(), m_peers.end()
, remote.address(), peer_address_compare()
);
}
if (m_round_robin > iter - m_peers.begin()) ++m_round_robin;
// we don't have any info about this peer.
// add a new entry
#if TORRENT_USE_IPV6
bool is_v6 = remote.address().is_v6();
#endif
peer* p =
p =
#if TORRENT_USE_IPV6
is_v6 ? (peer*)m_torrent->session().m_ipv6_peer_pool.malloc() :
#endif
@ -977,83 +1118,23 @@ namespace libtorrent
#endif
new (p) ipv4_peer(remote, true, src);
iter = m_peers.insert(iter, p);
i = *iter;
#ifndef TORRENT_DISABLE_ENCRYPTION
if (flags & 0x01) i->pe_support = true;
#endif
if (flags & 0x02)
if (!insert_peer(p, iter, flags))
{
i->seed = true;
++m_num_seeds;
#if TORRENT_USE_IPV6
if (is_v6) m_torrent->session().m_ipv6_peer_pool.free((ipv6_peer*)p);
else
#endif
m_torrent->session().m_ipv4_peer_pool.free((ipv4_peer*)p);
return 0;
}
#ifndef TORRENT_DISABLE_GEO_IP
int as = ses.as_for_ip(remote.address());
#ifdef TORRENT_DEBUG
i->inet_as_num = as;
#endif
i->inet_as = ses.lookup_as(as);
#endif
if (is_connect_candidate(*i, m_finished))
++m_num_connect_candidates;
}
else
{
i = *iter;
bool was_conn_cand = is_connect_candidate(*i, m_finished);
i->connectable = true;
TORRENT_ASSERT(i->address() == remote.address());
i->port = remote.port();
i->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->failcount > 0 && src == peer_info::tracker)
--i->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->connection)
{
if (!i->seed) ++m_num_seeds;
i->seed = true;
}
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
if (i->connection)
{
// this means we're already connected
// to this peer. don't connect to
// it again.
error_code ec;
char hex_pid[41];
to_hex((char*)&i->connection->pid()[0], 20, hex_pid);
char msg[200];
snprintf(msg, 200, "already connected to peer: %s %s"
, print_endpoint(remote).c_str(), hex_pid);
m_torrent->debug_log(msg);
TORRENT_ASSERT(i->connection->associated_torrent().lock().get() == m_torrent);
}
#endif
if (was_conn_cand != is_connect_candidate(*i, m_finished))
{
m_num_connect_candidates += was_conn_cand ? -1 : 1;
if (m_num_connect_candidates < 0) m_num_connect_candidates = 0;
}
p = *iter;
update_peer(p, src, flags, remote, 0);
}
return i;
return p;
}
bool policy::connect_one_peer(int session_time)
@ -1326,6 +1407,9 @@ namespace libtorrent
#endif
#if TORRENT_USE_IPV6
, is_v6_addr(false)
#endif
#if TORRENT_USE_I2P
, is_i2p_addr(false)
#endif
, on_parole(false)
, banned(false)

View File

@ -715,6 +715,20 @@ namespace libtorrent
}
#endif
#if TORRENT_USE_I2P
void session::set_i2p_proxy(proxy_settings const& s)
{
session_impl::mutex_t::scoped_lock l(m_impl->m_mutex);
m_impl->set_i2p_proxy(s);
}
proxy_settings const& session::i2p_proxy() const
{
session_impl::mutex_t::scoped_lock l(m_impl->m_mutex);
return m_impl->i2p_proxy();
}
#endif
int session::max_uploads() const
{
session_impl::mutex_t::scoped_lock l(m_impl->m_mutex);

View File

@ -192,6 +192,9 @@ namespace aux {
#endif
, m_tracker_manager(*this, m_tracker_proxy)
, m_listen_port_retries(listen_port_range.second - listen_port_range.first)
#if TORRENT_USE_I2P
, m_i2p_conn(m_io_service)
#endif
, m_abort(false)
, m_paused(false)
, m_max_uploads(8)
@ -524,6 +527,9 @@ namespace aux {
#endif
// abort the main thread
m_abort = true;
#if TORRENT_USE_I2P
m_i2p_conn.close();
#endif
m_queued_for_checking.clear();
if (m_lsd) m_lsd->close();
if (m_upnp) m_upnp->close();
@ -839,6 +845,9 @@ namespace aux {
}
open_new_incoming_socks_connection();
#if TORRENT_USE_I2P
open_new_incoming_i2p_connection();
#endif
if (!m_listen_sockets.empty())
{
@ -886,6 +895,47 @@ namespace aux {
, boost::bind(&session_impl::on_socks_accept, this, m_socks_listen_socket, _1));
}
#if TORRENT_USE_I2P
void session_impl::on_i2p_open(error_code const& ec)
{
open_new_incoming_i2p_connection();
}
void session_impl::open_new_incoming_i2p_connection()
{
if (!m_i2p_conn.is_open()) return;
if (m_i2p_listen_socket) return;
m_i2p_listen_socket = boost::shared_ptr<socket_type>(new socket_type(m_io_service));
bool ret = instantiate_connection(m_io_service, m_i2p_conn.proxy()
, *m_i2p_listen_socket);
TORRENT_ASSERT(ret);
i2p_stream& s = *m_i2p_listen_socket->get<i2p_stream>();
s.set_command(i2p_stream::cmd_accept);
s.set_session_id(m_i2p_conn.session_id());
s.async_connect(tcp::endpoint(address_v4::any(), m_listen_interface.port())
, boost::bind(&session_impl::on_i2p_accept, this, m_i2p_listen_socket, _1));
}
void session_impl::on_i2p_accept(boost::shared_ptr<socket_type> const& s
, error_code const& e)
{
m_i2p_listen_socket.reset();
if (e == asio::error::operation_aborted) return;
if (e)
{
if (m_alerts.should_post<listen_failed_alert>())
m_alerts.post_alert(listen_failed_alert(tcp::endpoint(
address_v4::any(), m_listen_interface.port()), e));
return;
}
open_new_incoming_i2p_connection();
incoming_connection(s);
}
#endif
#ifndef TORRENT_DISABLE_DHT
void session_impl::on_receive_udp(error_code const& e
@ -2323,7 +2373,8 @@ namespace aux {
boost::shared_ptr<torrent> t = find_torrent(ih).lock();
if (!t) return;
// don't add peers from lsd to private torrents
if (t->torrent_file().priv()) return;
if (t->torrent_file().priv() || (t->torrent_file().is_i2p()
&& !m_settings.allow_i2p_mixed)) return;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string()

View File

@ -1138,9 +1138,13 @@ namespace libtorrent
if (m_abort) return;
TORRENT_ASSERT(!m_torrent_file->priv());
if (m_torrent_file->is_valid() && m_torrent_file->priv())
if (m_torrent_file->is_valid()
&& (m_torrent_file->priv()
|| (torrent_file().is_i2p()
&& !m_settings.allow_i2p_mixed)))
return;
if (is_paused()) return;
boost::weak_ptr<torrent> self(shared_from_this());
@ -1187,6 +1191,10 @@ namespace libtorrent
m_ses.m_alerts.post_alert(dht_reply_alert(
get_handle(), peers.size()));
}
if (torrent_file().priv() || (torrent_file().is_i2p()
&& !m_settings.allow_i2p_mixed)) return;
std::for_each(peers.begin(), peers.end(), bind(
&policy::add_peer, boost::ref(m_policy), _1, peer_id(0)
, peer_info::dht, 0));
@ -1428,9 +1436,30 @@ namespace libtorrent
// assume this is because we got a hostname instead of
// an ip address from the tracker
tcp::resolver::query q(i->ip, to_string(i->port).elems);
m_host_resolver.async_resolve(q,
bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid));
#if TORRENT_USE_I2P
char const* top_domain = strrchr(i->ip.c_str(), '.');
if (top_domain && strcmp(top_domain, ".i2p") == 0 && m_ses.m_i2p_conn.is_open())
{
// this is an i2p name, we need to use the sam connection
// to do the name lookup
/*
m_ses.m_i2p_conn.async_name_lookup(i->ip.c_str()
, bind(&torrent::on_i2p_resolve
, shared_from_this(), _1, _2));
*/
// it seems like you're not supposed to do a name lookup
// on the peers returned from the tracker, but just strip
// the .i2p and use it as a destination
i->ip.resize(i->ip.size() - 4);
m_policy.add_i2p_peer(i->ip.c_str(), peer_info::tracker, 0);
}
else
#endif
{
tcp::resolver::query q(i->ip, to_string(i->port).elems);
m_host_resolver.async_resolve(q,
bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid));
}
}
else
{
@ -1483,6 +1512,19 @@ namespace libtorrent
}
}
#if TORRENT_USE_I2P
void torrent::on_i2p_resolve(error_code const& ec, char const* dest)
{
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
INVARIANT_CHECK;
if (ec || m_ses.is_aborted()) return;
m_policy.add_i2p_peer(dest, peer_info::tracker, 0);
}
#endif
void torrent::on_peer_name_lookup(error_code const& e, tcp::resolver::iterator host
, peer_id pid)
{
@ -1500,10 +1542,7 @@ namespace libtorrent
debug_log("blocked ip from tracker: " + host->endpoint().address().to_string(ec));
#endif
if (m_ses.m_alerts.should_post<peer_blocked_alert>())
{
m_ses.m_alerts.post_alert(peer_blocked_alert(host->endpoint().address()));
}
return;
}
@ -3837,7 +3876,11 @@ namespace libtorrent
peer_iterator i_ = std::find_if(m_connections.begin(), m_connections.end()
, bind(&peer_connection::remote, _1) == peerinfo->ip());
TORRENT_ASSERT(i_ == m_connections.end()
|| dynamic_cast<bt_peer_connection*>(*i_) == 0);
|| dynamic_cast<bt_peer_connection*>(*i_) == 0
#if TORRENT_USE_I2P
|| peerinfo->is_i2p_addr
#endif
);
#endif
TORRENT_ASSERT(want_more_peers());
@ -3848,9 +3891,24 @@ namespace libtorrent
boost::shared_ptr<socket_type> s(new socket_type(m_ses.m_io_service));
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.peer_proxy(), *s);
(void)ret;
TORRENT_ASSERT(ret);
#if TORRENT_USE_I2P
bool i2p = peerinfo->is_i2p_addr;
if (i2p)
{
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.i2p_proxy(), *s);
(void)ret;
TORRENT_ASSERT(ret);
s->get<i2p_stream>()->set_destination(static_cast<policy::i2p_peer*>(peerinfo)->destination);
s->get<i2p_stream>()->set_command(i2p_stream::cmd_connect);
s->get<i2p_stream>()->set_session_id(m_ses.m_i2p_conn.session_id());
}
else
#endif
{
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.peer_proxy(), *s);
(void)ret;
TORRENT_ASSERT(ret);
}
m_ses.setup_socket_buffers(*s);
@ -5068,7 +5126,10 @@ namespace libtorrent
// private torrents are never announced on LSD
// or on DHT, we don't need this timer.
if (!m_torrent_file->is_valid() || !m_torrent_file->priv())
if (!m_torrent_file->is_valid()
|| (!m_torrent_file->priv()
&& (!m_torrent_file->is_i2p()
|| m_settings.allow_i2p_mixed)))
{
error_code ec;
boost::weak_ptr<torrent> self(shared_from_this());

View File

@ -62,6 +62,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/file.hpp"
#include "libtorrent/utf8.hpp"
#if TORRENT_USE_I2P
#include "libtorrent/parse_url.hpp"
#endif
namespace gr = boost::gregorian;
namespace libtorrent
@ -366,6 +370,7 @@ namespace libtorrent
: m_creation_date(pt::ptime(pt::not_a_date_time))
, m_multifile(false)
, m_private(false)
, m_i2p(false)
, m_info_section_size(0)
, m_piece_hashes(0)
, m_merkle_first_leaf(0)
@ -579,6 +584,7 @@ namespace libtorrent
m_created_by.swap(ti.m_created_by);
swap(m_multifile, ti.m_multifile);
swap(m_private, ti.m_private);
swap(m_i2p, ti.m_i2p);
swap(m_info_section, ti.m_info_section);
swap(m_info_section_size, ti.m_info_section_size);
swap(m_piece_hashes, ti.m_piece_hashes);
@ -813,6 +819,19 @@ namespace libtorrent
return ret;
}
#if TORRENT_USE_I2P
bool is_i2p_url(std::string const& url)
{
using boost::tuples::ignore;
std::string hostname;
error_code ec;
boost::tie(ignore, ignore, hostname, ignore, ignore)
= parse_url_components(url, ec);
char const* top_domain = strrchr(hostname.c_str(), '.');
return top_domain && strcmp(top_domain, ".i2p") == 0;
}
#endif
bool torrent_info::parse_torrent_file(lazy_entry const& torrent_file, error_code& ec)
{
if (torrent_file.type() != lazy_entry::dict_t)
@ -838,6 +857,9 @@ namespace libtorrent
e.fail_limit = 0;
e.source = announce_entry::source_torrent;
e.trim();
#if TORRENT_USE_I2P
if (is_i2p_url(e.url)) m_i2p = true;
#endif
m_urls.push_back(e);
}
}
@ -868,6 +890,9 @@ namespace libtorrent
e.fail_limit = 0;
e.source = announce_entry::source_torrent;
e.trim();
#if TORRENT_USE_I2P
if (is_i2p_url(e.url)) m_i2p = true;
#endif
if (!e.url.empty()) m_urls.push_back(e);
}

View File

@ -234,7 +234,11 @@ namespace libtorrent
{
con = new http_tracker_connection(
ios, cc, *this, req, c
, m_ses, m_proxy, auth);
, m_ses, m_proxy, auth
#if TORRENT_USE_I2P
, &m_ses.m_i2p_conn
#endif
);
}
else if (protocol == "udp")
{

View File

@ -436,7 +436,8 @@ namespace libtorrent
boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
{
if (t->torrent_file().priv())
if (t->torrent_file().priv() || (t->torrent_file().is_i2p()
&& !t->settings().allow_i2p_mixed))
{
return boost::shared_ptr<torrent_plugin>();
}