From 80b57290c979312c47b53eb2b869f803b76b89b1 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sat, 31 Jan 2004 10:46:15 +0000 Subject: [PATCH] *** empty log message *** --- .../libtorrent/http_tracker_connection.hpp | 105 ++++ include/libtorrent/session.hpp | 2 +- include/libtorrent/torrent.hpp | 2 +- include/libtorrent/tracker_manager.hpp | 156 ++++++ include/libtorrent/udp_tracker_connection.hpp | 106 ++++ src/http_tracker_connection.cpp | 520 ++++++++++++++++++ src/peer_connection.cpp | 2 +- src/session.cpp | 2 +- src/torrent.cpp | 2 +- src/torrent_handle.cpp | 2 +- src/tracker_manager.cpp | 437 +++++++++++++++ src/udp_tracker_connection.cpp | 334 +++++++++++ 12 files changed, 1664 insertions(+), 6 deletions(-) create mode 100755 include/libtorrent/http_tracker_connection.hpp create mode 100755 include/libtorrent/tracker_manager.hpp create mode 100755 include/libtorrent/udp_tracker_connection.hpp create mode 100755 src/http_tracker_connection.cpp create mode 100755 src/tracker_manager.cpp create mode 100755 src/udp_tracker_connection.cpp diff --git a/include/libtorrent/http_tracker_connection.hpp b/include/libtorrent/http_tracker_connection.hpp new file mode 100755 index 000000000..e71748383 --- /dev/null +++ b/include/libtorrent/http_tracker_connection.hpp @@ -0,0 +1,105 @@ +/* + +Copyright (c) 2003, 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_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 + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/http_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/tracker_manager.hpp" + +namespace libtorrent +{ + + class http_tracker_connection: public tracker_connection + { + friend class tracker_manager; + public: + + http_tracker_connection( + tracker_request const& req + , std::string const& hostname + , unsigned short port + , std::string const& request + , request_callback* c + , const http_settings& stn); + virtual bool tick(); + virtual bool send_finished() const + { return m_send_buffer.empty(); } + + private: + + void parse(const entry& e); + peer_entry extract_peer_info(const entry& e); + + enum { read_status, read_header, read_body } m_state; + + enum { plain, gzip } m_content_encoding; + int m_content_length; + + boost::shared_ptr m_socket; + int m_recv_pos; + std::vector m_buffer; + std::string m_send_buffer; + + // used for time outs + boost::posix_time::ptime m_request_time; + + std::string m_server_message; + std::string m_server_protocol; + + const http_settings& m_settings; + }; + +} + +#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 0fc8b0122..60584cb35 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -61,7 +61,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_connection.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/policy.hpp" -#include "libtorrent/url_handler.hpp" +#include "libtorrent/tracker_manager.hpp" #include "libtorrent/peer_info.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/fingerprint.hpp" diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index e7e5014ef..a2d9e4876 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -57,7 +57,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket.hpp" #include "libtorrent/policy.hpp" #include "libtorrent/storage.hpp" -#include "libtorrent/url_handler.hpp" +#include "libtorrent/tracker_manager.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/alert.hpp" diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp new file mode 100755 index 000000000..2e6ca523b --- /dev/null +++ b/include/libtorrent/tracker_manager.hpp @@ -0,0 +1,156 @@ +/* + +Copyright (c) 2003, 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_TRACKER_MANAGER_HPP_INCLUDED +#define TORRENT_TRACKER_MANAGER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/http_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" + +namespace libtorrent +{ + struct request_callback; + class tracker_manager; + + // encodes a string using the base64 scheme + std::string base64encode(const std::string& s); + + // returns -1 if gzip header is invalid or the header size in bytes + int gzip_header(const char* buf, int size); + + bool inflate_gzip( + std::vector& buffer + , request_callback* requester + , int maximum_tracker_response_length); + + struct tracker_request + { + enum event_t + { + none, + completed, + started, + stopped + }; + + sha1_hash info_hash; + peer_id id; + size_type downloaded; + size_type uploaded; + size_type left; + unsigned short listen_port; + event_t event; + std::string url; + }; + + struct request_callback + { + friend class tracker_manager; + request_callback(): m_manager(0) {} + virtual ~request_callback(); + virtual void tracker_response( + std::vector& peers + , int interval) = 0; + virtual void tracker_request_timed_out() = 0; + virtual void tracker_request_error( + int response_code + , const std::string& description) = 0; + +#ifndef NDEBUG + virtual void debug_log(const std::string& line) = 0; +#endif + private: + tracker_manager* m_manager; + }; + + struct tracker_connection: boost::noncopyable + { + tracker_connection(request_callback* r) + : m_requester(r) + {} + + virtual bool tick() = 0; + virtual bool send_finished() const = 0; + request_callback* requester() { return m_requester; } + virtual ~tracker_connection() {} + + private: + + request_callback* m_requester; + + }; + + class tracker_manager: boost::noncopyable + { + public: + + tracker_manager(const http_settings& s) + : m_settings(s) {} + + void tick(); + void queue_request(tracker_request const& r, request_callback* c = 0); + void abort_request(request_callback* c); + void abort_all_requests(); + bool send_finished() const; + + private: + + std::vector > m_connections; + const http_settings& m_settings; + }; + + inline request_callback::~request_callback() + { if (m_manager) m_manager->abort_request(this); } + +} + +#endif // TORRENT_TRACKER_MANAGER_HPP_INCLUDED diff --git a/include/libtorrent/udp_tracker_connection.hpp b/include/libtorrent/udp_tracker_connection.hpp new file mode 100755 index 000000000..cce11a0da --- /dev/null +++ b/include/libtorrent/udp_tracker_connection.hpp @@ -0,0 +1,106 @@ +/* + +Copyright (c) 2003, 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_UDP_TRACKER_CONNECTION_HPP_INCLUDED +#define TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/http_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/tracker_manager.hpp" + +namespace libtorrent +{ + class udp_tracker_connection: public tracker_connection + { + friend class tracker_manager; + public: + + udp_tracker_connection( + tracker_request const& req + , std::string const& hostname + , unsigned short port + , request_callback* c + , const http_settings& stn); + + virtual bool tick(); + virtual bool send_finished() const; + + private: + + enum action_t + { + connect, + announce, + scrape, + error + }; + + void send_udp_connect(); + void send_udp_announce(); + bool parse_connect_response(const char* buf, int ret); + bool parse_announce_response(const char* buf, int ret); + + boost::shared_ptr m_socket; + + // used for time outs + boost::posix_time::ptime m_request_time; + + tracker_request m_request; + int m_transaction_id; + boost::int64_t m_connection_id; + const http_settings& m_settings; + int m_attempts; + }; + +} + +#endif // TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp new file mode 100755 index 000000000..70c7b67aa --- /dev/null +++ b/src/http_tracker_connection.cpp @@ -0,0 +1,520 @@ +/* + +Copyright (c) 2003, 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 +#include +#include +#include +#include + +#include "zlib.h" + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" + +using namespace libtorrent; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + + + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +namespace libtorrent +{ + + + http_tracker_connection::http_tracker_connection( + tracker_request const& req + , std::string const& hostname + , unsigned short port + , std::string const& request + , request_callback* c + , const http_settings& stn) + : tracker_connection(c) + , m_state(read_status) + , m_content_encoding(plain) + , m_content_length(0) + , m_recv_pos(0) + , m_request_time(boost::posix_time::second_clock::local_time()) + , m_settings(stn) + { + const std::string* connect_to_host; + bool using_proxy = false; + + // should we use the proxy? + if (!m_settings.proxy_ip.empty()) + { + connect_to_host = &m_settings.proxy_ip; + if (m_settings.proxy_port != 0) port = m_settings.proxy_port; + using_proxy = true; + } + else + { + connect_to_host = &hostname; + } + + // TODO: this is a problem. DNS-lookup is blocking! + // (may block up to 5 seconds) + address a(*connect_to_host, port); + boost::shared_ptr s(new socket(socket::tcp, false)); + s->connect(a); + + m_send_buffer.assign("GET "); + if (using_proxy) + { + m_send_buffer += "http://"; + m_send_buffer += hostname; + if (port != 80) + m_send_buffer += boost::lexical_cast(port); + } + + m_send_buffer += request; + m_send_buffer += "?info_hash="; + m_send_buffer += escape_string( + reinterpret_cast(req.info_hash.begin()), 20); + + m_send_buffer += "&peer_id="; + m_send_buffer += escape_string( + reinterpret_cast(req.id.begin()), 20); + + m_send_buffer += "&port="; + m_send_buffer += boost::lexical_cast(req.listen_port); + + m_send_buffer += "&uploaded="; + m_send_buffer += boost::lexical_cast(req.uploaded); + + m_send_buffer += "&downloaded="; + m_send_buffer += boost::lexical_cast(req.downloaded); + + m_send_buffer += "&left="; + m_send_buffer += boost::lexical_cast(req.left); + + if (req.event != tracker_request::none) + { + const char* event_string[] = {"completed", "started", "stopped"}; + m_send_buffer += "&event="; + m_send_buffer += event_string[req.event - 1]; + } + + // extension that tells the tracker that + // we don't need any peer_id's in the response + m_send_buffer += "&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 += " (libtorrent)\r\n" + "Host: "; + m_send_buffer += hostname; + if (port != 80) + { + m_send_buffer += ':'; + m_send_buffer += boost::lexical_cast(port); + } + if (using_proxy && !m_settings.proxy_login.empty()) + { + m_send_buffer += "\r\nProxy-Authorization: Basic "; + m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password); + } + m_send_buffer += "\r\n\r\n"; + #ifndef NDEBUG + if (c) c->debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]"); + #endif + m_socket = s; + } + + // returns true if this connection is finished and should be removed from + // the connections list. + bool http_tracker_connection::tick() + { + using namespace boost::posix_time; + + time_duration d = second_clock::local_time() - m_request_time; + if (d > seconds(m_settings.tracker_timeout) || + (requester() == 0 && d > seconds(m_settings.stop_tracker_timeout))) + { + if (requester()) requester()->tracker_request_timed_out(); + return true; + } + + #ifndef NDEBUG + if (requester()) requester()->debug_log("tracker connection tick"); + #endif + + // if we have a send buffer and the socket is ready for writing + // send the buffer + if (!m_send_buffer.empty() && m_socket->is_writable()) + { + int sent = m_socket->send(m_send_buffer.c_str(),(int)m_send_buffer.size()); + + if (sent == (int)m_send_buffer.size()) + { + m_send_buffer.clear(); + } + else if (sent > 0) + { + m_send_buffer.erase( + m_send_buffer.begin() + , m_send_buffer.begin() + sent); + } + + if (sent != 0) + m_request_time = boost::posix_time::second_clock::local_time(); + } + + + // if the socket isn't ready for reading, there's no point in continuing + // trying to read from it + if (!m_socket->is_readable()) return false; + m_request_time = boost::posix_time::second_clock::local_time(); + + #ifndef NDEBUG + if (requester()) requester()->debug_log("tracker connection socket readable"); + #endif + + // 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) + { + if (requester()) + { + requester()->tracker_request_error( + 200 + , "too large tracker response"); + } + return true; + } + assert(http_buffer_size > 0); + m_buffer.resize(m_buffer.size() + http_buffer_size); + } + + + assert(m_recv_pos >= 0); + assert(m_recv_pos < (int)m_buffer.size()); + int received = m_socket->receive(&m_buffer[m_recv_pos], (int)m_buffer.size() - m_recv_pos); + + assert(received <= (int)m_buffer.size() - m_recv_pos); + + if (received > 0) m_recv_pos += received; + + #ifndef NDEBUG + if (requester()) requester()->debug_log("received: " + boost::lexical_cast(m_recv_pos)); + #endif + + if (m_state == read_status) + { + if (received <= 0) + { + if (requester()) + { + requester()->tracker_request_error( + -1 + , "invalid tracker response, connection closed"); + } + return true; + } + + std::vector::iterator end = m_buffer.begin()+m_recv_pos; + std::vector::iterator newline = std::find(m_buffer.begin(), end, '\n'); + // if we don't have a full line yet, wait. + if (newline == end) return false; + + #ifndef NDEBUG + if (requester()) requester()->debug_log(std::string(m_buffer.begin(), newline)); + #endif + + std::istringstream line(std::string(m_buffer.begin(), newline)); + ++newline; + m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); + m_buffer.erase(m_buffer.begin(), newline); + + std::string protocol; + line >> m_server_protocol; + if (m_server_protocol.substr(0, 5) != "HTTP/") + { + std::string error_msg = "unknown protocol in response: " + m_server_protocol; + if (requester()) requester()->tracker_request_error(-1, error_msg.c_str()); + return true; + } + int code; + line >> code; + std::getline(line, m_server_message); + m_state = read_header; + + if (code != 200) + { + std::string error_msg = boost::lexical_cast(code) + " " + m_server_message; + if (requester()) requester()->tracker_request_error(code, error_msg.c_str()); + return true; + } + } + + if (m_state == read_header) + { + if (received <= 0) + { + if (requester()) + requester()->tracker_request_error(-1, "invalid tracker response, connection closed while reading header"); + return true; + } + + std::vector::iterator end = m_buffer.begin()+m_recv_pos; + std::vector::iterator newline = std::find(m_buffer.begin(), end, '\n'); + std::string line; + + while (newline != end && m_state == read_header) + { + line.assign(m_buffer.begin(), newline); + + #ifndef NDEBUG + if (requester()) requester()->debug_log(line); + #endif + + if (line.substr(0, 16) == "Content-Length: ") + { + try + { + m_content_length = boost::lexical_cast(line.substr(16)); + } + catch(boost::bad_lexical_cast&) + { + if (requester()) + { + requester()->tracker_request_error( + -1, + "invalid content-length in tracker response"); + } + return true; + } + if (m_content_length > m_settings.tracker_maximum_response_length) + { + if (requester()) + { + requester()->tracker_request_error( + -1 + , "content-length is greater than maximum response length"); + } + return true; + } + + if (m_content_length < minimum_tracker_response_length) + { + if (requester()) + { + requester()->tracker_request_error( + -1 + , "content-length is smaller than minimum response length"); + } + return true; + } + #ifndef NDEBUG + if (requester()) + { + requester()->debug_log( + std::string("m_content_length = ") + + boost::lexical_cast(m_content_length)); + } + #endif + } + else if (line.substr(0, 18) == "Content-Encoding: ") + { + if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip") + { + m_content_encoding = gzip; + } + else + { + std::string error_str = "unknown content encoding in response: \""; + error_str += line.substr(18, line.length() - 18 - 2); + error_str += "\""; + if (requester()) + { + requester()->tracker_request_error(-1, error_str.c_str()); + } + return true; + } + } + else if (line.size() < 3) + { + m_state = read_body; + #ifndef NDEBUG + if (requester()) requester()->debug_log("end of http header"); + #endif + } + + ++newline; + assert(m_recv_pos <= (int)m_buffer.size()); + m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); + m_buffer.erase(m_buffer.begin(), newline); + assert(m_recv_pos <= (int)m_buffer.size()); + end = m_buffer.begin() + m_recv_pos; + newline = std::find(m_buffer.begin(), end, '\n'); + } + + } + + if (m_state == read_body) + { + if (m_recv_pos == m_content_length || received <= 0) + { + // GZIP + if (m_content_encoding == gzip) + { + if (inflate_gzip(m_buffer, + requester(), + m_settings.tracker_maximum_response_length)) + return true; + } + + // handle tracker response + entry e = bdecode(m_buffer.begin(), m_buffer.end()); + parse(e); + return true; + } + return false; + } + else if (m_recv_pos > m_content_length && m_content_length > 0) + { + if (requester()) + { + requester()->tracker_request_error( + -1 + , "invalid tracker response (body > content_length)"); + } + return true; + } + + return false; + } + + peer_entry http_tracker_connection::extract_peer_info(const entry& e) + { + peer_entry ret; + + const entry::dictionary_type& info = e.dict(); + + // extract peer id (if any) + entry::dictionary_type::const_iterator i = info.find("peer id"); + if (i != info.end()) + { + if (i->second.string().length() != 20) throw std::runtime_error("invalid response from tracker"); + std::copy(i->second.string().begin(), i->second.string().end(), ret.id.begin()); + } + else + { + // if there's no peer_id, just initialize it to a bunch of zeroes + std::fill_n(ret.id.begin(), 20, 0); + } + + // extract ip + i = info.find("ip"); + if (i == info.end()) throw std::runtime_error("invalid response from tracker"); + ret.ip = i->second.string(); + + // extract port + i = info.find("port"); + if (i == info.end()) throw std::runtime_error("invalid response from tracker"); + ret.port = (unsigned short)i->second.integer(); + + return ret; + } + + void http_tracker_connection::parse(const entry& e) + { + if (requester() == 0) return; + + std::vector peer_list; + try + { + // parse the response + + entry::dictionary_type::const_iterator i = e.dict().find("failure reason"); + if (i != e.dict().end()) + { + throw std::runtime_error(i->second.string().c_str()); + } + + const entry::dictionary_type& msg = e.dict(); + i = msg.find("interval"); + if (i == msg.end()) throw std::runtime_error("invalid response from tracker (no interval)"); + + int interval = (int)i->second.integer(); + + i = msg.find("peers"); + if (i == msg.end()) throw std::runtime_error("invalid response from tracker (no peers)"); + + peer_list.clear(); + + const entry::list_type& l = i->second.list(); + for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i) + { + peer_entry p = extract_peer_info(*i); + peer_list.push_back(p); + } + + requester()->tracker_response(peer_list, interval); + } + catch(type_error& e) + { + requester()->tracker_request_error(-1, e.what()); + } + catch(std::runtime_error& e) + { + requester()->tracker_request_error(-1, e.what()); + } + } + +} diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 27d2ed8a9..580af3b83 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -1754,6 +1754,6 @@ namespace libtorrent bool peer_connection::is_seed() const { - return m_num_pieces == m_have_piece.size(); + return m_num_pieces == (int)m_have_piece.size(); } } diff --git a/src/session.cpp b/src/session.cpp index c07239375..512d78ea5 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -56,7 +56,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_id.hpp" #include "libtorrent/torrent_info.hpp" -#include "libtorrent/url_handler.hpp" +#include "libtorrent/tracker_manager.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" diff --git a/src/torrent.cpp b/src/torrent.cpp index 69e2d653b..5178f1d7f 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -53,7 +53,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent_handle.hpp" #include "libtorrent/session.hpp" #include "libtorrent/torrent_info.hpp" -#include "libtorrent/url_handler.hpp" +#include "libtorrent/tracker_manager.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 8718ab4d6..d9f33b6e5 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -54,7 +54,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_id.hpp" #include "libtorrent/torrent_info.hpp" -#include "libtorrent/url_handler.hpp" +#include "libtorrent/tracker_manager.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" diff --git a/src/tracker_manager.cpp b/src/tracker_manager.cpp new file mode 100755 index 000000000..ee0c97db2 --- /dev/null +++ b/src/tracker_manager.cpp @@ -0,0 +1,437 @@ +/* + +Copyright (c) 2003, 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 +#include +#include +#include +#include + +#include "zlib.h" + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" + +using namespace libtorrent; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + + + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +namespace libtorrent +{ + + // returns -1 if gzip header is invalid or the header size in bytes + int gzip_header(const char* buf, int size) + { + assert(buf != 0); + assert(size > 0); + + const unsigned char* buffer = reinterpret_cast(buf); + const int total_size = size; + + // The zip header cannot be shorter than 10 bytes + if (size < 10) return -1; + + // check the magic header of gzip + if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1; + + int method = buffer[2]; + int flags = buffer[3]; + + // check for reserved flag and make sure it's compressed with the correct metod + if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1; + + // skip time, xflags, OS code + size -= 10; + buffer += 10; + + if (flags & FEXTRA) + { + int extra_len; + + if (size < 2) return -1; + + extra_len = (buffer[1] << 8) | buffer[0]; + + if (size < (extra_len+2)) return -1; + size -= (extra_len + 2); + buffer += (extra_len + 2); + } + + if (flags & FNAME) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FCOMMENT) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FHCRC) + { + if (size < 2) return -1; + + size -= 2; + buffer += 2; + } + + return total_size - size; + } + + bool inflate_gzip( + std::vector& buffer + , request_callback* requester + , int maximum_tracker_response_length) + { + assert(maximum_tracker_response_length > 0); + + int header_len = gzip_header(&buffer[0], (int)buffer.size()); + if (header_len < 0) + { + requester->tracker_request_error(200, "invalid gzip header in tracker response"); + return true; + } + + // start off wth one kilobyte and grow + // if needed + std::vector inflate_buffer(1024); + + // initialize the zlib-stream + z_stream str; + + // subtract 8 from the end of the buffer since that's CRC32 and input size + // and those belong to the gzip file + str.avail_in = (int)buffer.size() - header_len - 8; + str.next_in = reinterpret_cast(&buffer[header_len]); + str.next_out = reinterpret_cast(&inflate_buffer[0]); + str.avail_out = (int)inflate_buffer.size(); + str.zalloc = Z_NULL; + str.zfree = Z_NULL; + str.opaque = 0; + // -15 is really important. It will make inflate() not look for a zlib header + // and just deflate the buffer + if (inflateInit2(&str, -15) != Z_OK) + { + requester->tracker_request_error(200, "gzip out of memory"); + return true; + } + + // inflate and grow inflate_buffer as needed + int ret = inflate(&str, Z_SYNC_FLUSH); + while (ret == Z_OK) + { + if (str.avail_out == 0) + { + if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length) + { + inflateEnd(&str); + requester->tracker_request_error(200, "tracker response too large"); + return true; + } + int new_size = (int)inflate_buffer.size() * 2; + if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length; + int old_size = (int)inflate_buffer.size(); + + inflate_buffer.resize(new_size); + str.next_out = reinterpret_cast(&inflate_buffer[old_size]); + str.avail_out = new_size - old_size; + } + + ret = inflate(&str, Z_SYNC_FLUSH); + } + + inflate_buffer.resize(inflate_buffer.size() - str.avail_out); + inflateEnd(&str); + + if (ret != Z_STREAM_END) + { + requester->tracker_request_error(200, "gzip error"); + return true; + } + + // commit the resulting buffer + std::swap(buffer, inflate_buffer); + return false; + } + + std::string base64encode(const std::string& s) + { + static const char base64_table[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + unsigned char inbuf[3]; + unsigned char outbuf[4]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + // available input is 1,2 or 3 bytes + // since we read 3 bytes at a time at most + int available_input = std::min(3, (int)std::distance(i, s.end())); + + // clear input buffer + std::fill(inbuf, inbuf+3, 0); + + // read a chunk of input into inbuf + for (int j = 0; j < available_input; ++j) + { + inbuf[j] = *i; + ++i; + } + + // encode inbuf to outbuf + outbuf[0] = (inbuf[0] & 0xfc) >> 2; + outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4); + outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6); + outbuf[3] = inbuf[2] & 0x3f; + + // write output + for (int j = 0; j < available_input+1; ++j) + { + ret += base64_table[outbuf[j]]; + } + + // write pad + for (int j = 0; j < 3 - available_input; ++j) + { + ret += '='; + } + } + return ret; + } + + + void tracker_manager::tick() + { + std::vector >::iterator i; + for (i = m_connections.begin(); i != m_connections.end(); ++i) + { + boost::shared_ptr& c = *i; + try + { + if (!c->tick()) return; + } + catch (const std::exception& e) + { + if (c->requester()) + c->requester()->tracker_request_error(-1, e.what()); + } + if (c->requester()) c->requester()->m_manager = 0; + m_connections.erase(i); + --i; // compensate for the remove + } + } + + void tracker_manager::queue_request( + tracker_request const& req + , request_callback* c) + { + try + { + std::string hostname; // hostname only + std::string protocol; // should be http + int port = 80; + + // PARSE URL + std::string::const_iterator start = req.url.begin(); + std::string::const_iterator end + = std::find(req.url.begin(), req.url.end(), ':'); + protocol = std::string(start, end); + + if (end == req.url.end()) throw std::runtime_error("invalid url"); + ++end; + if (end == req.url.end()) throw std::runtime_error("invalid url"); + if (*end != '/') throw std::runtime_error("invalid url"); + ++end; + if (end == req.url.end()) throw std::runtime_error("invalid url"); + if (*end != '/') throw std::runtime_error("invalid url"); + ++end; + start = end; + + end = std::find(start, req.url.end(), '/'); + std::string::const_iterator port_pos + = std::find(start, req.url.end(), ':'); + + if (port_pos < end) + { + hostname.assign(start, port_pos); + ++port_pos; + try + { + port = boost::lexical_cast(std::string(port_pos, end)); + } + catch(boost::bad_lexical_cast&) + { + throw std::runtime_error("invalid url"); + } + } + else + { + hostname.assign(start, end); + } + + start = end; + std::string request_string = std::string(start, req.url.end()); + + boost::shared_ptr con; + + if (protocol == "http") + { + con.reset(new http_tracker_connection( + req + , hostname + , port + , request_string + , c + , m_settings)); + } + else if (protocol == "udp") + { + con.reset(new udp_tracker_connection( + req + , hostname + , port + , c + , m_settings)); + } + else + { + throw std::runtime_error("unkown protocol in tracker url"); + } + + m_connections.push_back(con); + + if (con->requester()) con->requester()->m_manager = this; + } + catch (std::exception& e) + { + if (c) c->tracker_request_error(-1, e.what()); + } + } + + void tracker_manager::abort_request(request_callback* c) + { + assert(c != 0); + std::vector >::iterator i; + for (i = m_connections.begin(); i != m_connections.end(); ++i) + { + if ((*i)->requester() == c) + { + m_connections.erase(i); + break; + } + } + } + + void tracker_manager::abort_all_requests() + { + // removes all connections from m_connections + // except those with a requester == 0 (since those are + // 'event=stopped'-requests) + + std::vector > keep_connections; + + for (std::vector >::const_iterator i = + m_connections.begin(); + i != m_connections.end(); + ++i) + { + if ((*i)->requester() == 0) keep_connections.push_back(*i); + } + + std::swap(m_connections, keep_connections); + } + + bool tracker_manager::send_finished() const + { + for (std::vector >::const_iterator i = + m_connections.begin(); + i != m_connections.end(); + ++i) + { + if (!(*i)->send_finished()) return false; + } + return true; + } + +} diff --git a/src/udp_tracker_connection.cpp b/src/udp_tracker_connection.cpp new file mode 100755 index 000000000..505391600 --- /dev/null +++ b/src/udp_tracker_connection.cpp @@ -0,0 +1,334 @@ +/* + +Copyright (c) 2003, 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 +#include +#include +#include +#include + +#include "zlib.h" + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/io.hpp" + +namespace +{ + enum + { + udp_connection_retries = 4, + udp_announce_retries = 15, + udp_connect_timeout = 15, + udp_announce_timeout = 10, + udp_buffer_size = 2048 + }; +} + +namespace libtorrent +{ + + udp_tracker_connection::udp_tracker_connection( + tracker_request const& req + , std::string const& hostname + , unsigned short port + , request_callback* c + , const http_settings& stn) + : tracker_connection(c) + , m_request_time(boost::posix_time::second_clock::local_time()) + , m_request(req) + , m_transaction_id(0) + , m_connection_id(0) + , m_settings(stn) + , m_attempts(0) + { + // TODO: this is a problem. DNS-lookup is blocking! + // (may block up to 5 seconds) + address a(hostname, port); + m_socket.reset(new socket(socket::udp, false)); + m_socket->connect(a); + + send_udp_connect(); + } + + bool udp_tracker_connection::send_finished() const + { + using namespace boost::posix_time; + + time_duration d = second_clock::local_time() - m_request_time; + return (m_transaction_id != 0 + && m_connection_id != 0) + || d > seconds(m_settings.tracker_timeout); + } + + bool udp_tracker_connection::tick() + { + using namespace boost::posix_time; + + time_duration d = second_clock::local_time() - m_request_time; + if (m_connection_id == 0 + && d > seconds(udp_connect_timeout)) + { + if (m_attempts >= udp_connection_retries) + { + if (requester()) requester()->tracker_request_timed_out(); + return true; + } + send_udp_connect(); + return false; + } + + if (m_connection_id != 0 + && d > seconds(udp_announce_timeout)) + { + if (m_attempts >= udp_announce_retries) + { + if (requester()) requester()->tracker_request_timed_out(); + return true; + } + + send_udp_announce(); + return false; + } + + char buf[udp_buffer_size]; + int ret = m_socket->receive(buf, udp_buffer_size); + + // if there was nothing to receive, return + if (ret == 0) return false; + if (ret < 0) + { + socket::error_code err = m_socket->last_error(); + if (err == socket::would_block) return false; + throw network_error(m_socket->last_error()); + } + if (ret == udp_buffer_size) + { + if (requester()) + requester()->tracker_request_error(-1, "tracker reply too big"); + return true; + } + + if (m_connection_id == 0) + { + return parse_connect_response(buf, ret); + } + else + { + return parse_announce_response(buf, ret); + } + } + + void udp_tracker_connection::send_udp_announce() + { + if (m_transaction_id == 0) + m_transaction_id = rand() | (rand() << 16); + + char buf[94]; + char* ptr = buf; + + // connection_id + detail::write_int64(m_connection_id, ptr); + // action (announce) + detail::write_int32(announce, ptr); + // transaction_id + detail::write_int32(m_transaction_id, ptr); + // info_hash + std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), ptr); + ptr += 20; + // peer_id + std::copy(m_request.id.begin(), m_request.id.end(), ptr); + ptr += 20; + // downloaded + detail::write_int64(m_request.downloaded, ptr); + // left + detail::write_int64(m_request.left, ptr); + // uploaded + detail::write_int64(m_request.uploaded, ptr); + // event + detail::write_int32(m_request.event, ptr); + // ip address + detail::write_int32(0, ptr); + // num_want + detail::write_int32(-1, ptr); + // port + detail::write_uint16(m_request.listen_port, ptr); + + m_socket->send(buf, 94); + m_request_time = boost::posix_time::second_clock::local_time(); + ++m_attempts; + } + + void udp_tracker_connection::send_udp_connect() + { + char send_buf[16]; + char* ptr = send_buf; + + if (m_transaction_id == 0) + m_transaction_id = rand() | (rand() << 16); + + // connection_id + detail::write_int64(0, ptr); + // action (connect) + detail::write_int32(connect, ptr); + // transaction_id + detail::write_int32(m_transaction_id, ptr); + + m_socket->send(send_buf, 16); + m_request_time = boost::posix_time::second_clock::local_time(); + ++m_attempts; + } + + bool udp_tracker_connection::parse_announce_response(const char* buf, int len) + { + assert(buf != 0); + assert(len > 0); + + if (len < 8) + { +#ifndef NDEBUG + if (requester()) + requester()->debug_log("udp_tracker_connection: " + "got a message with size < 8, ignoring"); +#endif + return false; + } + + int action = detail::read_int32(buf); + int transaction = detail::read_int32(buf); + if (transaction != m_transaction_id) + { + return false; + } + + if (action == error) + { + if (requester()) + requester()->tracker_request_error(-1, std::string(buf, buf + len - 8)); + return true; + } + if (action != announce) return false; + + if (len < 12) + { +#ifndef NDEBUG + if (requester()) + requester()->debug_log("udp_tracker_connection: " + "got a message with size < 12, ignoring"); +#endif + return false; + } + int interval = detail::read_int32(buf); + int num_peers = (len - 12) / 6; + if ((len - 12) % 6 != 0) + { + if (requester()) + requester()->tracker_request_error(-1, "invalid tracker response"); + return true; + } + + if (requester() == 0) return true; + + std::vector peer_list; + for (int i = 0; i < num_peers; ++i) + { + peer_entry e; + std::stringstream s; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf); + e.ip = s.str(); + e.port = detail::read_uint16(buf); + e.id.clear(); + peer_list.push_back(e); + } + + requester()->tracker_response(peer_list, interval); + return true; + } + + bool udp_tracker_connection::parse_connect_response(const char* buf, int len) + { + assert(buf != 0); + assert(len > 0); + + if (len < 8) + { +#ifndef NDEBUG + if (requester()) + requester()->debug_log("udp_tracker_connection: " + "got a message with size < 8, ignoring"); +#endif + return false; + } + const char* ptr = buf; + int action = detail::read_int32(ptr); + int transaction = detail::read_int32(ptr); + + if (action == error) + { + if (requester()) + requester()->tracker_request_error(-1, std::string(ptr, buf + len)); + return true; + } + if (action != connect) return false; + if (m_transaction_id != transaction) + { +#ifndef NDEBUG + if (requester()) + requester()->debug_log("udp_tracker_connection: " + "got a message with incorrect transaction id, ignoring"); +#endif + return false; + } + + if (len < 16) + { +#ifndef NDEBUG + if (requester()) + requester()->debug_log("udp_tracker_connection: " + "got a connection message size < 16, ignoring"); +#endif + return false; + } + // reset transaction + m_transaction_id = 0; + m_attempts = 0; + m_connection_id = detail::read_int64(ptr); + + send_udp_announce(); + return false; + } + + +}