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
+= boost::lexical_cast
<std::string
>(tracker_req().listen_port
);
132 url
+= boost::lexical_cast
<std::string
>(tracker_req().uploaded
);
134 url
+= "&downloaded=";
135 url
+= boost::lexical_cast
<std::string
>(tracker_req().downloaded
);
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"};
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
+= boost::lexical_cast
<std::string
>(
156 (std::min
)(tracker_req().num_want
, 999));
158 if (m_settings
.announce_ip
!= address())
161 url
+= m_settings
.announce_ip
.to_string();
164 #ifndef TORRENT_DISABLE_ENCRYPTION
165 url
+= "&supportcrypto=1";
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();
193 cb
->debug_log("==> TRACKER_REQUEST [ url: " + url
+ " ]");
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
)
212 boost::intrusive_ptr
<http_tracker_connection
> me(this);
214 if (ec
&& ec
!= asio::error::eof
)
216 fail(-1, ec
.message().c_str());
220 if (!parser
.header_finished())
222 fail(-1, "premature end of file");
226 if (parser
.status_code() != 200)
228 fail(parser
.status_code(), parser
.message().c_str());
232 if (ec
&& ec
!= asio::error::eof
)
234 fail(parser
.status_code(), ec
.message().c_str());
238 received_bytes(size
+ parser
.body_start());
240 // handle tracker response
242 e
= bdecode(data
, data
+ size
);
244 if (e
.type() == entry::dictionary_t
)
246 parse(parser
.status_code(), e
);
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
) + " ";
257 fail(parser
.status_code(), error_str
.c_str());
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)");
270 entry
const* i
= info
.find_key("peer id");
273 if (i
->type() != entry::string_t
|| i
->string().length() != 20)
275 fail(-1, "invalid response from tracker (invalid peer id)");
278 std::copy(i
->string().begin(), i
->string().end(), ret
.pid
.begin());
282 // if there's no peer_id, just initialize it to a bunch of zeroes
283 std::fill_n(ret
.pid
.begin(), 20, 0);
287 i
= info
.find_key("ip");
288 if (i
== 0 || i
->type() != entry::string_t
)
290 fail(-1, "invalid response from tracker");
293 ret
.ip
= i
->string();
296 i
= info
.find_key("port");
297 if (i
== 0 || i
->type() != entry::int_t
)
299 fail(-1, "invalid response from tracker");
302 ret
.port
= (unsigned short)i
->integer();
307 void http_tracker_connection::parse(int status_code
, entry
const& e
)
309 boost::shared_ptr
<request_callback
> cb
= requester();
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());
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");
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");
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");
356 cb
->tracker_scrape_response(tracker_req(), int(complete
->integer())
357 , int(incomplete
->integer()), int(downloaded
->integer()));
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");
368 entry
const* peers_ent
= e
.find_key("peers");
371 fail(-1, "missing 'peers' entry in tracker response");
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();
381 if (std::distance(i
, peers
.end()) < 6) break;
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
)
396 if (!extract_peer_info(*i
, p
)) return;
397 peer_list
.push_back(p
);
402 fail(-1, "invalid 'peers' entry in tracker response");
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();
413 if (std::distance(i
, peers
.end()) < 18) break;
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
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
);