3
Copyright (c) 2003, Arvid Norberg
6
Redistribution and use in source and binary forms, with or without
7
modification, are permitted provided that the following conditions
10
* Redistributions of source code must retain the above copyright
11
notice, this list of conditions and the following disclaimer.
12
* Redistributions in binary form must reproduce the above copyright
13
notice, this list of conditions and the following disclaimer in
14
the documentation and/or other materials provided with the distribution.
15
* Neither the name of the author nor the names of its
16
contributors may be used to endorse or promote products derived
17
from this software without specific prior written permission.
19
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
POSSIBILITY OF SUCH DAMAGE.
33
#include "libtorrent/pch.hpp"
42
#include "libtorrent/config.hpp"
43
#include "libtorrent/gzip.hpp"
46
#pragma warning(push, 1)
49
#include <boost/bind.hpp>
55
#include "libtorrent/tracker_manager.hpp"
56
#include "libtorrent/http_tracker_connection.hpp"
57
#include "libtorrent/http_connection.hpp"
58
#include "libtorrent/entry.hpp"
59
#include "libtorrent/bencode.hpp"
60
#include "libtorrent/torrent.hpp"
61
#include "libtorrent/io.hpp"
62
#include "libtorrent/socket.hpp"
64
using namespace libtorrent;
70
http_tracker_connection::http_tracker_connection(
72
, connection_queue& cc
73
, tracker_manager& man
74
, tracker_request const& req
76
, boost::weak_ptr<request_callback> c
77
, session_settings const& stn
78
, proxy_settings const& ps
79
, std::string const& auth)
80
: tracker_connection(man, req, ios, bind_infc, c)
83
, m_bind_iface(bind_infc)
89
void http_tracker_connection::start()
91
// TODO: authentication
92
std::string url = tracker_req().url;
94
if (tracker_req().kind == tracker_request::scrape_request)
96
// find and replace "announce" with "scrape"
99
std::size_t pos = url.find("announce");
100
if (pos == std::string::npos)
102
fail(-1, ("scrape is not available on url: '"
103
+ tracker_req().url +"'").c_str());
106
url.replace(pos, 8, "scrape");
109
// if request-string already contains
110
// some parameters, append an ampersand instead
111
// of a question mark
112
size_t arguments_start = url.find('?');
113
if (arguments_start != std::string::npos)
119
url += escape_string(
120
reinterpret_cast<const char*>(tracker_req().info_hash.begin()), 20);
122
if (tracker_req().kind == tracker_request::announce_request)
125
url += escape_string(
126
reinterpret_cast<const char*>(tracker_req().pid.begin()), 20);
129
url += to_string(tracker_req().listen_port).elems;
132
url += to_string(tracker_req().uploaded).elems;
134
url += "&downloaded=";
135
url += to_string(tracker_req().downloaded).elems;
138
url += to_string(tracker_req().left).elems;
140
if (tracker_req().event != tracker_request::none)
142
const char* event_string[] = {"completed", "started", "stopped"};
144
url += event_string[tracker_req().event - 1];
148
std::stringstream key_string;
149
key_string << std::hex << tracker_req().key;
150
url += key_string.str();
155
url += to_string((std::min)(tracker_req().num_want, 999)).elems;
157
if (m_settings.announce_ip != address())
160
std::string ip = m_settings.announce_ip.to_string(ec);
161
if (!ec) url += "&ip=" + ip;
164
#ifndef TORRENT_DISABLE_ENCRYPTION
165
url += "&supportcrypto=1";
167
if (!tracker_req().ipv6.empty())
170
url += tracker_req().ipv6;
173
if (!tracker_req().ipv4.empty())
176
url += tracker_req().ipv4;
179
// extension that tells the tracker that
180
// we don't need any peer_id's in the response
181
url += "&no_peer_id=1";
184
m_tracker_connection.reset(new http_connection(m_ios, m_cc
185
, boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)));
187
int timeout = tracker_req().event==tracker_request::stopped
188
?m_settings.stop_tracker_timeout
189
:m_settings.tracker_completion_timeout;
191
m_tracker_connection->get(url, seconds(timeout)
192
, 1, &m_ps, 5, m_settings.user_agent, m_bind_iface);
194
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
196
boost::shared_ptr<request_callback> cb = requester();
199
cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
204
void http_tracker_connection::close()
206
if (m_tracker_connection)
208
m_tracker_connection->close();
209
m_tracker_connection.reset();
211
tracker_connection::close();
214
void http_tracker_connection::on_response(error_code const& ec
215
, http_parser const& parser, char const* data, int size)
218
boost::intrusive_ptr<http_tracker_connection> me(this);
220
if (ec && ec != asio::error::eof)
222
fail(-1, ec.message().c_str());
226
if (!parser.header_finished())
228
fail(-1, "premature end of file");
232
if (parser.status_code() != 200)
234
fail(parser.status_code(), parser.message().c_str());
238
if (ec && ec != asio::error::eof)
240
fail(parser.status_code(), ec.message().c_str());
244
// handle tracker response
246
e = bdecode(data, data + size);
248
if (e.type() == entry::dictionary_t)
250
parse(parser.status_code(), e);
254
std::string error_str("invalid bencoding of tracker response: \"");
255
for (char const* i = data, *end(data + size); i != end; ++i)
257
if (*i >= ' ' && *i <= '~') error_str += *i;
258
else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
261
fail(parser.status_code(), error_str.c_str());
266
bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret)
268
// extract peer id (if any)
269
if (info.type() != entry::dictionary_t)
271
fail(-1, "invalid response from tracker (invalid peer entry)");
274
entry const* i = info.find_key("peer id");
277
if (i->type() != entry::string_t || i->string().length() != 20)
279
fail(-1, "invalid response from tracker (invalid peer id)");
282
std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
286
// if there's no peer_id, just initialize it to a bunch of zeroes
287
std::fill_n(ret.pid.begin(), 20, 0);
291
i = info.find_key("ip");
292
if (i == 0 || i->type() != entry::string_t)
294
fail(-1, "invalid response from tracker");
297
ret.ip = i->string();
300
i = info.find_key("port");
301
if (i == 0 || i->type() != entry::int_t)
303
fail(-1, "invalid response from tracker");
306
ret.port = (unsigned short)i->integer();
311
void http_tracker_connection::parse(int status_code, entry const& e)
313
boost::shared_ptr<request_callback> cb = requester();
316
// parse the response
317
entry const* failure = e.find_key("failure reason");
318
if (failure && failure->type() == entry::string_t)
320
fail(status_code, failure->string().c_str());
324
entry const* warning = e.find_key("warning message");
325
if (warning && warning->type() == entry::string_t)
327
cb->tracker_warning(tracker_req(), warning->string());
330
std::vector<peer_entry> peer_list;
332
if (tracker_req().kind == tracker_request::scrape_request)
334
std::string ih = tracker_req().info_hash.to_string();
336
entry const* files = e.find_key("files");
337
if (files == 0 || files->type() != entry::dictionary_t)
339
fail(-1, "invalid or missing 'files' entry in scrape response");
343
entry const* scrape_data = files->find_key(ih);
344
if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t)
346
fail(-1, "missing or invalid info-hash entry in scrape response");
349
entry const* complete = scrape_data->find_key("complete");
350
entry const* incomplete = scrape_data->find_key("incomplete");
351
entry const* downloaded = scrape_data->find_key("downloaded");
352
if (complete == 0 || incomplete == 0 || downloaded == 0
353
|| complete->type() != entry::int_t
354
|| incomplete->type() != entry::int_t
355
|| downloaded->type() != entry::int_t)
357
fail(-1, "missing 'complete' or 'incomplete' entries in scrape response");
360
cb->tracker_scrape_response(tracker_req(), int(complete->integer())
361
, int(incomplete->integer()), int(downloaded->integer()));
365
entry const* interval = e.find_key("interval");
366
if (interval == 0 || interval->type() != entry::int_t)
368
fail(-1, "missing or invalid 'interval' entry in tracker response");
372
entry const* peers_ent = e.find_key("peers");
373
if (peers_ent && peers_ent->type() == entry::string_t)
375
std::string const& peers = peers_ent->string();
376
for (std::string::const_iterator i = peers.begin();
379
if (std::distance(i, peers.end()) < 6) break;
384
p.ip = detail::read_v4_address(i).to_string(ec);
386
p.port = detail::read_uint16(i);
387
peer_list.push_back(p);
390
else if (peers_ent && peers_ent->type() == entry::list_t)
392
entry::list_type const& l = peers_ent->list();
393
for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
396
if (!extract_peer_info(*i, p)) return;
397
peer_list.push_back(p);
405
entry const* ipv6_peers = e.find_key("peers6");
406
if (ipv6_peers && ipv6_peers->type() == entry::string_t)
408
std::string const& peers = ipv6_peers->string();
409
for (std::string::const_iterator i = peers.begin();
412
if (std::distance(i, peers.end()) < 18) break;
417
p.ip = detail::read_v6_address(i).to_string(ec);
419
p.port = detail::read_uint16(i);
420
peer_list.push_back(p);
428
if (peers_ent == 0 && ipv6_peers == 0)
430
fail(-1, "missing 'peers' and 'peers6' entry in tracker response");
435
// look for optional scrape info
440
entry const* ip_ent = e.find_key("external ip");
441
if (ip_ent && ip_ent->type() == entry::string_t)
443
std::string const& ip = ip_ent->string();
444
char const* p = &ip[0];
445
if (ip.size() == address_v4::bytes_type::static_size)
446
external_ip = detail::read_v4_address(p);
447
else if (ip.size() == address_v6::bytes_type::static_size)
448
external_ip = detail::read_v6_address(p);
451
entry const* complete_ent = e.find_key("complete");
452
if (complete_ent && complete_ent->type() == entry::int_t)
453
complete = int(complete_ent->integer());
455
entry const* incomplete_ent = e.find_key("incomplete");
456
if (incomplete_ent && incomplete_ent->type() == entry::int_t)
457
incomplete = int(incomplete_ent->integer());
459
cb->tracker_response(tracker_req(), peer_list, interval->integer(), complete
460
, incomplete, external_ip);