From 3562c3e646e9a993e61c67b06db155e690de16a8 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Thu, 31 Jan 2008 08:24:01 +0000 Subject: [PATCH] add bind address support to http_connection and replace http_tracker_connection's code with http_connection --- include/libtorrent/http_connection.hpp | 12 +- .../libtorrent/http_tracker_connection.hpp | 66 +- include/libtorrent/web_peer_connection.hpp | 3 +- src/http_connection.cpp | 55 +- src/http_tracker_connection.cpp | 588 +++--------------- src/lsd.cpp | 2 + 6 files changed, 145 insertions(+), 581 deletions(-) diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index 5b1e4b1b9..0a6e5af54 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -42,10 +42,11 @@ POSSIBILITY OF SUCH DAMAGE. #include #include "libtorrent/socket.hpp" -#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/http_parser.hpp" #include "libtorrent/time.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/socket_type.hpp" +#include "libtorrent/session_settings.hpp" #ifdef TORRENT_USE_OPENSSL #include "libtorrent/ssl_stream.hpp" @@ -56,6 +57,7 @@ namespace libtorrent { struct http_connection; +struct connection_queue; typedef boost::function http_handler; @@ -101,11 +103,11 @@ struct http_connection : boost::enable_shared_from_this, boost: void get(std::string const& url, time_duration timeout = seconds(30) , proxy_settings const* ps = 0, int handle_redirects = 5 - , std::string const& user_agent = ""); + , std::string const& user_agent = "", address const& bind_addr = address_v4::any()); void start(std::string const& hostname, std::string const& port , time_duration timeout, proxy_settings const* ps = 0, bool ssl = false - , int handle_redirect = 5); + , int handle_redirect = 5, address const& bind_addr = address_v4::any()); void close(); @@ -183,6 +185,10 @@ private: // true if the connection is using ssl bool m_ssl; + + // the address to bind to. address_v4::any() + // means do not bind + address m_bind_addr; }; } diff --git a/include/libtorrent/http_tracker_connection.hpp b/include/libtorrent/http_tracker_connection.hpp index cb1ce7fbd..13a205d33 100755 --- a/include/libtorrent/http_tracker_connection.hpp +++ b/include/libtorrent/http_tracker_connection.hpp @@ -33,44 +33,31 @@ POSSIBILITY OF SUCH DAMAGE. #ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED #define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED -#include #include -#include -#include #ifdef _MSC_VER #pragma warning(push, 1) #endif #include -#include -#include -#include #ifdef _MSC_VER #pragma warning(pop) #endif -#include "libtorrent/socket.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/session_settings.hpp" #include "libtorrent/peer_id.hpp" -#include "libtorrent/peer.hpp" #include "libtorrent/tracker_manager.hpp" #include "libtorrent/config.hpp" -#include "libtorrent/buffer.hpp" -#include "libtorrent/socket_type.hpp" -#include "libtorrent/connection_queue.hpp" -#include "libtorrent/http_parser.hpp" - -#ifdef TORRENT_USE_OPENSSL -#include "libtorrent/ssl_stream.hpp" -#include "libtorrent/variant_stream.hpp" -#endif namespace libtorrent { + class http_connection; + class entry; + class http_parser; + class connection_queue; + class session_settings; + class TORRENT_EXPORT http_tracker_connection : public tracker_connection { @@ -99,47 +86,16 @@ namespace libtorrent boost::intrusive_ptr self() { return boost::intrusive_ptr(this); } - void on_response(); - - void init_send_buffer( - std::string const& hostname - , std::string const& request); + void on_response(asio::error_code const& ec, http_parser const& parser + , char const* data, int size); - void name_lookup(asio::error_code const& error, tcp::resolver::iterator i); - void connect(int ticket, tcp::endpoint target_address); - void connected(asio::error_code const& error); - void sent(asio::error_code const& error); - void receive(asio::error_code const& error - , std::size_t bytes_transferred); + virtual void on_timeout() {} - virtual void on_timeout(); - - void parse(const entry& e); + void parse(int status_code, const entry& e); bool extract_peer_info(const entry& e, peer_entry& ret); tracker_manager& m_man; - http_parser m_parser; - - tcp::resolver m_name_lookup; - int m_port; -#ifdef TORRENT_USE_OPENSSL - variant_stream > m_socket; - bool m_ssl; -#else - socket_type m_socket; -#endif - int m_recv_pos; - std::vector m_buffer; - std::string m_send_buffer; - - session_settings const& m_settings; - proxy_settings const& m_proxy; - std::string m_password; - - bool m_timed_out; - - int m_connection_ticket; - connection_queue& m_cc; + boost::shared_ptr m_tracker_connection; }; } diff --git a/include/libtorrent/web_peer_connection.hpp b/include/libtorrent/web_peer_connection.hpp index 742d823f0..24acdd972 100755 --- a/include/libtorrent/web_peer_connection.hpp +++ b/include/libtorrent/web_peer_connection.hpp @@ -70,8 +70,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" // parse_url #include "libtorrent/tracker_manager.hpp" -// http_parser -#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/http_parser.hpp" namespace libtorrent { diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 923e92e11..f402973d0 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/escape_string.hpp" #include "libtorrent/instantiate_connection.hpp" #include "libtorrent/gzip.hpp" +#include "libtorrent/tracker_manager.hpp" #include #include @@ -44,16 +45,12 @@ using boost::bind; namespace libtorrent { -namespace -{ - char to_lower(char c) { return std::tolower(c); } -} - enum { max_bottled_buffer = 1024 * 1024 }; void http_connection::get(std::string const& url, time_duration timeout - , proxy_settings const* ps, int handle_redirects, std::string const& user_agent) + , proxy_settings const* ps, int handle_redirects, std::string const& user_agent + , address const& bind_addr) { std::string protocol; std::string auth; @@ -106,16 +103,16 @@ void http_connection::get(std::string const& url, time_duration timeout sendbuffer = headers.str(); start(hostname, boost::lexical_cast(port), timeout, ps - , ssl, handle_redirects); + , ssl, handle_redirects, bind_addr); } void http_connection::start(std::string const& hostname, std::string const& port - , time_duration timeout, proxy_settings const* ps, bool ssl, int handle_redirects) + , time_duration timeout, proxy_settings const* ps, bool ssl, int handle_redirects + , address const& bind_addr) { m_redirects = handle_redirects; if (ps) m_proxy = *ps; - m_ssl = ssl; m_timeout = timeout; asio::error_code ec; m_timer.expires_from_now(m_timeout, ec); @@ -126,13 +123,22 @@ void http_connection::start(std::string const& hostname, std::string const& port m_recvbuffer.clear(); m_read_pos = 0; - if (m_sock.is_open() && m_hostname == hostname && m_port == port) + if (ec) + { + callback(ec); + return; + } + + if (m_sock.is_open() && m_hostname == hostname && m_port == port + && m_ssl == ssl && m_bind_addr == bind_addr) { asio::async_write(m_sock, asio::buffer(sendbuffer) , bind(&http_connection::on_write, shared_from_this(), _1)); } else { + m_ssl = ssl; + m_bind_addr = bind_addr; asio::error_code ec; m_sock.close(ec); @@ -155,6 +161,16 @@ void http_connection::start(std::string const& hostname, std::string const& port bool ret = instantiate_connection(m_resolver.get_io_service(), m_proxy, m_sock); TORRENT_ASSERT(ret); #endif + if (m_bind_addr != address_v4::any()) + { + asio::error_code ec; + m_sock.bind(tcp::endpoint(m_bind_addr, 0), ec); + if (ec) + { + callback(ec); + return; + } + } tcp::resolver::query query(hostname, port); m_resolver.async_resolve(query, bind(&http_connection::on_resolve @@ -212,7 +228,7 @@ void http_connection::close() } void http_connection::on_resolve(asio::error_code const& e - , tcp::resolver::iterator i) + , tcp::resolver::iterator i) { if (e) { @@ -221,7 +237,22 @@ void http_connection::on_resolve(asio::error_code const& e return; } TORRENT_ASSERT(i != tcp::resolver::iterator()); - m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i) + + // look for an address that has the same kind as the one + // we're binding to. To make sure a tracker get our + // correct listening address. + tcp::resolver::iterator target = i; + tcp::resolver::iterator end; + tcp::endpoint target_address = *i; + for (; target != end && target->endpoint().address().is_v4() + != m_bind_addr.is_v4(); ++target); + + if (target != end) + { + target_address = *target; + } + + m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, target_address) , bind(&http_connection::on_connect_timeout, shared_from_this()) , m_timeout); } diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index 24b637447..3213a8131 100755 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -54,29 +54,15 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/tracker_manager.hpp" #include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/http_connection.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/io.hpp" -#include "libtorrent/instantiate_connection.hpp" using namespace libtorrent; using boost::bind; -namespace -{ - enum - { - minimum_tracker_response_length = 3, - http_buffer_size = 2048 - }; -} - -namespace -{ - char to_lower(char c) { return std::tolower(c); } -} - namespace libtorrent { @@ -96,571 +82,155 @@ namespace libtorrent , std::string const& auth) : tracker_connection(man, req, ios, bind_infc, c) , m_man(man) - , m_name_lookup(ios) - , m_port(port) - , m_socket(ios) -#ifdef TORRENT_USE_OPENSSL - , m_ssl(protocol == "https") -#endif - , m_recv_pos(0) - , m_buffer(http_buffer_size) - , m_settings(stn) - , m_proxy(ps) - , m_password(auth) - , m_timed_out(false) - , m_connection_ticket(-1) - , m_cc(cc) { - m_send_buffer.assign("GET "); + // TODO: authentication + std::string url = req.url; - // should we use the proxy? - if (m_proxy.type == proxy_settings::http - || m_proxy.type == proxy_settings::http_pw) - { - m_send_buffer += "http://"; - m_send_buffer += hostname; - if (port != 80) - { - m_send_buffer += ":"; - m_send_buffer += boost::lexical_cast(port); - } - } - - if (tracker_req().kind == tracker_request::scrape_request) + if (req.kind == tracker_request::scrape_request) { // find and replace "announce" with "scrape" // in request - std::size_t pos = request.find("announce"); + std::size_t pos = url.find("announce"); if (pos == std::string::npos) { fail(-1, ("scrape is not available on url: '" - + tracker_req().url +"'").c_str()); + + req.url +"'").c_str()); return; } - request.replace(pos, 8, "scrape"); + url.replace(pos, 8, "scrape"); } - - m_send_buffer += request; - + // if request-string already contains // some parameters, append an ampersand instead // of a question mark - size_t arguments_start = request.find('?'); + size_t arguments_start = url.find('?'); if (arguments_start != std::string::npos) - m_send_buffer += "&"; + url += "&"; else - m_send_buffer += "?"; + url += "?"; - if (!url_has_argument(request, "info_hash")) + url += "info_hash="; + url += escape_string( + reinterpret_cast(req.info_hash.begin()), 20); + + if (req.kind == tracker_request::announce_request) { - m_send_buffer += "info_hash="; - m_send_buffer += escape_string( - reinterpret_cast(req.info_hash.begin()), 20); - m_send_buffer += '&'; - } + url += "&peer_id="; + url += escape_string( + reinterpret_cast(req.pid.begin()), 20); - if (tracker_req().kind == tracker_request::announce_request) - { - if (!url_has_argument(request, "peer_id")) - { - m_send_buffer += "peer_id="; - m_send_buffer += escape_string( - reinterpret_cast(req.pid.begin()), 20); - m_send_buffer += '&'; - } + url += "&port="; + url += boost::lexical_cast(req.listen_port); - if (!url_has_argument(request, "port")) - { - m_send_buffer += "port="; - m_send_buffer += boost::lexical_cast(req.listen_port); - m_send_buffer += '&'; - } + url += "&uploaded="; + url += boost::lexical_cast(req.uploaded); - if (!url_has_argument(request, "uploaded")) - { - m_send_buffer += "uploaded="; - m_send_buffer += boost::lexical_cast(req.uploaded); - m_send_buffer += '&'; - } + url += "&downloaded="; + url += boost::lexical_cast(req.downloaded); - if (!url_has_argument(request, "downloaded")) - { - m_send_buffer += "downloaded="; - m_send_buffer += boost::lexical_cast(req.downloaded); - m_send_buffer += '&'; - } - - if (!url_has_argument(request, "left")) - { - m_send_buffer += "left="; - m_send_buffer += boost::lexical_cast(req.left); - m_send_buffer += '&'; - } + url += "&left="; + url += boost::lexical_cast(req.left); if (req.event != tracker_request::none) { - if (!url_has_argument(request, "event")) - { - const char* event_string[] = {"completed", "started", "stopped"}; - m_send_buffer += "event="; - m_send_buffer += event_string[req.event - 1]; - m_send_buffer += '&'; - } - } - if (!url_has_argument(request, "key")) - { - m_send_buffer += "key="; - std::stringstream key_string; - key_string << std::hex << req.key; - m_send_buffer += key_string.str(); - m_send_buffer += '&'; + const char* event_string[] = {"completed", "started", "stopped"}; + url += "&event="; + url += event_string[req.event - 1]; } - if (!url_has_argument(request, "compact")) + url += "&key="; + std::stringstream key_string; + key_string << std::hex << req.key; + url += key_string.str(); + + url += "&compact=1"; + + url += "&numwant="; + url += boost::lexical_cast( + (std::min)(req.num_want, 999)); + + if (stn.announce_ip != address()) { - m_send_buffer += "compact=1&"; - } - if (!url_has_argument(request, "numwant")) - { - m_send_buffer += "numwant="; - m_send_buffer += boost::lexical_cast( - (std::min)(req.num_want, 999)); - m_send_buffer += '&'; - } - if (m_settings.announce_ip != address() && !url_has_argument(request, "ip")) - { - m_send_buffer += "ip="; - m_send_buffer += m_settings.announce_ip.to_string(); - m_send_buffer += '&'; + url += "&ip="; + url += stn.announce_ip.to_string(); } #ifndef TORRENT_DISABLE_ENCRYPTION - m_send_buffer += "supportcrypto=1&"; + url += "&supportcrypto=1"; #endif - - if (!url_has_argument(request, "ipv6") && !req.ipv6.empty()) - { - m_send_buffer += "ipv6="; - m_send_buffer += req.ipv6; - m_send_buffer += '&'; - } + url += "&ipv6="; + url += req.ipv6; // extension that tells the tracker that // we don't need any peer_id's in the response - if (!url_has_argument(request, "no_peer_id")) - { - m_send_buffer += "no_peer_id=1"; - } - else - { - // remove the trailing '&' - m_send_buffer.resize(m_send_buffer.size() - 1); - } + url += "&no_peer_id=1"; } - m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n" - "User-Agent: "; - m_send_buffer += m_settings.user_agent; - m_send_buffer += "\r\n" - "Host: "; - m_send_buffer += hostname; - if (port != 80) - { - m_send_buffer += ':'; - m_send_buffer += boost::lexical_cast(port); - } - if (m_proxy.type == proxy_settings::http_pw) - { - m_send_buffer += "\r\nProxy-Authorization: Basic "; - m_send_buffer += base64encode(m_proxy.username + ":" + m_proxy.password); - } - if (!auth.empty()) - { - m_send_buffer += "\r\nAuthorization: Basic "; - m_send_buffer += base64encode(auth); - } - m_send_buffer += "\r\n\r\n"; + m_tracker_connection.reset(new http_connection(ios, cc + , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4))); + + m_tracker_connection->get(url, seconds(stn.tracker_completion_timeout) + , &ps, 5, stn.user_agent, bind_infc); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) boost::shared_ptr cb = requester(); if (cb) { - cb->debug_log("==> TRACKER_REQUEST [ ih: " + boost::lexical_cast(req.info_hash) - + " str: " + m_send_buffer + " ]"); + cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]"); } #endif - - tcp::resolver::query q(hostname - , boost::lexical_cast(m_port)); - m_name_lookup.async_resolve(q, - boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2)); - set_timeout(req.event == tracker_request::stopped - ? m_settings.stop_tracker_timeout - : m_settings.tracker_completion_timeout - , m_settings.tracker_receive_timeout); - } - - void http_tracker_connection::on_timeout() - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr cb = requester(); - if (cb) cb->debug_log("*** HTTP_TRACKER [ timed out ]"); -#endif - m_timed_out = true; - asio::error_code ec; - m_socket.close(ec); - m_name_lookup.cancel(); - if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); - m_connection_ticket = -1; - fail_timeout(); } void http_tracker_connection::close() { - asio::error_code ec; - m_socket.close(ec); - m_name_lookup.cancel(); - if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); - m_connection_ticket = -1; - m_timed_out = true; + if (m_tracker_connection) + { + m_tracker_connection->close(); + m_tracker_connection.reset(); + } tracker_connection::close(); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr cb = requester(); - if (cb) cb->debug_log("*** HTTP_TRACKER [ close: " - + boost::lexical_cast(m_man.num_requests()) + " ]"); -#endif } - void http_tracker_connection::name_lookup(asio::error_code const& error - , tcp::resolver::iterator i) + void http_tracker_connection::on_response(asio::error_code const& ec + , http_parser const& parser, char const* data, int size) { - boost::shared_ptr cb = requester(); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (cb) cb->debug_log("*** HTTP_TRACKER [ tracker name lookup handler called ]"); -#endif - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - - if (error || i == tcp::resolver::iterator()) - { - fail(-1, error.message().c_str()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (cb) cb->debug_log("*** HTTP_TRACKER [ name lookup successful ]"); -#endif - restart_read_timeout(); - - // look for an address that has the same kind as the one - // we're listening on. To make sure the tracker get our - // correct listening address. - tcp::resolver::iterator target = i; - tcp::resolver::iterator end; - tcp::endpoint target_address = *i; - for (; target != end && target->endpoint().address().is_v4() - != bind_interface().is_v4(); ++target); - if (target == end) - { - TORRENT_ASSERT(target_address.address().is_v4() != bind_interface().is_v4()); - if (cb) - { - std::string tracker_address_type = target_address.address().is_v4() ? "IPv4" : "IPv6"; - std::string bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; - cb->tracker_warning("the tracker only resolves to an " - + tracker_address_type + " address, and you're listening on an " - + bind_address_type + " socket. This may prevent you from receiving incoming connections."); - } - } - else - { - target_address = *target; - } - - if (cb) cb->m_tracker_address = target_address; - asio::io_service& ios = m_name_lookup.io_service(); -#ifdef TORRENT_USE_OPENSSL - if (m_ssl) - { - m_socket.instantiate >(ios); - ssl_stream& s = m_socket.get >(); - bool ret = instantiate_connection(ios, m_proxy, s.next_layer()); - TORRENT_ASSERT(ret); - } - else - { - m_socket.instantiate(ios); - bool ret = instantiate_connection(ios, m_proxy, m_socket.get()); - TORRENT_ASSERT(ret); - } -#else - bool ret = instantiate_connection(ios, m_proxy, m_socket); - TORRENT_ASSERT(ret); -#endif - - if (m_proxy.type == proxy_settings::http - || m_proxy.type == proxy_settings::http_pw) - { - // the tracker connection will talk immediately to - // the proxy, without requiring CONNECT support - m_socket.get().set_no_connect(true); - } - - asio::error_code ec; - m_socket.open(target_address.protocol(), ec); - if (ec) - { - fail(-1, ec.message().c_str()); - return; - } - m_socket.bind(tcp::endpoint(bind_interface(), 0), ec); - if (ec) - { - fail(-1, ec.message().c_str()); - return; - } - m_cc.enqueue(bind(&http_tracker_connection::connect, self(), _1, target_address) - , bind(&http_tracker_connection::on_timeout, self()) - , seconds(m_settings.tracker_receive_timeout)); - } - - void http_tracker_connection::connect(int ticket, tcp::endpoint target_address) - { - m_connection_ticket = ticket; - m_socket.async_connect(target_address, bind(&http_tracker_connection::connected, self(), _1)); - } - - void http_tracker_connection::connected(asio::error_code const& error) - { - if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); - m_connection_ticket = -1; - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - if (error) - { - fail(-1, error.message().c_str()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr cb = requester(); - if (cb) cb->debug_log("*** HTTP_TRACKER [ connection successful ]"); -#endif - - restart_read_timeout(); - async_write(m_socket, asio::buffer(m_send_buffer.c_str() - , m_send_buffer.size()), bind(&http_tracker_connection::sent - , self(), _1)); - } - - void http_tracker_connection::sent(asio::error_code const& error) - { - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - if (error) - { - fail(-1, error.message().c_str()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr cb = requester(); - if (cb) cb->debug_log("*** HTTP_TRACKER [ send completed ]"); -#endif - restart_read_timeout(); - TORRENT_ASSERT(m_buffer.size() - m_recv_pos > 0); - m_socket.async_read_some(asio::buffer(&m_buffer[m_recv_pos] - , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive - , self(), _1, _2)); - } - - void http_tracker_connection::receive(asio::error_code const& error - , std::size_t bytes_transferred) - { - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - - if (error) - { - if (error == asio::error::eof) - { - on_response(); - close(); - return; - } - - fail(-1, error.message().c_str()); - return; - } - - restart_read_timeout(); - TORRENT_ASSERT(bytes_transferred > 0); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr cb = requester(); - if (cb) cb->debug_log("*** HTTP_TRACKER [ reading: " - + boost::lexical_cast(bytes_transferred) + " ]"); -#endif - - m_recv_pos += bytes_transferred; - bool e = false; - m_parser.incoming(buffer::const_interval(&m_buffer[0] - , &m_buffer[0] + m_recv_pos), e); - if (e) - { - fail(-1, "incorrect http response"); - } - - // if the receive buffer is full, expand it with http_buffer_size - if ((int)m_buffer.size() == m_recv_pos) - { - if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length) - { - fail(200, "too large tracker response"); - return; - } - TORRENT_ASSERT(http_buffer_size > 0); - if ((int)m_buffer.size() + http_buffer_size - > m_settings.tracker_maximum_response_length) - m_buffer.resize(m_settings.tracker_maximum_response_length); - else - m_buffer.resize(m_buffer.size() + http_buffer_size); - } - - if (m_parser.header_finished()) - { - int cl = atoi(m_parser.header("content-length").c_str()); - if (cl > m_settings.tracker_maximum_response_length) - { - fail(-1, "content-length is greater than maximum response length"); - return; - } - - if (cl > 0 && cl < minimum_tracker_response_length && m_parser.status_code() == 200) - { - fail(-1, "content-length is smaller than minimum response length"); - return; - } - } - - if (m_parser.finished()) - { - on_response(); - close(); - return; - } - - TORRENT_ASSERT(m_buffer.size() - m_recv_pos > 0); - m_socket.async_read_some(asio::buffer(&m_buffer[m_recv_pos] - , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive - , self(), _1, _2)); - } - - void http_tracker_connection::on_response() - { - if (!m_parser.header_finished()) + if (!parser.header_finished()) { fail(-1, "premature end of file"); return; } - - std::string location = m_parser.header("location"); - boost::shared_ptr cb = requester(); + if (parser.status_code() != 200) + { + fail(parser.status_code(), parser.message().c_str()); + return; + } + + if (ec && ec != asio::error::eof) + { + fail(parser.status_code(), ec.message().c_str()); + return; + } - if (m_parser.status_code() >= 300 && m_parser.status_code() < 400) - { - if (location.empty()) - { - std::string error_str = "got redirection response ("; - error_str += boost::lexical_cast(m_parser.status_code()); - error_str += ") without 'Location' header"; - fail(-1, error_str.c_str()); - return; - } - - // if the protocol isn't specified, assume http - if (location.compare(0, 7, "http://") != 0 - && location.compare(0, 6, "udp://") != 0) - { - location.insert(0, "http://"); - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (cb) cb->debug_log("*** HTTP_TRACKER [ redirecting to: " + location + "]"); -#endif - if (cb) cb->tracker_warning("Redirecting to \"" + location + "\""); - tracker_request req = tracker_req(); - - req.url = location; - - m_man.queue_request(m_name_lookup.get_io_service(), m_cc, req - , m_password, bind_interface(), m_requester); - close(); - return; - } - - if (m_parser.status_code() != 200) - { - fail(m_parser.status_code(), m_parser.message().c_str()); - return; - } - - buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos); - - std::string content_encoding = m_parser.header("content-encoding"); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (cb) cb->debug_log("*** HTTP_TRACKER [ content-encoding: " + content_encoding + "]"); -#endif - - if (content_encoding == "gzip" || content_encoding == "x-gzip") - { - if (!cb) - { - close(); - return; - } - std::vector buffer; - std::string error; - if (inflate_gzip(&m_buffer[0] + m_parser.body_start(), m_buffer.size(), buffer - , m_settings.tracker_maximum_response_length, error)) - { - cb->tracker_request_error(tracker_req(), 200, error); - close(); - return; - } - m_buffer.swap(buffer); - buf.begin = &m_buffer[0]; - buf.end = &m_buffer[0] + m_buffer.size(); - } - else if (!content_encoding.empty()) - { - std::string error_str = "unknown content encoding in response: \""; - error_str += content_encoding; - error_str += "\""; - fail(-1, error_str.c_str()); - return; - } - // handle tracker response - entry e = bdecode(buf.begin, buf.end); + entry e = bdecode(data, data + size); if (e.type() != entry::undefined_t) { - parse(e); + parse(parser.status_code(), e); } else { std::string error_str("invalid bencoding of tracker response: \""); - for (char const* i = buf.begin, *end(buf.end); i != end; ++i) + for (char const* i = data, *end(data + size); i != end; ++i) { if (std::isprint(*i)) error_str += *i; else error_str += "0x" + boost::lexical_cast((unsigned int)*i) + " "; } error_str += "\""; - fail(m_parser.status_code(), error_str.c_str()); + fail(parser.status_code(), error_str.c_str()); } close(); } @@ -710,7 +280,7 @@ namespace libtorrent return true; } - void http_tracker_connection::parse(entry const& e) + void http_tracker_connection::parse(int status_code, entry const& e) { boost::shared_ptr cb = requester(); if (!cb) return; @@ -719,7 +289,7 @@ namespace libtorrent entry const* failure = e.find_key("failure reason"); if (failure && failure->type() == entry::string_t) { - fail(m_parser.status_code(), failure->string().c_str()); + fail(status_code, failure->string().c_str()); return; } diff --git a/src/lsd.cpp b/src/lsd.cpp index 5e0831b7f..d52eb2c93 100644 --- a/src/lsd.cpp +++ b/src/lsd.cpp @@ -35,6 +35,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/lsd.hpp" #include "libtorrent/io.hpp" #include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/http_parser.hpp" #include #include