*** empty log message ***
This commit is contained in:
parent
5b7100656e
commit
80b57290c9
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue