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"
63 #include "libtorrent/aux_/session_impl.hpp"
65 using namespace libtorrent
;
71 http_tracker_connection::http_tracker_connection(
73 , connection_queue
& cc
74 , tracker_manager
& man
75 , tracker_request
const& req
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
)
84 , m_bind_iface(bind_infc
)
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"
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());
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
)
122 url
+= escape_string(
123 reinterpret_cast<const char*>(tracker_req().info_hash
.begin()), 20);
125 if (tracker_req().kind
== tracker_request::announce_request
)
128 url
+= escape_string(
129 reinterpret_cast<const char*>(tracker_req().pid
.begin()), 20);
132 url
+= boost::lexical_cast
<std::string
>(tracker_req().listen_port
);
135 url
+= boost::lexical_cast
<std::string
>(tracker_req().uploaded
);
137 url
+= "&downloaded=";
138 url
+= boost::lexical_cast
<std::string
>(tracker_req().downloaded
);
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"};
147 url
+= event_string
[tracker_req().event
- 1];
151 std::stringstream key_string
;
152 key_string
<< std::hex
<< tracker_req().key
;
153 url
+= key_string
.str();
158 url
+= boost::lexical_cast
<std::string
>(
159 (std::min
)(tracker_req().num_want
, 999));
161 if (settings
.announce_ip
!= address())
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";
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();
199 cb
->debug_log("==> TRACKER_REQUEST [ url: " + url
+ " ]");
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
)
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
)
230 boost::intrusive_ptr
<http_tracker_connection
> me(this);
232 if (ec
&& ec
!= asio::error::eof
)
234 fail(-1, ec
.message().c_str());
238 if (!parser
.header_finished())
240 fail(-1, "premature end of file");
244 if (parser
.status_code() != 200)
246 fail(parser
.status_code(), parser
.message().c_str());
250 if (ec
&& ec
!= asio::error::eof
)
252 fail(parser
.status_code(), ec
.message().c_str());
256 received_bytes(size
+ parser
.body_start());
258 // handle tracker response
260 e
= bdecode(data
, data
+ size
);
262 if (e
.type() == entry::dictionary_t
)
264 parse(parser
.status_code(), e
);
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
) + " ";
275 fail(parser
.status_code(), error_str
.c_str());
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)");
288 entry
const* i
= info
.find_key("peer id");
291 if (i
->type() != entry::string_t
|| i
->string().length() != 20)
293 fail(-1, "invalid response from tracker (invalid peer id)");
296 std::copy(i
->string().begin(), i
->string().end(), ret
.pid
.begin());
300 // if there's no peer_id, just initialize it to a bunch of zeroes
301 std::fill_n(ret
.pid
.begin(), 20, 0);
305 i
= info
.find_key("ip");
306 if (i
== 0 || i
->type() != entry::string_t
)
308 fail(-1, "invalid response from tracker");
311 ret
.ip
= i
->string();
314 i
= info
.find_key("port");
315 if (i
== 0 || i
->type() != entry::int_t
)
317 fail(-1, "invalid response from tracker");
320 ret
.port
= (unsigned short)i
->integer();
325 void http_tracker_connection::parse(int status_code
, entry
const& e
)
327 boost::shared_ptr
<request_callback
> cb
= requester();
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());
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");
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");
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");
374 cb
->tracker_scrape_response(tracker_req(), int(complete
->integer())
375 , int(incomplete
->integer()), int(downloaded
->integer()));
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");
386 entry
const* peers_ent
= e
.find_key("peers");
389 fail(-1, "missing 'peers' entry in tracker response");
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();
399 if (std::distance(i
, peers
.end()) < 6) break;
404 p
.ip
= detail::read_v4_address(i
).to_string(ec
);
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
)
416 if (!extract_peer_info(*i
, p
)) return;
417 peer_list
.push_back(p
);
422 fail(-1, "invalid 'peers' entry in tracker response");
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();
433 if (std::distance(i
, peers
.end()) < 18) break;
438 p
.ip
= detail::read_v6_address(i
).to_string(ec
);
440 p
.port
= detail::read_uint16(i
);
441 peer_list
.push_back(p
);
445 // look for optional scrape info
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
);