diff --git a/CMakeLists.txt b/CMakeLists.txt index e3dc6dbac..154355a5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(sources http_connection http_stream http_parser + i2p_stream identify_client ip_filter peer_connection diff --git a/ChangeLog b/ChangeLog index 878595203..a0e582be8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * added support for i2p torrents + 0.15 release * reduced the number of floating point operations (to better support diff --git a/Jamfile b/Jamfile index 73922fd33..713b99643 100755 --- a/Jamfile +++ b/Jamfile @@ -358,6 +358,7 @@ SOURCES = bt_peer_connection web_peer_connection http_seed_connection + i2p_stream instantiate_connection natpmp piece_picker diff --git a/docs/manual.rst b/docs/manual.rst index 2cc08920f..e5fb9aea8 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -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() ----------------------------------------------------- diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 7c0ea6968..ed2c2023d 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -747,6 +747,9 @@ int main(int argc, char* argv[]) " -t sets the scan interval of the monitor dir\n" " -x loads an emule IP-filter file\n" " -c sets the max number of connections\n" +#if TORRENT_USE_I2P + " -i the hostname to an I2P SAM bridge to use\n" +#endif " -C sets the max cache size. Specified in 16kB blocks\n" " -F 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 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 diff --git a/include/Makefile.am b/include/Makefile.am index a3dce8c58..67ce6bc94 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -33,6 +33,7 @@ libtorrent/http_cHTTP/1.1 200 OK Date: Wed, 23 Jul 2025 07:26:14 GMT Content-Type: text/plain; charset=utf-8 Connection: close Transfer-Encoding: chunked Cache-Control: max-age=0, private, must-revalidate, no-transform Set-Cookie: i_like_gitea=87253c5a023870e5; Path=/; HttpOnly; Secure; SameSite=Lax Set-Cookie: _csrf=JI2YjFv_ARhU_zjSoBnf-_0jzjs6MTc1MzI1NTU3NDAzOTU3MjAxMg; Path=/; Max-Age=86400; HttpOnly; Secure; SameSite=Lax X-Frame-Options: SAMEORIGIN X-Cache-Status: HIT X-Cache-Age: 0 5000 diff --git a/CMakeLists.txt b/CMakeLists.txt index e3dc6dbac..154355a5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(sources http_connection http_stream http_parser + i2p_stream identify_client ip_filter peer_connection diff --git a/ChangeLog b/ChangeLog index 878595203..a0e582be8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * added support for i2p torrents + 0.15 release * reduced the number of floating point operations (to better support diff --git a/Jamfile b/Jamfile index 73922fd33..713b99643 100755 --- a/Jamfile +++ b/Jamfile @@ -358,6 +358,7 @@ SOURCES = bt_peer_connection web_peer_connection http_seed_connection + i2p_stream instantiate_connection natpmp piece_picker diff --git a/docs/manual.rst b/docs/manual.rst index 2cc08920f..e5fb9aea8 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -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() ----------------------------------------------------- diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 7c0ea6968..ed2c2023d 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -747,6 +747,9 @@ int main(int argc, char* argv[]) " -t sets the scan interval of the monitor dir\n" " -x loads an emule IP-filter file\n" " -c sets the max number of connections\n" +#if TORRENT_USE_I2P + " -i the hostname to an I2P SAM bridge to use\n" +#endif " -C sets the max cache size. Specified in 16kB blocks\n" " -F 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 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 diff --git a/include/Makefile.am b/include/Makefile.am index a3dce8c58..67ce6bc94 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -33,6 +33,7 @@ libtorrent/http_c 1000 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(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(this)->addr); + else +#endif +#if TORRENT_USE_I2P + if (is_i2p_addr) return libtorrent::address(); + else #endif return static_cast(this)->addr; } diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 980d2de59..6afc5ed09 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -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; diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index 4d923d263..2723c78a4 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -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 diff --git a/include/libtorrent/socket_type.hpp b/include/libtorrent/socket_type.hpp index 01fb3f9be..d4026336a 100644 --- a/include/libtorrent/socket_type.hpp +++ b/include/libtorrent/socket_type.hpp @@ -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 diff --git a/include/libtorrent/time.hpp b/include/libtorrent/time.hpp index 464dba78f..d0502cb55 100644 --- a/include/libtorrent/time.hpp +++ b/include/libtorrent/time.hpp @@ -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; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index ae76b0a05..b730125c4 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -513,6 +513,10 @ 1000 @ 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 diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp index efeb7ee39..9fbfcb790 100644 --- a/include/libtorrent/torrent_info.hpp +++ b/include/libtorrent/torrent_info.hpp @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 4e20f97c1..205c52677 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/error_code.cpp b/src/error_code.cpp index 4d9c8cdc0..c920f11c0 100644 --- a/src/error_code.cpp +++ b/src/error_code.cpp @@ -184,6 +184,7 @@ namespace libtorrent "", "", "", + "no i2p router is set up", }; if (ev < 0 || ev >= sizeof(msgs)/sizeof(msgs[0])) return "Unknown error"; diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 2f59c943f..905d357d3 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -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(st