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"
43
#include <boost/bind.hpp>
45
#include "libtorrent/tracker_manager.hpp"
46
#include "libtorrent/http_tracker_connection.hpp"
47
#include "libtorrent/udp_tracker_connection.hpp"
48
#include "libtorrent/entry.hpp"
49
#include "libtorrent/bencode.hpp"
50
#include "libtorrent/torrent.hpp"
51
#include "libtorrent/peer_connection.hpp"
53
using namespace libtorrent;
54
using boost::tuples::make_tuple;
55
using boost::tuples::tuple;
62
minimum_tracker_response_length = 3,
63
http_buffer_size = 2048
84
// returns -1 if gzip header is invalid or the header size in bytes
85
int gzip_header(const char* buf, int size)
87
TORRENT_ASSERT(buf != 0);
88
TORRENT_ASSERT(size > 0);
90
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
91
const int total_size = size;
93
// The zip header cannot be shorter than 10 bytes
94
if (size < 10) return -1;
96
// check the magic header of gzip
97
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
99
int method = buffer[2];
100
int flags = buffer[3];
102
// check for reserved flag and make sure it's compressed with the correct metod
103
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
105
// skip time, xflags, OS code
113
if (size < 2) return -1;
115
extra_len = (buffer[1] << 8) | buffer[0];
117
if (size < (extra_len+2)) return -1;
118
size -= (extra_len + 2);
119
buffer += (extra_len + 2);
124
while (size && *buffer)
129
if (!size || *buffer) return -1;
135
if (flags & FCOMMENT)
137
while (size && *buffer)
142
if (!size || *buffer) return -1;
150
if (size < 2) return -1;
156
return total_size - size;
160
std::vector<char>& buffer
161
, tracker_request const& req
162
, request_callback* requester
163
, int maximum_tracker_response_length)
165
TORRENT_ASSERT(maximum_tracker_response_length > 0);
167
int header_len = gzip_header(&buffer[0], (int)buffer.size());
170
requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
174
// start off wth one kilobyte and grow
176
std::vector<char> inflate_buffer(1024);
178
// initialize the zlib-stream
181
// subtract 8 from the end of the buffer since that's CRC32 and input size
182
// and those belong to the gzip file
183
str.avail_in = (int)buffer.size() - header_len - 8;
184
str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
185
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
186
str.avail_out = (int)inflate_buffer.size();
190
// -15 is really important. It will make inflate() not look for a zlib header
191
// and just deflate the buffer
192
if (inflateInit2(&str, -15) != Z_OK)
194
requester->tracker_request_error(req, 200, "gzip out of memory");
198
// inflate and grow inflate_buffer as needed
199
int ret = inflate(&str, Z_SYNC_FLUSH);
202
if (str.avail_out == 0)
204
if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
207
requester->tracker_request_error(req, 200
208
, "tracker response too large");
211
int new_size = (int)inflate_buffer.size() * 2;
212
if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
213
int old_size = (int)inflate_buffer.size();
215
inflate_buffer.resize(new_size);
216
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
217
str.avail_out = new_size - old_size;
220
ret = inflate(&str, Z_SYNC_FLUSH);
223
inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
226
if (ret != Z_STREAM_END)
228
requester->tracker_request_error(req, 200, "gzip error");
232
// commit the resulting buffer
233
std::swap(buffer, inflate_buffer);
237
std::string base64encode(const std::string& s)
239
static const char base64_table[] =
241
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
242
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
243
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
244
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
245
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
246
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
247
'w', 'x', 'y', 'z', '0', '1', '2', '3',
248
'4', '5', '6', '7', '8', '9', '+', '/'
251
unsigned char inbuf[3];
252
unsigned char outbuf[4];
255
for (std::string::const_iterator i = s.begin(); i != s.end();)
257
// available input is 1,2 or 3 bytes
258
// since we read 3 bytes at a time at most
259
int available_input = (std::min)(3, (int)std::distance(i, s.end()));
261
// clear input buffer
262
std::fill(inbuf, inbuf+3, 0);
264
// read a chunk of input into inbuf
265
for (int j = 0; j < available_input; ++j)
271
// encode inbuf to outbuf
272
outbuf[0] = (inbuf[0] & 0xfc) >> 2;
273
outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
274
outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
275
outbuf[3] = inbuf[2] & 0x3f;
278
for (int j = 0; j < available_input+1; ++j)
280
ret += base64_table[outbuf[j]];
284
for (int j = 0; j < 3 - available_input; ++j)
292
timeout_handler::timeout_handler(asio::strand& str)
294
, m_start_time(time_now())
295
, m_read_time(time_now())
296
, m_timeout(str.io_service())
297
, m_completion_timeout(0)
301
void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
303
m_completion_timeout = completion_timeout;
304
m_read_timeout = read_timeout;
305
m_start_time = time_now();
306
m_read_time = time_now();
308
m_timeout.expires_at((std::min)(
309
m_read_time + seconds(m_read_timeout)
310
, m_start_time + seconds(m_completion_timeout)));
311
m_timeout.async_wait(m_strand.wrap(bind(
312
&timeout_handler::timeout_callback, self(), _1)));
315
void timeout_handler::restart_read_timeout()
317
m_read_time = time_now();
320
void timeout_handler::cancel()
322
m_completion_timeout = 0;
326
void timeout_handler::timeout_callback(asio::error_code const& error) try
329
if (m_completion_timeout == 0) return;
331
ptime now(time_now());
332
time_duration receive_timeout = now - m_read_time;
333
time_duration completion_timeout = now - m_start_time;
336
< total_seconds(receive_timeout)
337
|| m_completion_timeout
338
< total_seconds(completion_timeout))
344
m_timeout.expires_at((std::min)(
345
m_read_time + seconds(m_read_timeout)
346
, m_start_time + seconds(m_completion_timeout)));
347
m_timeout.async_wait(m_strand.wrap(
348
bind(&timeout_handler::timeout_callback, self(), _1)));
350
catch (std::exception& e)
352
TORRENT_ASSERT(false);
355
tracker_connection::tracker_connection(
357
, tracker_request req
359
, address bind_interface_
360
, boost::weak_ptr<request_callback> r)
361
: timeout_handler(str)
363
, m_bind_interface(bind_interface_)
368
boost::shared_ptr<request_callback> tracker_connection::requester()
370
return m_requester.lock();
373
void tracker_connection::fail(int code, char const* msg)
375
boost::shared_ptr<request_callback> cb = requester();
376
if (cb) cb->tracker_request_error(m_req, code, msg);
380
void tracker_connection::fail_timeout()
382
boost::shared_ptr<request_callback> cb = requester();
383
if (cb) cb->tracker_request_timed_out(m_req);
387
void tracker_connection::close()
390
m_man.remove_request(this);
393
void tracker_manager::remove_request(tracker_connection const* c)
395
mutex_t::scoped_lock l(m_mutex);
397
tracker_connections_t::iterator i = std::find(m_connections.begin()
398
, m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
399
if (i == m_connections.end()) return;
401
m_connections.erase(i);
404
// returns protocol, auth, hostname, port, path
405
tuple<std::string, std::string, std::string, int, std::string>
406
parse_url_components(std::string url)
408
std::string hostname; // hostname only
409
std::string auth; // user:pass
410
std::string protocol; // should be http
414
std::string::iterator start = url.begin();
415
// remove white spaces in front of the url
416
while (start != url.end() && (*start == ' ' || *start == '\t'))
418
std::string::iterator end
419
= std::find(url.begin(), url.end(), ':');
420
protocol.assign(start, end);
422
if (end == url.end()) throw std::runtime_error("invalid url");
424
if (end == url.end()) throw std::runtime_error("invalid url");
425
if (*end != '/') throw std::runtime_error("invalid url");
427
if (end == url.end()) throw std::runtime_error("invalid url");
428
if (*end != '/') throw std::runtime_error("invalid url");
432
std::string::iterator at = std::find(start, url.end(), '@');
433
std::string::iterator colon = std::find(start, url.end(), ':');
434
end = std::find(start, url.end(), '/');
437
&& colon != url.end()
441
auth.assign(start, at);
446
std::string::iterator port_pos;
448
// this is for IPv6 addresses
449
if (start != url.end() && *start == '[')
451
port_pos = std::find(start, url.end(), ']');
452
if (port_pos == url.end()) throw std::runtime_error("invalid hostname syntax");
453
port_pos = std::find(port_pos, url.end(), ':');
457
port_pos = std::find(start, url.end(), ':');
462
hostname.assign(start, port_pos);
466
port = boost::lexical_cast<int>(std::string(port_pos, end));
468
catch(boost::bad_lexical_cast&)
470
throw std::runtime_error("invalid url: \"" + url
471
+ "\", port number expected");
476
hostname.assign(start, end);
480
return make_tuple(protocol, auth, hostname, port
481
, std::string(start, url.end()));
484
void tracker_manager::queue_request(
486
, connection_queue& cc
487
, tracker_request req
488
, std::string const& auth
490
, boost::weak_ptr<request_callback> c)
492
mutex_t::scoped_lock l(m_mutex);
493
TORRENT_ASSERT(req.num_want >= 0);
494
if (req.event == tracker_request::stopped)
497
TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped);
498
if (m_abort && req.event != tracker_request::stopped)
503
std::string protocol;
504
std::string hostname;
506
std::string request_string;
508
using boost::tuples::ignore;
509
// TODO: should auth be used here?
510
boost::tie(protocol, ignore, hostname, port, request_string)
511
= parse_url_components(req.url);
513
boost::intrusive_ptr<tracker_connection> con;
515
if (protocol == "http")
517
con = new http_tracker_connection(
531
else if (protocol == "udp")
533
con = new udp_tracker_connection(
545
throw std::runtime_error("unkown protocol in tracker url");
548
m_connections.push_back(con);
550
boost::shared_ptr<request_callback> cb = con->requester();
551
if (cb) cb->m_manager = this;
553
catch (std::exception& e)
555
if (boost::shared_ptr<request_callback> r = c.lock())
556
r->tracker_request_error(req, -1, e.what());
560
void tracker_manager::abort_all_requests()
562
// removes all connections from m_connections
563
// except those with a requester == 0 (since those are
564
// 'event=stopped'-requests)
565
mutex_t::scoped_lock l(m_mutex);
568
tracker_connections_t keep_connections;
570
for (tracker_connections_t::const_iterator i =
571
m_connections.begin(); i != m_connections.end(); ++i)
573
tracker_request const& req = (*i)->tracker_req();
574
if (req.event == tracker_request::stopped)
575
keep_connections.push_back(*i);
578
std::swap(m_connections, keep_connections);
581
bool tracker_manager::empty() const
583
mutex_t::scoped_lock l(m_mutex);
584
return m_connections.empty();
587
int tracker_manager::num_requests() const
589
mutex_t::scoped_lock l(m_mutex);
590
return m_connections.size();