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 // TODO: authentication
84 std::string url
= req
.url
;
86 if (req
.kind
== tracker_request::scrape_request
)
88 // find and replace "announce" with "scrape"
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());
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
)
111 url
+= escape_string(
112 reinterpret_cast<const char*>(req
.info_hash
.begin()), 20);
114 if (req
.kind
== tracker_request::announce_request
)
117 url
+= escape_string(
118 reinterpret_cast<const char*>(req
.pid
.begin()), 20);
121 url
+= boost::lexical_cast
<std::string
>(req
.listen_port
);
124 url
+= boost::lexical_cast
<std::string
>(req
.uploaded
);
126 url
+= "&downloaded=";
127 url
+= boost::lexical_cast
<std::string
>(req
.downloaded
);
130 url
+= boost::lexical_cast
<std::string
>(req
.left
);
132 if (req
.event
!= tracker_request::none
)
134 const char* event_string
[] = {"completed", "started", "stopped"};
136 url
+= event_string
[req
.event
- 1];
140 std::stringstream key_string
;
141 key_string
<< std::hex
<< req
.key
;
142 url
+= key_string
.str();
147 url
+= boost::lexical_cast
<std::string
>(
148 (std::min
)(req
.num_want
, 999));
150 if (stn
.announce_ip
!= address())
153 url
+= stn
.announce_ip
.to_string();
156 #ifndef TORRENT_DISABLE_ENCRYPTION
157 url
+= "&supportcrypto=1";
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();
182 cb
->debug_log("==> TRACKER_REQUEST [ url: " + url
+ " ]");
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
)
201 boost::intrusive_ptr
<http_tracker_connection
> me(this);
203 if (ec
&& ec
!= asio::error::eof
)
205 fail(-1, ec
.message().c_str());
209 if (!parser
.header_finished())
211 fail(-1, "premature end of file");
215 if (parser
.status_code() != 200)
217 fail(parser
.status_code(), parser
.message().c_str());
221 if (ec
&& ec
!= asio::error::eof
)
223 fail(parser
.status_code(), ec
.message().c_str());
227 // handle tracker response
229 e
= bdecode(data
, data
+ size
);
231 if (e
.type() != entry::undefined_t
)
233 parse(parser
.status_code(), e
);
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
) + " ";
244 fail(parser
.status_code(), error_str
.c_str());
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)");
257 entry
const* i
= info
.find_key("peer id");
260 if (i
->type() != entry::string_t
|| i
->string().length() != 20)
262 fail(-1, "invalid response from tracker (invalid peer id)");
265 std::copy(i
->string().begin(), i
->string().end(), ret
.pid
.begin());
269 // if there's no peer_id, just initialize it to a bunch of zeroes
270 std::fill_n(ret
.pid
.begin(), 20, 0);
274 i
= info
.find_key("ip");
275 if (i
== 0 || i
->type() != entry::string_t
)
277 fail(-1, "invalid response from tracker");
280 ret
.ip
= i
->string();
283 i
= info
.find_key("port");
284 if (i
== 0 || i
->type() != entry::int_t
)
286 fail(-1, "invalid response from tracker");
289 ret
.port
= (unsigned short)i
->integer();
294 void http_tracker_connection::parse(int status_code
, entry
const& e
)
296 boost::shared_ptr
<request_callback
> cb
= requester();
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());
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");
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");
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");
343 cb
->tracker_scrape_response(tracker_req(), int(complete
->integer())
344 , int(incomplete
->integer()), int(downloaded
->integer()));
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");
355 entry
const* peers_ent
= e
.find_key("peers");
358 fail(-1, "missing 'peers' entry in tracker response");
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();
368 if (std::distance(i
, peers
.end()) < 6) break;
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
)
383 if (!extract_peer_info(*i
, p
)) return;
384 peer_list
.push_back(p
);
389 fail(-1, "invalid 'peers' entry in tracker response");
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();
400 if (std::distance(i
, peers
.end()) < 18) break;
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
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
);