From fd5f1bf80bc19db6035d520f5e49cab7097186c5 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Tue, 3 Aug 2010 09:08:37 +0000 Subject: [PATCH] improved SOCKS5 support --- ChangeLog | 1 + docs/manual.rst | 5 + include/libtorrent/aux_/session_impl.hpp | 3 + include/libtorrent/http_connection.hpp | 2 + include/libtorrent/http_stream.hpp | 18 +++ include/libtorrent/proxy_base.hpp | 19 +++- include/libtorrent/session_settings.hpp | 10 +- include/libtorrent/socks5_stream.hpp | 24 +++- include/libtorrent/tracker_manager.hpp | 6 + include/libtorrent/udp_socket.hpp | 18 ++- include/libtorrent/udp_tracker_connection.hpp | 8 +- src/http_connection.cpp | 27 +++++ src/http_stream.cpp | 12 +- src/peer_connection.cpp | 68 +++++++---- src/session_impl.cpp | 15 ++- src/socks5_stream.cpp | 24 +++- src/torrent.cpp | 57 +++++++--- src/tracker_manager.cpp | 14 +++ src/udp_socket.cpp | 106 ++++++++++++++++-- src/udp_tracker_connection.cpp | 71 ++++++++++-- src/web_peer_connection.cpp | 2 +- test/test_http_connection.cpp | 3 +- 22 files changed, 434 insertions(+), 79 deletions(-) diff --git a/ChangeLog b/ChangeLog index 259e1da2e..da011e14d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * improved SOCKS5 support by proxying hostname lookups * improved support for multi-homed clients * added feature to not count downloaded bytes from web seeds in stats * added alert for incoming local service discovery messages diff --git a/docs/manual.rst b/docs/manual.rst index d450fc76e..b160d8d01 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -4679,6 +4679,7 @@ direct certain traffic to a proxy. }; proxy_type type; + bool proxy_hostnames; }; ``hostname`` is the name or IP of the proxy server. ``port`` is the @@ -4715,6 +4716,10 @@ options are available: .. _`RFC 1929`: http://www.faqs.org/rfcs/rfc1929.html .. _CONNECT: draft-luotonen-web-proxy-tunneling-01.txt +``proxy_hostnames`` defaults to true. It means that hostnames should be +attempted to be resolved through the proxy instead of using the local DNS +service. This is only supported by SOCKS5 and HTTP. + ip_filter ========= diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 17f44367f..9b33b999b 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -717,6 +717,9 @@ namespace libtorrent void on_receive_udp(error_code const& e , udp::endpoint const& ep, char const* buf, int len); + void on_receive_udp_hostname(error_code const& e + , char const* hostname, char const* buf, int len); + // this announce timer is used // by the DHT. deadline_timer m_dht_announce_timer; diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index 763e657fb..f40798426 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -160,6 +160,8 @@ private: std::vector m_recvbuffer; #ifdef TORRENT_USE_OPENSSL + // TODO: for proxies to work correctly over SSL, the + // ssl_stream needs to be wrapped inside the socket_type variant_stream > m_sock; #else socket_type m_sock; diff --git a/include/libtorrent/http_stream.hpp b/include/libtorrent/http_stream.hpp index 4a5dc84f3..3139ef29b 100644 --- a/include/libtorrent/http_stream.hpp +++ b/include/libtorrent/http_stream.hpp @@ -57,6 +57,23 @@ public: m_password = password; } + void set_dst_name(std::string const& host) + { + m_dst_name = host; + } + + void close(error_code& ec) + { + m_dst_name.clear(); + proxy_base::close(ec); + } + + void close() + { + m_dst_name.clear(); + proxy_base::close(); + } + typedef boost::function handler_type; template @@ -92,6 +109,7 @@ private: // proxy authentication std::string m_user; std::string m_password; + std::string m_dst_name; // this is true if the connection is HTTP based and // want to talk directly to the proxy diff --git a/include/libtorrent/proxy_base.hpp b/include/libtorrent/proxy_base.hpp index 8b35c85ef..9ff726cde 100644 --- a/include/libtorrent/proxy_base.hpp +++ b/include/libtorrent/proxy_base.hpp @@ -122,25 +122,35 @@ public: #ifndef BOOST_NO_EXCEPTIONS void bind(endpoint_type const& endpoint) { - m_sock.bind(endpoint); +// m_sock.bind(endpoint); } #endif void bind(endpoint_type const& endpoint, error_code& ec) { - m_sock.bind(endpoint, ec); + // the reason why we ignore binds here is because we don't + // (necessarily) yet know what address family the proxy + // will resolve to, and binding to the wrong one would + // break our connection attempt later. The caller here + // doesn't necessarily know that we're proxying, so this + // bind address is based on the final endpoint, not the + // proxy. + // TODO: it would be nice to remember the bind port and bind once we know where the proxy is +// m_sock.bind(endpoint, ec); } #ifndef BOOST_NO_EXCEPTIONS void open(protocol_type const& p) { - m_sock.open(p); +// m_sock.open(p); } #endif void open(protocol_type const& p, error_code& ec) { - m_sock.open(p, ec); + // we need to ignore this for the same reason as stated + // for ignoring bind() +// m_sock.open(p, ec); } #ifndef BOOST_NO_EXCEPTIONS @@ -154,6 +164,7 @@ public: void close(error_code& ec) { + m_remote_endpoint = endpoint_type(); m_sock.close(ec); m_resolver.cancel(); } diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index d2977138b..fa1437f68 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -42,7 +42,8 @@ namespace libtorrent struct TORRENT_EXPORT proxy_settings { - proxy_settings() : port(0), type(none) {} + proxy_settings() : port(0), type(none) + , proxy_hostnames(true) {} std::string hostname; int port; @@ -78,7 +79,10 @@ namespace libtorrent }; proxy_type type; - + + // when set to true, hostname are resolved + // through the proxy (if supported) + bool proxy_hostnames; }; struct TORRENT_EXPORT session_settings @@ -105,7 +109,7 @@ namespace libtorrent , allow_multiple_connections_per_ip(false) , max_failcount(3) , min_reconnect_time(60) - , peer_connect_timeout(7) + , peer_connect_timeout(15) , ignore_limits_on_local_network(true) , connection_speed(10) , send_redundant_have(false) diff --git a/include/libtorrent/socks5_stream.hpp b/include/libtorrent/socks5_stream.hpp index 92d218252..9a668ad9a 100644 --- a/include/libtorrent/socks5_stream.hpp +++ b/include/libtorrent/socks5_stream.hpp @@ -96,6 +96,27 @@ public: m_password = password; } + void set_dst_name(std::string const& host) + { + m_dst_name = host; + if (m_dst_name.size() > 255) + m_dst_name.resize(255); + } + + void close(error_code& ec) + { + m_hostname.clear(); + m_dst_name.clear(); + proxy_base::close(ec); + } + + void close() + { + m_hostname.clear(); + m_dst_name.clear(); + proxy_base::close(); + } + typedef boost::function handler_type; //#error fix error messages to use custom error_code category @@ -112,7 +133,7 @@ public: // 3.1 send SOCKS5 authentication method message // 3.2 read SOCKS5 authentication response // 3.3 send username+password - // 4 send SOCKS command message + // 4. send SOCKS command message // to avoid unnecessary copying of the handler, // store it in a shaed_ptr @@ -142,6 +163,7 @@ private: // proxy authentication std::string m_user; std::string m_password; + std::string m_dst_name; int m_version; int m_command; // set to one when we're waiting for the diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp index 21becdf0f..103083025 100644 --- a/include/libtorrent/tracker_manager.hpp +++ b/include/libtorrent/tracker_manager.hpp @@ -211,6 +211,8 @@ namespace libtorrent void received_bytes(int bytes); virtual bool on_receive(error_code const& ec, udp::endpoint const& ep , char const* buf, int size) { return false; } + virtual bool on_receive_hostname(error_code const& ec, char const* hostname + , char const* buf, int size) { return false; } protected: boost::weak_ptr m_requester; @@ -246,6 +248,10 @@ namespace libtorrent void received_bytes(int bytes); bool incoming_udp(error_code const& e, udp::endpoint const& ep, char const* buf, int size); + + // this is only used for SOCKS packets, since + // they may be addressed to hostname + bool incoming_udp(error_code const& e, char const* hostname, char const* buf, int size); private: diff --git a/include/libtorrent/udp_socket.hpp b/include/libtorrent/udp_socket.hpp index beb901b52..378874acf 100644 --- a/include/libtorrent/udp_socket.hpp +++ b/include/libtorrent/udp_socket.hpp @@ -53,8 +53,10 @@ namespace libtorrent public: typedef boost::function callback_t; + typedef boost::function callback2_t; - udp_socket(io_service& ios, callback_t const& c, connection_queue& cc); + udp_socket(io_service& ios, callback_t const& c, callback2_t const& c2, connection_queue& cc); ~udp_socket(); bool is_open() const @@ -67,6 +69,9 @@ namespace libtorrent } io_service& get_io_service() { return m_ipv4_sock.get_io_service(); } + // this is only valid when using a socks5 proxy + void send_hostname(char const* hostname, int port, char const* p, int len, error_code& ec); + void send(udp::endpoint const& ep, char const* p, int len, error_code& ec); void bind(udp::endpoint const& ep, error_code& ec); void bind(int port); @@ -89,13 +94,19 @@ namespace libtorrent struct queued_packet { udp::endpoint ep; + char* hostname; buffer buf; }; private: + // callback for regular incoming packets callback_t m_callback; + // callback for proxied incoming packets with a domain + // name as source + callback2_t m_callback2; + void on_read(udp::socket* sock, error_code const& e, std::size_t bytes_transferred); void on_name_lookup(error_code const& e, tcp::resolver::iterator i); void on_timeout(); @@ -111,6 +122,7 @@ namespace libtorrent void hung_up(error_code const& e); void wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec); + void wrap(char const* hostname, int port, char const* p, int len, error_code& ec); void unwrap(error_code const& e, char const* buf, int size); mutable mutex m_mutex; @@ -133,7 +145,7 @@ namespace libtorrent proxy_settings m_proxy_settings; connection_queue& m_cc; tcp::resolver m_resolver; - char m_tmp_buf[100]; + char m_tmp_buf[270]; bool m_queue_packets; bool m_tunnel_packets; bool m_abort; @@ -151,7 +163,7 @@ namespace libtorrent struct rate_limited_udp_socket : public udp_socket { - rate_limited_udp_socket(io_service& ios, callback_t const& c, connection_queue& cc); + rate_limited_udp_socket(io_service& ios, callback_t const& c, callback2_t const& c2, connection_queue& cc); void set_rate_limit(int limit) { m_rate_limit = limit; } bool can_send() const { return int(m_queue.size()) >= m_queue_size_limit; } bool send(udp::endpoint const& ep, char const* p, int len, error_code& ec, int flags = 0); diff --git a/include/libtorrent/udp_tracker_connection.hpp b/include/libtorrent/udp_tracker_connection.hpp index aed2fee77..a29bfa474 100644 --- a/include/libtorrent/udp_tracker_connection.hpp +++ b/include/libtorrent/udp_tracker_connection.hpp @@ -93,9 +93,12 @@ namespace libtorrent void name_lookup(error_code const& error, tcp::resolver::iterator i); void timeout(error_code const& error); + void start_announce(); bool on_receive(error_code const& e, udp::endpoint const& ep , char const* buf, int size); + bool on_receive_hostname(error_code const& e, char const* hostname + , char const* buf, int size); bool on_connect_response(char const* buf, int size); bool on_announce_response(char const* buf, int size); bool on_scrape_response(char const* buf, int size); @@ -108,9 +111,8 @@ namespace libtorrent tracker_manager& m_man; -// udp::resolver m_name_lookup; -// udp_socket m_socket; bool m_abort; + std::string m_hostname; udp::endpoint m_target; std::list m_endpoints; @@ -128,6 +130,8 @@ namespace libtorrent static mutex m_cache_mutex; action_t m_state; + + proxy_settings m_proxy; }; } diff --git a/src/http_connection.cpp b/src/http_connection.cpp index b92aa6217..220967bc4 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -296,6 +296,16 @@ void http_connection::start(std::string const& hostname, std::string const& port } else #endif + if (ps && ps->proxy_hostnames + && (ps->type == proxy_settings::socks5 + || ps->type == proxy_settings::socks5_pw)) + { + m_hostname = hostname; + m_port = port; + m_endpoints.push_back(tcp::endpoint(address(), atoi(port.c_str()))); + queue_connect(); + } + else { tcp::resolver::query query(hostname, port); m_resolver.async_resolve(query, boost::bind(&http_connection::on_resolve @@ -452,6 +462,23 @@ void http_connection::queue_connect() void http_connection::connect(int ticket, tcp::endpoint target_address) { m_connection_ticket = ticket; + if (m_proxy.proxy_hostnames + && (m_proxy.type == proxy_settings::socks5 + || m_proxy.type == proxy_settings::socks5_pw)) + { + // we're using a socks proxy and we're resolving + // hostnames through it +#ifdef TORRENT_USE_OPENSSL + if (!m_ssl) + { + TORRENT_ASSERT(m_sock.get()->get()); + m_sock.get()->get()->set_dst_name(m_hostname); + } +#else + TORRENT_ASSERT(m_sock.get()); + m_sock.get()->set_dst_name(m_hostname); +#endif + } m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect , shared_from_this(), _1)); } diff --git a/src/http_stream.cpp b/src/http_stream.cpp index c482f13e7..fa089c713 100644 --- a/src/http_stream.cpp +++ b/src/http_stream.cpp @@ -75,8 +75,16 @@ namespace libtorrent // send CONNECT std::back_insert_iterator > p(m_buffer); - write_string("CONNECT " + print_endpoint(m_remote_endpoint) - + " HTTP/1.0\r\n", p); + std::string endpoint; + if (!m_hostname.empty()) + { + endpoint = m_hostname + ':' + to_string(m_remote_endpoint.port()).elems; + } + else + { + endpoint = print_endpoint(m_remote_endpoint); + } + write_string("CONNECT " + endpoint + " HTTP/1.0\r\n", p); if (!m_user.empty()) { write_string("Proxy-Authorization: Basic " + base64encode( diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index b4f7a0bcf..76d4dde6a 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -3174,9 +3174,11 @@ namespace libtorrent TORRENT_ASSERT(m_connecting); #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - error_code ec; - (*m_ses.m_logger) << time_now_string() << " CONNECTION TIMED OUT: " << m_remote.address().to_string(ec) + (*m_ses.m_logger) << time_now_string() << " CONNECTION TIMED OUT: " << print_endpoint(m_remote) << "\n"; +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " CONNECTION TIMED OUT: " << print_endpoint(m_remote) << "\n"; #endif disconnect(errors::timed_out, 1); } @@ -4786,8 +4788,7 @@ namespace libtorrent error_code ec; #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << time_now_string() << " CONNECTING: " << m_remote.address().to_string(ec) - << ":" << m_remote.port() << "\n"; + (*m_ses.m_logger) << time_now_string() << " ON_CONNECT: " << print_endpoint(m_remote) << "\n"; #endif m_connection_ticket = ticket; @@ -4802,6 +4803,9 @@ namespace libtorrent return; } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " OPEN " << (m_remote.address().is_v4()?"IPv4":"IPv6") << "\n"; +#endif m_socket->open(m_remote.protocol(), ec); if (ec) { @@ -4809,27 +4813,23 @@ namespace libtorrent return; } - // set the socket to non-blocking, so that we can - // read the entire buffer on each read event we get - tcp::socket::non_blocking_io ioc(true); - m_socket->io_control(ioc, ec); - if (ec) - { - disconnect(ec); - return; - } - tcp::endpoint bind_interface = t->get_interface(); std::pair const& out_ports = m_ses.settings().outgoing_ports; if (out_ports.first > 0 && out_ports.second >= out_ports.first) { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " SET REUSE ADDRESS\n"; +#endif m_socket->set_option(socket_acceptor::reuse_address(true), ec); - if (ec) - { - disconnect(ec); - return; - } + // ignore errors because the underlying socket may not + // be opened yet. This happens when we're routing through + // a proxy. In that case, we don't yet know the address of + // the proxy server, and more importantly, we don't know + // the address family of its address. This means we can't + // open the socket yet. The socks abstraction layer defers + // opening it. + ec.clear(); bind_interface.port(m_ses.next_port()); } @@ -4845,12 +4845,21 @@ namespace libtorrent bind_interface.address(address_v4::any()); } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " BIND: " << print_endpoint(bind_interface) << "\n"; +#endif m_socket->bind(bind_interface, ec); if (ec) { disconnect(ec); return; } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_ses.m_logger) << time_now_string() << " ASYNC_CONNECT: " << print_endpoint(m_remote) << "\n"; +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " ASYNC_CONNECT: " << print_endpoint(m_remote) << "\n"; +#endif m_socket->async_connect(m_remote , boost::bind(&peer_connection::on_connection_complete, self(), _1)); m_connect = time_now(); @@ -4882,7 +4891,11 @@ namespace libtorrent if (e) { #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << time_now_string() << " CONNECTION FAILED: " << m_remote.address().to_string(ec) + (*m_ses.m_logger) << time_now_string() << " CONNECTION FAILED: " << print_endpoint(m_remote) + << ": " << e.message() << "\n"; +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " CONNECTION FAILED: " << print_endpoint(m_remote) << ": " << e.message() << "\n"; #endif disconnect(e, 1); @@ -4898,10 +4911,23 @@ namespace libtorrent TORRENT_ASSERT(m_socket); #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " COMPLETED: " << m_remote.address().to_string(ec) + (*m_ses.m_logger) << time_now_string() << " COMPLETED: " << print_endpoint(m_remote) << " rtt = " << m_rtt << "\n"; #endif + // set the socket to non-blocking, so that we can + // read the entire buffer on each read event we get + tcp::socket::non_blocking_io ioc(true); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " SET NON-BLOCKING\n"; +#endif + m_socket->io_control(ioc, ec); + if (ec) + { + disconnect(ec); + return; + } + if (m_remote == m_socket->local_endpoint(ec)) { // if the remote endpoint is the same as the local endpoint, we're connected diff --git a/src/session_impl.cpp b/src/session_impl.cpp index d86d8b91d..761179c8a 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -511,7 +511,9 @@ namespace aux { , m_dht_announce_timer(m_io_service) #endif , m_external_udp_port(0) - , m_udp_socket(m_io_service, boost::bind(&session_impl::on_receive_udp, this, _1, _2, _3, _4) + , m_udp_socket(m_io_service + , boost::bind(&session_impl::on_receive_udp, this, _1, _2, _3, _4) + , boost::bind(&session_impl::on_receive_udp_hostname, this, _1, _2, _3, _4) , m_half_open) , m_timer(m_io_service) , m_lsd_announce_timer(m_io_service) @@ -1640,6 +1642,17 @@ namespace aux { } } + void session_impl::on_receive_udp_hostname(error_code const& e + , char const* hostname, char const* buf, int len) + { + // it's probably a udp tracker response + if (m_tracker_manager.incoming_udp(e, hostname, buf, len)) + { + m_stat.received_tracker_bytes(len + 28); + } + } + + #endif void session_impl::async_accept(boost::shared_ptr const& listener) diff --git a/src/socks5_stream.cpp b/src/socks5_stream.cpp index 829301276..1e69ea4ed 100644 --- a/src/socks5_stream.cpp +++ b/src/socks5_stream.cpp @@ -79,6 +79,9 @@ namespace libtorrent return; } + m_sock.open(i->endpoint().protocol()); + // TOOD: we could bind the socket here, since we know what the + // target endpoint is of the proxy m_sock.async_connect(i->endpoint(), boost::bind( &socks5_stream::connected, this, _1, h)); } @@ -259,14 +262,27 @@ namespace libtorrent if (m_version == 5) { // send SOCKS5 connect command - m_buffer.resize(6 + (m_remote_endpoint.address().is_v4()?4:16)); + m_buffer.resize(6 + (!m_dst_name.empty() + ?m_dst_name.size() + 1 + :(m_remote_endpoint.address().is_v4()?4:16))); char* p = &m_buffer[0]; write_uint8(5, p); // SOCKS VERSION 5 write_uint8(m_command, p); // CONNECT/BIND command write_uint8(0, p); // reserved - write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type - write_endpoint(m_remote_endpoint, p); - TORRENT_ASSERT(p - &m_buffer[0] == int(m_buffer.size())); + if (!m_dst_name.empty()) + { + write_uint8(3, p); // address type + TORRENT_ASSERT(m_dst_name.size() <= 255); + write_uint8(m_dst_name.size(), p); + std::copy(m_dst_name.begin(), m_dst_name.end(), p); + p += m_dst_name.size(); + } + else + { + 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); } else if (m_version == 4) { diff --git a/src/torrent.cpp b/src/torrent.cpp index a4a915855..d0d2e47b2 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1729,7 +1729,7 @@ namespace libtorrent , tracker_ips.end(), boost::bind(&address::is_v4, _1) != tracker_ip.is_v4()); if (i != tracker_ips.end()) { - // the tracker did resolve to a different type if address, so announce + // the tracker did resolve to a different type of address, so announce // to that as well // tell the tracker to bind to the opposite protocol type @@ -3333,6 +3333,18 @@ namespace libtorrent // TODO: we have already resolved this URL, just connect } + if (m_ses.m_port_filter.access(port) & port_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::port_blocked)); + } + // never try it again + m_web_seeds.erase(web); + return; + } + web->resolving = true; proxy_settings const& ps = m_ses.web_seed_proxy(); if (ps.type == proxy_settings::http @@ -3343,20 +3355,14 @@ namespace libtorrent m_ses.m_host_resolver.async_resolve(q, boost::bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, web)); } + else if (ps.proxy_hostnames + && (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw)) + { + connect_web_seed(web, tcp::endpoint(address(), port)); + } else { - if (m_ses.m_port_filter.access(port) & port_filter::blocked) - { - if (m_ses.m_alerts.should_post()) - { - m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), web->url, errors::port_blocked)); - } - // never try it again - m_web_seeds.erase(web); - return; - } - tcp::resolver::query q(hostname, to_string(port).elems); m_ses.m_host_resolver.async_resolve(q, boost::bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, web @@ -3456,7 +3462,11 @@ namespace libtorrent } tcp::endpoint a(host->endpoint()); + connect_web_seed(web, a); + } + void torrent::connect_web_seed(std::list::iterator web, tcp::endpoint const& a) + { if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) { if (m_ses.m_alerts.should_post()) @@ -3476,14 +3486,31 @@ namespace libtorrent (void)ret; TORRENT_ASSERT(ret); - if (m_ses.web_seed_proxy().type == proxy_settings::http - || m_ses.web_seed_proxy().type == proxy_settings::http_pw) + proxy_settings const& ps = m_ses.web_seed_proxy(); + if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) { // the web seed connection will talk immediately to // the proxy, without requiring CONNECT support s->get()->set_no_connect(true); } + if (ps.proxy_hostnames + && (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw)) + { + // we're using a socks proxy and we're resolving + // hostnames through it + TORRENT_ASSERT(s->get()); + + using boost::tuples::ignore; + std::string hostname; + error_code ec; + boost::tie(ignore, ignore, hostname, ignore, ignore) + = parse_url_components(web->url, ec); + s->get()->set_dst_name(hostname); + } + boost::intrusive_ptr c; if (web->type == web_seed_entry::url_seed) { diff --git a/src/tracker_manager.cpp b/src/tracker_manager.cpp index 8846b3a7a..782378a72 100644 --- a/src/tracker_manager.cpp +++ b/src/tracker_manager.cpp @@ -266,6 +266,20 @@ namespace libtorrent return false; } + bool tracker_manager::incoming_udp(error_code const& e + , char const* hostname, char const* buf, int size) + { + for (tracker_connections_t::iterator i = m_connections.begin(); + i != m_connections.end();) + { + boost::intrusive_ptr p = *i; + ++i; + // on_receive() may remove the tracker connection from the list + if (p->on_receive_hostname(e, hostname, buf, size)) return true; + } + return false; + } + void tracker_manager::abort_all_requests(bool all) { // removes all connections from m_connections diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index fa7c0b163..f57d353af 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -47,9 +47,12 @@ POSSIBILITY OF SUCH DAMAGE. using namespace libtorrent; -udp_socket::udp_socket(asio::io_service& ios, udp_socket::callback_t const& c +udp_socket::udp_socket(asio::io_service& ios + , udp_socket::callback_t const& c + , udp_socket::callback2_t const& c2 , connection_queue& cc) : m_callback(c) + , m_callback2(c2) , m_ipv4_sock(ios) #if TORRENT_USE_IPV6 , m_ipv6_sock(ios) @@ -93,6 +96,33 @@ udp_socket::~udp_socket() #define CHECK_MAGIC do {} while (false) #endif +void udp_socket::send_hostname(char const* hostname, int port + , char const* p, int len, error_code& ec) +{ + CHECK_MAGIC; + + TORRENT_ASSERT(is_open()); + + // if the sockets are closed, the udp_socket is closing too + if (!is_open()) return; + + if (m_tunnel_packets) + { + // send udp packets through SOCKS5 server + wrap(hostname, port, p, len, ec); + return; + } + + TORRENT_ASSERT(m_queue_packets); + if (!m_queue_packets) return; + + m_queue.push_back(queued_packet()); + queued_packet& qp = m_queue.back(); + qp.ep.port(port); + qp.hostname = strdup(hostname); + qp.buf.insert(qp.buf.begin(), p, p + len); +} + void udp_socket::send(udp::endpoint const& ep, char const* p, int len, error_code& ec) { CHECK_MAGIC; @@ -114,6 +144,7 @@ void udp_socket::send(udp::endpoint const& ep, char const* p, int len, error_cod m_queue.push_back(queued_packet()); queued_packet& qp = m_queue.back(); qp.ep = ep; + qp.hostname = 0; qp.buf.insert(qp.buf.begin(), p, p + len); return; } @@ -288,8 +319,38 @@ void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, error_cod 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); + write_endpoint(ep, h); + + boost::array iovec; + iovec[0] = asio::const_buffer(header, h - header); + iovec[1] = asio::const_buffer(p, len); + +#if TORRENT_USE_IPV6 + if (m_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) +#endif + m_ipv4_sock.send_to(iovec, m_proxy_addr, 0, ec); +#if TORRENT_USE_IPV6 + else + m_ipv6_sock.send_to(iovec, m_proxy_addr, 0, ec); +#endif +} + +void udp_socket::wrap(char const* hostname, int port, char const* p, int len, error_code& ec) +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + char header[270]; + char* h = header; + + write_uint16(0, h); // reserved + write_uint8(0, h); // fragment + write_uint8(3, h); // atyp + int hostlen = (std::min)(strlen(hostname), size_t(255)); + write_uint8(hostlen, h); // hostname len + memcpy(h, hostname, hostlen); + h += hostlen; + write_uint16(port, h); boost::array iovec; iovec[0] = asio::const_buffer(header, h - header); @@ -337,7 +398,11 @@ void udp_socket::unwrap(error_code const& e, char const* buf, int size) #endif else { - // domain name not supported + int len = read_uint8(p); + if (len > (buf + size) - p) return; + std::string hostname(p, p + len); + p += len; + m_callback2(e, hostname.c_str(), p, size - (p - buf)); return; } @@ -646,9 +711,20 @@ void udp_socket::socks_forward_udp(mutex::scoped_lock& l) write_uint8(5, p); // SOCKS VERSION 5 write_uint8(3, p); // UDP ASSOCIATE command write_uint8(0, p); // reserved - write_uint8(1, p); // ATYP IPv4 - write_uint32(0, p); // IP any - write_uint16(m_bind_port, p); + error_code ec; + tcp::endpoint local = m_socks5_sock.local_endpoint(ec); + write_uint8(local.address().is_v4() ? 1 : 4, p); // ATYP IPv4 + detail::write_address(local.address(), p); + int port = 0; +#if TORRENT_USE_IPV6 + if (local.address().is_v4()) +#endif + port = m_ipv4_sock.local_endpoint(ec).port(); +#if TORRENT_USE_IPV6 + else + port = m_ipv6_sock.local_endpoint(ec).port(); +#endif + detail::write_uint16(port , p); asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) , boost::bind(&udp_socket::connect1, this, _1)); @@ -702,7 +778,15 @@ void udp_socket::connect2(error_code const& e) { queued_packet const& p = m_queue.front(); error_code ec; - udp_socket::send(p.ep, &p.buf[0], p.buf.size(), ec); + if (p.hostname) + { + udp_socket::send_hostname(p.hostname, p.ep.port(), &p.buf[0], p.buf.size(), ec); + free(p.hostname); + } + else + { + udp_socket::send(p.ep, &p.buf[0], p.buf.size(), ec); + } m_queue.pop_front(); } @@ -724,8 +808,10 @@ void udp_socket::hung_up(error_code const& e) } rate_limited_udp_socket::rate_limited_udp_socket(io_service& ios - , callback_t const& c, connection_queue& cc) - : udp_socket(ios, c, cc) + , callback_t const& c + , callback2_t const& c2 + , connection_queue& cc) + : udp_socket(ios, c, c2, cc) , m_timer(ios) , m_queue_size_limit(200) , m_rate_limit(4000) diff --git a/src/udp_tracker_connection.cpp b/src/udp_tracker_connection.cpp index a79411a68..b146af6d1 100644 --- a/src/udp_tracker_connection.cpp +++ b/src/udp_tracker_connection.cpp @@ -75,6 +75,7 @@ namespace libtorrent , m_ses(ses) , m_attempts(0) , m_state(action_error) + , m_proxy(proxy) { } @@ -98,18 +99,30 @@ namespace libtorrent session_settings const& settings = m_ses.settings(); - tcp::resolver::query q(hostname, to_string(port).elems); - m_ses.m_host_resolver.async_resolve(q - , boost::bind( - &udp_tracker_connection::name_lookup, self(), _1, _2)); + if (m_proxy.proxy_hostnames + && (m_proxy.type == proxy_settings::socks5 + || m_proxy.type == proxy_settings::socks5_pw)) + { + m_hostname = hostname; + m_target.port(port); + start_announce(); + } + else + { + tcp::resolver::query q(hostname, to_string(port).elems); + m_ses.m_host_resolver.async_resolve(q + , boost::bind( + &udp_tracker_connection::name_lookup, self(), _1, _2)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) cb->debug_log(("*** UDP_TRACKER [ initiating name lookup: " + hostname + " ]").c_str()); +#endif + } + set_timeout(tracker_req().event == tracker_request::stopped ? settings.stop_tracker_timeout : settings.tracker_completion_timeout , settings.tracker_receive_timeout); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - boost::shared_ptr cb = requester(); - if (cb) cb->debug_log(("*** UDP_TRACKER [ initiating name lookup: " + hostname + " ]").c_str()); -#endif } void udp_tracker_connection::name_lookup(error_code const& error @@ -197,6 +210,11 @@ namespace libtorrent if (cb) cb->m_tracker_address = tcp::endpoint(m_target.address(), m_target.port()); + start_announce(); + } + + void udp_tracker_connection::start_announce() + { mutex::scoped_lock l(m_cache_mutex); std::map::iterator cc = m_connection_cache.find(m_target.address()); @@ -238,6 +256,16 @@ namespace libtorrent tracker_connection::close(); } + bool udp_tracker_connection::on_receive_hostname(error_code const& e + , char const* hostname, char const* buf, int size) + { + // just ignore the hostname this came from, pretend that + // it's from the same endpoint we sent it to (i.e. the same + // port). We have so many other ways of confirming this packet + // comes from the tracker anyway, so it's not a big deal + return on_receive(e, m_target, buf, size); + } + bool udp_tracker_connection::on_receive(error_code const& e , udp::endpoint const& ep, char const* buf, int size) { @@ -367,7 +395,14 @@ namespace libtorrent TORRENT_ASSERT(ptr - buf == sizeof(buf)); error_code ec; - m_ses.m_udp_socket.send(m_target, buf, 16, ec); + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, 16, ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, 16, ec); + } m_state = action_connect; sent_bytes(16 + 28); // assuming UDP/IP header ++m_attempts; @@ -403,7 +438,14 @@ namespace libtorrent TORRENT_ASSERT(out - buf == sizeof(buf)); error_code ec; - m_ses.m_udp_socket.send(m_target, buf, sizeof(buf), ec); + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, sizeof(buf), ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, sizeof(buf), ec); + } m_state = action_scrape; sent_bytes(sizeof(buf) + 28); // assuming UDP/IP header ++m_attempts; @@ -590,7 +632,14 @@ namespace libtorrent #endif error_code ec; - m_ses.m_udp_socket.send(m_target, buf, sizeof(buf), ec); + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, sizeof(buf), ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, sizeof(buf), ec); + } m_state = action_announce; sent_bytes(sizeof(buf) + 28); // assuming UDP/IP header ++m_attempts; diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 1849b4af5..30003b313 100644 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -96,7 +96,7 @@ namespace libtorrent // according to the settings. set_timeout(ses.settings().urlseed_timeout); #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "*** web_peer_connection\n"; + (*m_logger) << "*** web_peer_connection " << url << "\n"; #endif std::string protocol; diff --git a/test/test_http_connection.cpp b/test/test_http_connection.cpp index aa61877f8..0a285dce0 100644 --- a/test/test_http_connection.cpp +++ b/test/test_http_connection.cpp @@ -71,7 +71,8 @@ void http_connect_handler(http_connection& c) error_code ec; std::cerr << "connected to: " << print_endpoint(c.socket().remote_endpoint(ec)) << std::endl; - TEST_CHECK(c.socket().remote_endpoint(ec).address() == address::from_string("127.0.0.1", ec)); +// this is not necessarily true when using a proxy and proxying hostnames +// TEST_CHECK(c.socket().remote_endpoint(ec).address() == address::from_string("127.0.0.1", ec)); } void http_handler(error_code const& ec, http_parser const& parser