added some precautionary checks in bdecoder
[libtorrent.git] / src / http_tracker_connection.cpp
blobb87ee0a28ddaebd48fe323ae9625db3564d973fc
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)
83 // TODO: authentication
84 std::string url = req.url;
86 if (req.kind == tracker_request::scrape_request)
88 // find and replace "announce" with "scrape"
89 // in request
91 std::size_t pos = url.find("announce");
92 if (pos == std::string::npos)
94 fail(-1, ("scrape is not available on url: '"
95 + req.url +"'").c_str());
96 return;
98 url.replace(pos, 8, "scrape");
101 // if request-string already contains
102 // some parameters, append an ampersand instead
103 // of a question mark
104 size_t arguments_start = url.find('?');
105 if (arguments_start != std::string::npos)
106 url += "&";
107 else
108 url += "?";
110 url += "info_hash=";
111 url += escape_string(
112 reinterpret_cast<const char*>(req.info_hash.begin()), 20);
114 if (req.kind == tracker_request::announce_request)
116 url += "&peer_id=";
117 url += escape_string(
118 reinterpret_cast<const char*>(req.pid.begin()), 20);
120 url += "&port=";
121 url += boost::lexical_cast<std::string>(req.listen_port);
123 url += "&uploaded=";
124 url += boost::lexical_cast<std::string>(req.uploaded);
126 url += "&downloaded=";
127 url += boost::lexical_cast<std::string>(req.downloaded);
129 url += "&left=";
130 url += boost::lexical_cast<std::string>(req.left);
132 if (req.event != tracker_request::none)
134 const char* event_string[] = {"completed", "started", "stopped"};
135 url += "&event=";
136 url += event_string[req.event - 1];
139 url += "&key=";
140 std::stringstream key_string;
141 key_string << std::hex << req.key;
142 url += key_string.str();
144 url += "&compact=1";
146 url += "&numwant=";
147 url += boost::lexical_cast<std::string>(
148 (std::min)(req.num_want, 999));
150 if (stn.announce_ip != address())
152 url += "&ip=";
153 url += stn.announce_ip.to_string();
156 #ifndef TORRENT_DISABLE_ENCRYPTION
157 url += "&supportcrypto=1";
158 #endif
159 url += "&ipv6=";
160 url += req.ipv6;
162 // extension that tells the tracker that
163 // we don't need any peer_id's in the response
164 url += "&no_peer_id=1";
167 m_tracker_connection.reset(new http_connection(ios, cc
168 , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)));
170 int timeout = req.event==tracker_request::stopped
171 ?stn.stop_tracker_timeout
172 :stn.tracker_completion_timeout;
174 m_tracker_connection->get(url, seconds(timeout)
175 , 1, &ps, 5, stn.user_agent, bind_infc);
177 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
179 boost::shared_ptr<request_callback> cb = requester();
180 if (cb)
182 cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
184 #endif
187 void http_tracker_connection::close()
189 if (m_tracker_connection)
191 m_tracker_connection->close();
192 m_tracker_connection.reset();
194 tracker_connection::close();
197 void http_tracker_connection::on_response(error_code const& ec
198 , http_parser const& parser, char const* data, int size)
200 // keep this alive
201 boost::intrusive_ptr<http_tracker_connection> me(this);
203 if (ec && ec != asio::error::eof)
205 fail(-1, ec.message().c_str());
206 return;
209 if (!parser.header_finished())
211 fail(-1, "premature end of file");
212 return;
215 if (parser.status_code() != 200)
217 fail(parser.status_code(), parser.message().c_str());
218 return;
221 if (ec && ec != asio::error::eof)
223 fail(parser.status_code(), ec.message().c_str());
224 return;
227 // handle tracker response
228 entry e;
229 e = bdecode(data, data + size);
231 if (e.type() != entry::undefined_t)
233 parse(parser.status_code(), e);
235 else
237 std::string error_str("invalid bencoding of tracker response: \"");
238 for (char const* i = data, *end(data + size); i != end; ++i)
240 if (std::isprint(*i)) error_str += *i;
241 else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
243 error_str += "\"";
244 fail(parser.status_code(), error_str.c_str());
246 close();
249 bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret)
251 // extract peer id (if any)
252 if (info.type() != entry::dictionary_t)
254 fail(-1, "invalid response from tracker (invalid peer entry)");
255 return false;
257 entry const* i = info.find_key("peer id");
258 if (i != 0)
260 if (i->type() != entry::string_t || i->string().length() != 20)
262 fail(-1, "invalid response from tracker (invalid peer id)");
263 return false;
265 std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
267 else
269 // if there's no peer_id, just initialize it to a bunch of zeroes
270 std::fill_n(ret.pid.begin(), 20, 0);
273 // extract ip
274 i = info.find_key("ip");
275 if (i == 0 || i->type() != entry::string_t)
277 fail(-1, "invalid response from tracker");
278 return false;
280 ret.ip = i->string();
282 // extract port
283 i = info.find_key("port");
284 if (i == 0 || i->type() != entry::int_t)
286 fail(-1, "invalid response from tracker");
287 return false;
289 ret.port = (unsigned short)i->integer();
291 return true;
294 void http_tracker_connection::parse(int status_code, entry const& e)
296 boost::shared_ptr<request_callback> cb = requester();
297 if (!cb) return;
299 // parse the response
300 entry const* failure = e.find_key("failure reason");
301 if (failure && failure->type() == entry::string_t)
303 fail(status_code, failure->string().c_str());
304 return;
307 entry const* warning = e.find_key("warning message");
308 if (warning && warning->type() == entry::string_t)
310 cb->tracker_warning(tracker_req(), warning->string());
313 std::vector<peer_entry> peer_list;
315 if (tracker_req().kind == tracker_request::scrape_request)
317 std::string ih = tracker_req().info_hash.to_string();
319 entry const* files = e.find_key("files");
320 if (files == 0 || files->type() != entry::dictionary_t)
322 fail(-1, "invalid or missing 'files' entry in scrape response");
323 return;
326 entry const* scrape_data = files->find_key(ih);
327 if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t)
329 fail(-1, "missing or invalid info-hash entry in scrape response");
330 return;
332 entry const* complete = scrape_data->find_key("complete");
333 entry const* incomplete = scrape_data->find_key("incomplete");
334 entry const* downloaded = scrape_data->find_key("downloaded");
335 if (complete == 0 || incomplete == 0 || downloaded == 0
336 || complete->type() != entry::int_t
337 || incomplete->type() != entry::int_t
338 || downloaded->type() != entry::int_t)
340 fail(-1, "missing 'complete' or 'incomplete' entries in scrape response");
341 return;
343 cb->tracker_scrape_response(tracker_req(), int(complete->integer())
344 , int(incomplete->integer()), int(downloaded->integer()));
345 return;
348 entry const* interval = e.find_key("interval");
349 if (interval == 0 || interval->type() != entry::int_t)
351 fail(-1, "missing or invalid 'interval' entry in tracker response");
352 return;
355 entry const* peers_ent = e.find_key("peers");
356 if (peers_ent == 0)
358 fail(-1, "missing 'peers' entry in tracker response");
359 return;
362 if (peers_ent->type() == entry::string_t)
364 std::string const& peers = peers_ent->string();
365 for (std::string::const_iterator i = peers.begin();
366 i != peers.end();)
368 if (std::distance(i, peers.end()) < 6) break;
370 peer_entry p;
371 p.pid.clear();
372 p.ip = detail::read_v4_address(i).to_string();
373 p.port = detail::read_uint16(i);
374 peer_list.push_back(p);
377 else if (peers_ent->type() == entry::list_t)
379 entry::list_type const& l = peers_ent->list();
380 for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
382 peer_entry p;
383 if (!extract_peer_info(*i, p)) return;
384 peer_list.push_back(p);
387 else
389 fail(-1, "invalid 'peers' entry in tracker response");
390 return;
393 entry const* ipv6_peers = e.find_key("peers6");
394 if (ipv6_peers && ipv6_peers->type() == entry::string_t)
396 std::string const& peers = ipv6_peers->string();
397 for (std::string::const_iterator i = peers.begin();
398 i != peers.end();)
400 if (std::distance(i, peers.end()) < 18) break;
402 peer_entry p;
403 p.pid.clear();
404 p.ip = detail::read_v6_address(i).to_string();
405 p.port = detail::read_uint16(i);
406 peer_list.push_back(p);
410 // look for optional scrape info
411 int complete = -1;
412 int incomplete = -1;
413 address external_ip;
415 entry const* ip_ent = e.find_key("external ip");
416 if (ip_ent && ip_ent->type() == entry::string_t)
418 std::string const& ip = ip_ent->string();
419 char const* p = &ip[0];
420 if (ip.size() == address_v4::bytes_type::static_size)
421 external_ip = detail::read_v4_address(p);
422 else if (ip.size() == address_v6::bytes_type::static_size)
423 external_ip = detail::read_v6_address(p);
426 entry const* complete_ent = e.find_key("complete");
427 if (complete_ent && complete_ent->type() == entry::int_t)
428 complete = int(complete_ent->integer());
430 entry const* incomplete_ent = e.find_key("incomplete");
431 if (incomplete_ent && incomplete_ent->type() == entry::int_t)
432 incomplete = int(incomplete_ent->integer());
434 cb->tracker_response(tracker_req(), peer_list, interval->integer(), complete
435 , incomplete, external_ip);