LT SYNC
[tore.git] / libtorrent / src / http_tracker_connection.cpp
blob6d79549663124e42e548ac25cd0c1792491e5e70
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"
64 using namespace libtorrent;
65 using boost::bind;
67 namespace libtorrent
70 http_tracker_connection::http_tracker_connection(
71 io_service& ios
72 , connection_queue& cc
73 , tracker_manager& man
74 , tracker_request const& req
75 , address bind_infc
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)
81 , m_man(man)
82 , m_settings(stn)
83 , m_bind_iface(bind_infc)
84 , m_ps(ps)
85 , m_cc(cc)
86 , m_ios(ios)
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"
97 // in request
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());
104 return;
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)
114 url += "&";
115 else
116 url += "?";
118 url += "info_hash=";
119 url += escape_string(
120 reinterpret_cast<const char*>(tracker_req().info_hash.begin()), 20);
122 if (tracker_req().kind == tracker_request::announce_request)
124 url += "&peer_id=";
125 url += escape_string(
126 reinterpret_cast<const char*>(tracker_req().pid.begin()), 20);
128 url += "&port=";
129 url += boost::lexical_cast<std::string>(tracker_req().listen_port);
131 url += "&uploaded=";
132 url += boost::lexical_cast<std::string>(tracker_req().uploaded);
134 url += "&downloaded=";
135 url += boost::lexical_cast<std::string>(tracker_req().downloaded);
137 url += "&left=";
138 url += boost::lexical_cast<std::string>(tracker_req().left);
140 if (tracker_req().event != tracker_request::none)
142 const char* event_string[] = {"completed", "started", "stopped"};
143 url += "&event=";
144 url += event_string[tracker_req().event - 1];
147 url += "&key=";
148 std::stringstream key_string;
149 key_string << std::hex << tracker_req().key;
150 url += key_string.str();
152 url += "&compact=1";
154 url += "&numwant=";
155 url += boost::lexical_cast<std::string>(
156 (std::min)(tracker_req().num_want, 999));
158 if (m_settings.announce_ip != address())
160 url += "&ip=";
161 url += m_settings.announce_ip.to_string();
164 #ifndef TORRENT_DISABLE_ENCRYPTION
165 url += "&supportcrypto=1";
166 #endif
167 url += "&ipv6=";
168 url += tracker_req().ipv6;
170 // extension that tells the tracker that
171 // we don't need any peer_id's in the response
172 url += "&no_peer_id=1";
175 m_tracker_connection.reset(new http_connection(m_ios, m_cc
176 , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)));
178 int timeout = tracker_req().event==tracker_request::stopped
179 ?m_settings.stop_tracker_timeout
180 :m_settings.tracker_completion_timeout;
182 m_tracker_connection->get(url, seconds(timeout)
183 , 1, &m_ps, 5, m_settings.user_agent, m_bind_iface);
185 // the url + 100 estimated header size
186 sent_bytes(url.size() + 100);
188 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
190 boost::shared_ptr<request_callback> cb = requester();
191 if (cb)
193 cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
195 #endif
198 void http_tracker_connection::close()
200 if (m_tracker_connection)
202 m_tracker_connection->close();
203 m_tracker_connection.reset();
205 tracker_connection::close();
208 void http_tracker_connection::on_response(error_code const& ec
209 , http_parser const& parser, char const* data, int size)
211 // keep this alive
212 boost::intrusive_ptr<http_tracker_connection> me(this);
214 if (ec && ec != asio::error::eof)
216 fail(-1, ec.message().c_str());
217 return;
220 if (!parser.header_finished())
222 fail(-1, "premature end of file");
223 return;
226 if (parser.status_code() != 200)
228 fail(parser.status_code(), parser.message().c_str());
229 return;
232 if (ec && ec != asio::error::eof)
234 fail(parser.status_code(), ec.message().c_str());
235 return;
238 received_bytes(size + parser.body_start());
240 // handle tracker response
241 entry e;
242 e = bdecode(data, data + size);
244 if (e.type() == entry::dictionary_t)
246 parse(parser.status_code(), e);
248 else
250 std::string error_str("invalid bencoding of tracker response: \"");
251 for (char const* i = data, *end(data + size); i != end; ++i)
253 if (*i >= ' ' && *i <= '~') error_str += *i;
254 else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
256 error_str += "\"";
257 fail(parser.status_code(), error_str.c_str());
259 close();
262 bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret)
264 // extract peer id (if any)
265 if (info.type() != entry::dictionary_t)
267 fail(-1, "invalid response from tracker (invalid peer entry)");
268 return false;
270 entry const* i = info.find_key("peer id");
271 if (i != 0)
273 if (i->type() != entry::string_t || i->string().length() != 20)
275 fail(-1, "invalid response from tracker (invalid peer id)");
276 return false;
278 std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
280 else
282 // if there's no peer_id, just initialize it to a bunch of zeroes
283 std::fill_n(ret.pid.begin(), 20, 0);
286 // extract ip
287 i = info.find_key("ip");
288 if (i == 0 || i->type() != entry::string_t)
290 fail(-1, "invalid response from tracker");
291 return false;
293 ret.ip = i->string();
295 // extract port
296 i = info.find_key("port");
297 if (i == 0 || i->type() != entry::int_t)
299 fail(-1, "invalid response from tracker");
300 return false;
302 ret.port = (unsigned short)i->integer();
304 return true;
307 void http_tracker_connection::parse(int status_code, entry const& e)
309 boost::shared_ptr<request_callback> cb = requester();
310 if (!cb) return;
312 // parse the response
313 entry const* failure = e.find_key("failure reason");
314 if (failure && failure->type() == entry::string_t)
316 fail(status_code, failure->string().c_str());
317 return;
320 entry const* warning = e.find_key("warning message");
321 if (warning && warning->type() == entry::string_t)
323 cb->tracker_warning(tracker_req(), warning->string());
326 std::vector<peer_entry> peer_list;
328 if (tracker_req().kind == tracker_request::scrape_request)
330 std::string ih = tracker_req().info_hash.to_string();
332 entry const* files = e.find_key("files");
333 if (files == 0 || files->type() != entry::dictionary_t)
335 fail(-1, "invalid or missing 'files' entry in scrape response");
336 return;
339 entry const* scrape_data = files->find_key(ih);
340 if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t)
342 fail(-1, "missing or invalid info-hash entry in scrape response");
343 return;
345 entry const* complete = scrape_data->find_key("complete");
346 entry const* incomplete = scrape_data->find_key("incomplete");
347 entry const* downloaded = scrape_data->find_key("downloaded");
348 if (complete == 0 || incomplete == 0 || downloaded == 0
349 || complete->type() != entry::int_t
350 || incomplete->type() != entry::int_t
351 || downloaded->type() != entry::int_t)
353 fail(-1, "missing 'complete' or 'incomplete' entries in scrape response");
354 return;
356 cb->tracker_scrape_response(tracker_req(), int(complete->integer())
357 , int(incomplete->integer()), int(downloaded->integer()));
358 return;
361 entry const* interval = e.find_key("interval");
362 if (interval == 0 || interval->type() != entry::int_t)
364 fail(-1, "missing or invalid 'interval' entry in tracker response");
365 return;
368 entry const* peers_ent = e.find_key("peers");
369 if (peers_ent == 0)
371 fail(-1, "missing 'peers' entry in tracker response");
372 return;
375 if (peers_ent->type() == entry::string_t)
377 std::string const& peers = peers_ent->string();
378 for (std::string::const_iterator i = peers.begin();
379 i != peers.end();)
381 if (std::distance(i, peers.end()) < 6) break;
383 peer_entry p;
384 p.pid.clear();
385 p.ip = detail::read_v4_address(i).to_string();
386 p.port = detail::read_uint16(i);
387 peer_list.push_back(p);
390 else if (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)
395 peer_entry p;
396 if (!extract_peer_info(*i, p)) return;
397 peer_list.push_back(p);
400 else
402 fail(-1, "invalid 'peers' entry in tracker response");
403 return;
406 entry const* ipv6_peers = e.find_key("peers6");
407 if (ipv6_peers && ipv6_peers->type() == entry::string_t)
409 std::string const& peers = ipv6_peers->string();
410 for (std::string::const_iterator i = peers.begin();
411 i != peers.end();)
413 if (std::distance(i, peers.end()) < 18) break;
415 peer_entry p;
416 p.pid.clear();
417 p.ip = detail::read_v6_address(i).to_string();
418 p.port = detail::read_uint16(i);
419 peer_list.push_back(p);
423 // look for optional scrape info
424 int complete = -1;
425 int incomplete = -1;
426 address external_ip;
428 entry const* ip_ent = e.find_key("external ip");
429 if (ip_ent && ip_ent->type() == entry::string_t)
431 std::string const& ip = ip_ent->string();
432 char const* p = &ip[0];
433 if (ip.size() == address_v4::bytes_type::static_size)
434 external_ip = detail::read_v4_address(p);
435 else if (ip.size() == address_v6::bytes_type::static_size)
436 external_ip = detail::read_v6_address(p);
439 entry const* complete_ent = e.find_key("complete");
440 if (complete_ent && complete_ent->type() == entry::int_t)
441 complete = int(complete_ent->integer());
443 entry const* incomplete_ent = e.find_key("incomplete");
444 if (incomplete_ent && incomplete_ent->type() == entry::int_t)
445 incomplete = int(incomplete_ent->integer());
447 cb->tracker_response(tracker_req(), peer_list, interval->integer(), complete
448 , incomplete, external_ip);