AUTO_LT_SYNC
[tore.git] / libtorrent / src / http_tracker_connection.cpp
blob01487013ae700c476ee0067109f74a1635f02d89
1 /*
3 Copyright (c) 2003, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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"
35 #include <vector>
36 #include <iostream>
37 #include <cctype>
38 #include <iomanip>
39 #include <sstream>
40 #include <algorithm>
42 #include "libtorrent/config.hpp"
43 #include "libtorrent/gzip.hpp"
45 #ifdef _MSC_VER
46 #pragma warning(push, 1)
47 #endif
49 #include <boost/bind.hpp>
51 #ifdef _MSC_VER
52 #pragma warning(pop)
53 #endif
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"
63 #include "libtorrent/aux_/session_impl.hpp"
65 using namespace libtorrent;
66 using boost::bind;
68 namespace libtorrent
71 http_tracker_connection::http_tracker_connection(
72 io_service& ios
73 , connection_queue& cc
74 , tracker_manager& man
75 , tracker_request const& req
76 , address bind_infc
77 , boost::weak_ptr<request_callback> c
78 , aux::session_impl const& ses
79 , proxy_settings const& ps
80 , std::string const& auth)
81 : tracker_connection(man, req, ios, bind_infc, c)
82 , m_man(man)
83 , m_ses(ses)
84 , m_bind_iface(bind_infc)
85 , m_ps(ps)
86 , m_cc(cc)
87 , m_ios(ios)
90 void http_tracker_connection::start()
92 // TODO: authentication
93 std::string url = tracker_req().url;
95 if (tracker_req().kind == tracker_request::scrape_request)
97 // find and replace "announce" with "scrape"
98 // in request
100 std::size_t pos = url.find("announce");
101 if (pos == std::string::npos)
103 fail(-1, ("scrape is not available on url: '"
104 + tracker_req().url +"'").c_str());
105 return;
107 url.replace(pos, 8, "scrape");
110 session_settings const& settings = m_ses.settings();
112 // if request-string already contains
113 // some parameters, append an ampersand instead
114 // of a question mark
115 size_t arguments_start = url.find('?');
116 if (arguments_start != std::string::npos)
117 url += "&";
118 else
119 url += "?";
121 url += "info_hash=";
122 url += escape_string(
123 reinterpret_cast<const char*>(tracker_req().info_hash.begin()), 20);
125 if (tracker_req().kind == tracker_request::announce_request)
127 url += "&peer_id=";
128 url += escape_string(
129 reinterpret_cast<const char*>(tracker_req().pid.begin()), 20);
131 url += "&port=";
132 url += boost::lexical_cast<std::string>(tracker_req().listen_port);
134 url += "&uploaded=";
135 url += boost::lexical_cast<std::string>(tracker_req().uploaded);
137 url += "&downloaded=";
138 url += boost::lexical_cast<std::string>(tracker_req().downloaded);
140 url += "&left=";
141 url += boost::lexical_cast<std::string>(tracker_req().left);
143 if (tracker_req().event != tracker_request::none)
145 const char* event_string[] = {"completed", "started", "stopped"};
146 url += "&event=";
147 url += event_string[tracker_req().event - 1];
150 url += "&key=";
151 std::stringstream key_string;
152 key_string << std::hex << tracker_req().key;
153 url += key_string.str();
155 url += "&compact=1";
157 url += "&numwant=";
158 url += boost::lexical_cast<std::string>(
159 (std::min)(tracker_req().num_want, 999));
161 if (settings.announce_ip != address())
163 error_code ec;
164 std::string ip = settings.announce_ip.to_string(ec);
165 if (!ec) url += "&ip=" + ip;
168 #ifndef TORRENT_DISABLE_ENCRYPTION
169 url += "&supportcrypto=1";
170 #endif
171 url += "&ipv6=";
172 url += tracker_req().ipv6;
174 // extension that tells the tracker that
175 // we don't need any peer_id's in the response
176 url += "&no_peer_id=1";
179 m_tracker_connection.reset(new http_connection(m_ios, m_cc
180 , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)
181 , true, http_connect_handler()
182 , boost::bind(&http_tracker_connection::on_filter, self(), _1, _2)));
184 int timeout = tracker_req().event==tracker_request::stopped
185 ?settings.stop_tracker_timeout
186 :settings.tracker_completion_timeout;
188 m_tracker_connection->get(url, seconds(timeout)
189 , 1, &m_ps, 5, settings.user_agent, m_bind_iface);
191 // the url + 100 estimated header size
192 sent_bytes(url.size() + 100);
194 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
196 boost::shared_ptr<request_callback> cb = requester();
197 if (cb)
199 cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
201 #endif
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_filter(http_connection& c, std::list<tcp::endpoint>& endpoints)
216 // remove endpoints that are filtered by the IP filter
217 endpoints.erase(std::remove_if(endpoints.begin(), endpoints.end()
218 , boost::bind(&ip_filter::access, boost::ref(m_ses.m_ip_filter)
219 , boost::bind(&tcp::endpoint::address, _1)) == ip_filter::blocked)
220 , endpoints.end());
222 if (endpoints.empty())
223 fail(-1, "blocked by IP filter");
226 void http_tracker_connection::on_response(error_code const& ec
227 , http_parser const& parser, char const* data, int size)
229 // keep this alive
230 boost::intrusive_ptr<http_tracker_connection> me(this);
232 if (ec && ec != asio::error::eof)
234 fail(-1, ec.message().c_str());
235 return;
238 if (!parser.header_finished())
240 fail(-1, "premature end of file");
241 return;
244 if (parser.status_code() != 200)
246 fail(parser.status_code(), parser.message().c_str());
247 return;
250 if (ec && ec != asio::error::eof)
252 fail(parser.status_code(), ec.message().c_str());
253 return;
256 received_bytes(size + parser.body_start());
258 // handle tracker response
259 entry e;
260 e = bdecode(data, data + size);
262 if (e.type() == entry::dictionary_t)
264 parse(parser.status_code(), e);
266 else
268 std::string error_str("invalid bencoding of tracker response: \"");
269 for (char const* i = data, *end(data + size); i != end; ++i)
271 if (*i >= ' ' && *i <= '~') error_str += *i;
272 else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
274 error_str += "\"";
275 fail(parser.status_code(), error_str.c_str());
277 close();
280 bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret)
282 // extract peer id (if any)
283 if (info.type() != entry::dictionary_t)
285 fail(-1, "invalid response from tracker (invalid peer entry)");
286 return false;
288 entry const* i = info.find_key("peer id");
289 if (i != 0)
291 if (i->type() != entry::string_t || i->string().length() != 20)
293 fail(-1, "invalid response from tracker (invalid peer id)");
294 return false;
296 std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
298 else
300 // if there's no peer_id, just initialize it to a bunch of zeroes
301 std::fill_n(ret.pid.begin(), 20, 0);
304 // extract ip
305 i = info.find_key("ip");
306 if (i == 0 || i->type() != entry::string_t)
308 fail(-1, "invalid response from tracker");
309 return false;
311 ret.ip = i->string();
313 // extract port
314 i = info.find_key("port");
315 if (i == 0 || i->type() != entry::int_t)
317 fail(-1, "invalid response from tracker");
318 return false;
320 ret.port = (unsigned short)i->integer();
322 return true;
325 void http_tracker_connection::parse(int status_code, entry const& e)
327 boost::shared_ptr<request_callback> cb = requester();
328 if (!cb) return;
330 // parse the response
331 entry const* failure = e.find_key("failure reason");
332 if (failure && failure->type() == entry::string_t)
334 fail(status_code, failure->string().c_str());
335 return;
338 entry const* warning = e.find_key("warning message");
339 if (warning && warning->type() == entry::string_t)
341 cb->tracker_warning(tracker_req(), warning->string());
344 std::vector<peer_entry> peer_list;
346 if (tracker_req().kind == tracker_request::scrape_request)
348 std::string ih = tracker_req().info_hash.to_string();
350 entry const* files = e.find_key("files");
351 if (files == 0 || files->type() != entry::dictionary_t)
353 fail(-1, "invalid or missing 'files' entry in scrape response");
354 return;
357 entry const* scrape_data = files->find_key(ih);
358 if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t)
360 fail(-1, "missing or invalid info-hash entry in scrape response");
361 return;
363 entry const* complete = scrape_data->find_key("complete");
364 entry const* incomplete = scrape_data->find_key("incomplete");
365 entry const* downloaded = scrape_data->find_key("downloaded");
366 if (complete == 0 || incomplete == 0 || downloaded == 0
367 || complete->type() != entry::int_t
368 || incomplete->type() != entry::int_t
369 || downloaded->type() != entry::int_t)
371 fail(-1, "missing 'complete' or 'incomplete' entries in scrape response");
372 return;
374 cb->tracker_scrape_response(tracker_req(), int(complete->integer())
375 , int(incomplete->integer()), int(downloaded->integer()));
376 return;
379 entry const* interval = e.find_key("interval");
380 if (interval == 0 || interval->type() != entry::int_t)
382 fail(-1, "missing or invalid 'interval' entry in tracker response");
383 return;
386 entry const* peers_ent = e.find_key("peers");
387 if (peers_ent == 0)
389 fail(-1, "missing 'peers' entry in tracker response");
390 return;
393 if (peers_ent->type() == entry::string_t)
395 std::string const& peers = peers_ent->string();
396 for (std::string::const_iterator i = peers.begin();
397 i != peers.end();)
399 if (std::distance(i, peers.end()) < 6) break;
401 peer_entry p;
402 p.pid.clear();
403 error_code ec;
404 p.ip = detail::read_v4_address(i).to_string(ec);
405 if (ec) continue;
406 p.port = detail::read_uint16(i);
407 peer_list.push_back(p);
410 else if (peers_ent->type() == entry::list_t)
412 entry::list_type const& l = peers_ent->list();
413 for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
415 peer_entry p;
416 if (!extract_peer_info(*i, p)) return;
417 peer_list.push_back(p);
420 else
422 fail(-1, "invalid 'peers' entry in tracker response");
423 return;
426 entry const* ipv6_peers = e.find_key("peers6");
427 if (ipv6_peers && ipv6_peers->type() == entry::string_t)
429 std::string const& peers = ipv6_peers->string();
430 for (std::string::const_iterator i = peers.begin();
431 i != peers.end();)
433 if (std::distance(i, peers.end()) < 18) break;
435 peer_entry p;
436 p.pid.clear();
437 error_code ec;
438 p.ip = detail::read_v6_address(i).to_string(ec);
439 if (ec) continue;
440 p.port = detail::read_uint16(i);
441 peer_list.push_back(p);
445 // look for optional scrape info
446 int complete = -1;
447 int incomplete = -1;
448 address external_ip;
450 entry const* ip_ent = e.find_key("external ip");
451 if (ip_ent && ip_ent->type() == entry::string_t)
453 std::string const& ip = ip_ent->string();
454 char const* p = &ip[0];
455 if (ip.size() == address_v4::bytes_type::static_size)
456 external_ip = detail::read_v4_address(p);
457 else if (ip.size() == address_v6::bytes_type::static_size)
458 external_ip = detail::read_v6_address(p);
461 entry const* complete_ent = e.find_key("complete");
462 if (complete_ent && complete_ent->type() == entry::int_t)
463 complete = int(complete_ent->integer());
465 entry const* incomplete_ent = e.find_key("incomplete");
466 if (incomplete_ent && incomplete_ent->type() == entry::int_t)
467 incomplete = int(incomplete_ent->integer());
469 cb->tracker_response(tracker_req(), peer_list, interval->integer(), complete
470 , incomplete, external_ip);