*** empty log message ***

This commit is contained in:
Arvid Norberg 2004-01-31 10:46:15 +00:00
parent 5b7100656e
commit 80b57290c9
12 changed files with 1664 additions and 6 deletions

View File

@ -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 <vector>
#include <string>
#include <utility>
#include <ctime>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/shared_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#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<socket> m_socket;
int m_recv_pos;
std::vector<char> 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

View File

@ -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"

View File

@ -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"

View File

@ -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 <vector>
#include <string>
#include <utility>
#include <ctime>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/shared_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#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<char>& 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<peer_entry>& 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<boost::shared_ptr<tracker_connection> > 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

View File

@ -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 <vector>
#include <string>
#include <utility>
#include <ctime>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/shared_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#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<socket> 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

520
src/http_tracker_connection.cpp Executable file
View File

@ -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 <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#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<socket> 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<std::string>(port);
}
m_send_buffer += request;
m_send_buffer += "?info_hash=";
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.info_hash.begin()), 20);
m_send_buffer += "&peer_id=";
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.id.begin()), 20);
m_send_buffer += "&port=";
m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
m_send_buffer += "&uploaded=";
m_send_buffer += boost::lexical_cast<std::string>(req.uploaded);
m_send_buffer += "&downloaded=";
m_send_buffer += boost::lexical_cast<std::string>(req.downloaded);
m_send_buffer += "&left=";
m_send_buffer += boost::lexical_cast<std::string>(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<std::string>(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<std::string>(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<char>::iterator end = m_buffer.begin()+m_recv_pos;
std::vector<char>::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<std::string>(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<char>::iterator end = m_buffer.begin()+m_recv_pos;
std::vector<char>::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<int>(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<std::string>(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_entry> 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());
}
}
}

View File

@ -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();
}
}

View File

@ -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"

View File

@ -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"

View File

@ -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"

437
src/tracker_manager.cpp Executable file
View File

@ -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 <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#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<const unsigned char*>(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<char>& 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<char> 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<Bytef*>(&buffer[header_len]);
str.next_out = reinterpret_cast<Bytef*>(&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<Bytef*>(&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<boost::shared_ptr<tracker_connection> >::iterator i;
for (i = m_connections.begin(); i != m_connections.end(); ++i)
{
boost::shared_ptr<tracker_connection>& 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<int>(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<tracker_connection> 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<boost::shared_ptr<tracker_connection> >::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<boost::shared_ptr<tracker_connection> > keep_connections;
for (std::vector<boost::shared_ptr<tracker_connection> >::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<boost::shared_ptr<tracker_connection> >::const_iterator i =
m_connections.begin();
i != m_connections.end();
++i)
{
if (!(*i)->send_finished()) return false;
}
return true;
}
}

334
src/udp_tracker_connection.cpp Executable file
View File

@ -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 <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#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_entry> 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;
}
}