~ubuntu-branches/ubuntu/intrepid/miro/intrepid

« back to all changes in this revision

Viewing changes to portable/libtorrent/src/tracker_manager.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Christopher James Halse Rogers
  • Date: 2008-02-09 13:37:10 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20080209133710-9rs90q6gckvp1b6i
Tags: 1.1.2-0ubuntu1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 
 
3
Copyright (c) 2003, Arvid Norberg
 
4
All rights reserved.
 
5
 
 
6
Redistribution and use in source and binary forms, with or without
 
7
modification, are permitted provided that the following conditions
 
8
are met:
 
9
 
 
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.
 
18
 
 
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.
 
30
 
 
31
*/
 
32
 
 
33
#include "libtorrent/pch.hpp"
 
34
 
 
35
#include <vector>
 
36
#include <iostream>
 
37
#include <cctype>
 
38
#include <iomanip>
 
39
#include <sstream>
 
40
 
 
41
#include "zlib.h"
 
42
 
 
43
#include <boost/bind.hpp>
 
44
 
 
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"
 
52
 
 
53
using namespace libtorrent;
 
54
using boost::tuples::make_tuple;
 
55
using boost::tuples::tuple;
 
56
using boost::bind;
 
57
 
 
58
namespace
 
59
{
 
60
        enum
 
61
        {
 
62
                minimum_tracker_response_length = 3,
 
63
                http_buffer_size = 2048
 
64
        };
 
65
 
 
66
 
 
67
        enum
 
68
        {
 
69
                FTEXT = 0x01,
 
70
                FHCRC = 0x02,
 
71
                FEXTRA = 0x04,
 
72
                FNAME = 0x08,
 
73
                FCOMMENT = 0x10,
 
74
                FRESERVED = 0xe0,
 
75
 
 
76
                GZIP_MAGIC0 = 0x1f,
 
77
                GZIP_MAGIC1 = 0x8b
 
78
        };
 
79
 
 
80
}
 
81
 
 
82
namespace libtorrent
 
83
{
 
84
        // returns -1 if gzip header is invalid or the header size in bytes
 
85
        int gzip_header(const char* buf, int size)
 
86
        {
 
87
                TORRENT_ASSERT(buf != 0);
 
88
                TORRENT_ASSERT(size > 0);
 
89
 
 
90
                const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
 
91
                const int total_size = size;
 
92
 
 
93
                // The zip header cannot be shorter than 10 bytes
 
94
                if (size < 10) return -1;
 
95
 
 
96
                // check the magic header of gzip
 
97
                if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
 
98
 
 
99
                int method = buffer[2];
 
100
                int flags = buffer[3];
 
101
 
 
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;
 
104
 
 
105
                // skip time, xflags, OS code
 
106
                size -= 10;
 
107
                buffer += 10;
 
108
 
 
109
                if (flags & FEXTRA)
 
110
                {
 
111
                        int extra_len;
 
112
 
 
113
                        if (size < 2) return -1;
 
114
 
 
115
                        extra_len = (buffer[1] << 8) | buffer[0];
 
116
 
 
117
                        if (size < (extra_len+2)) return -1;
 
118
                        size -= (extra_len + 2);
 
119
                        buffer += (extra_len + 2);
 
120
                }
 
121
 
 
122
                if (flags & FNAME)
 
123
                {
 
124
                        while (size && *buffer)
 
125
                        {
 
126
                                --size;
 
127
                                ++buffer;
 
128
                        }
 
129
                        if (!size || *buffer) return -1;
 
130
 
 
131
                        --size;
 
132
                        ++buffer;
 
133
                }
 
134
 
 
135
                if (flags & FCOMMENT)
 
136
                {
 
137
                        while (size && *buffer)
 
138
                        {
 
139
                                --size;
 
140
                                ++buffer;
 
141
                        }
 
142
                        if (!size || *buffer) return -1;
 
143
 
 
144
                        --size;
 
145
                        ++buffer;
 
146
                }
 
147
 
 
148
                if (flags & FHCRC)
 
149
                {
 
150
                        if (size < 2) return -1;
 
151
 
 
152
                        size -= 2;
 
153
                        buffer += 2;
 
154
                }
 
155
 
 
156
                return total_size - size;
 
157
        }
 
158
 
 
159
        bool inflate_gzip(
 
160
                std::vector<char>& buffer
 
161
                , tracker_request const& req
 
162
                , request_callback* requester
 
163
                , int maximum_tracker_response_length)
 
164
        {
 
165
                TORRENT_ASSERT(maximum_tracker_response_length > 0);
 
166
 
 
167
                int header_len = gzip_header(&buffer[0], (int)buffer.size());
 
168
                if (header_len < 0)
 
169
                {
 
170
                        requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
 
171
                        return true;
 
172
                }
 
173
 
 
174
                // start off wth one kilobyte and grow
 
175
                // if needed
 
176
                std::vector<char> inflate_buffer(1024);
 
177
 
 
178
                // initialize the zlib-stream
 
179
                z_stream str;
 
180
 
 
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();
 
187
                str.zalloc = Z_NULL;
 
188
                str.zfree = Z_NULL;
 
189
                str.opaque = 0;
 
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)
 
193
                {
 
194
                        requester->tracker_request_error(req, 200, "gzip out of memory");
 
195
                        return true;
 
196
                }
 
197
 
 
198
                // inflate and grow inflate_buffer as needed
 
199
                int ret = inflate(&str, Z_SYNC_FLUSH);
 
200
                while (ret == Z_OK)
 
201
                {
 
202
                        if (str.avail_out == 0)
 
203
                        {
 
204
                                if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
 
205
                                {
 
206
                                        inflateEnd(&str);
 
207
                                        requester->tracker_request_error(req, 200
 
208
                                                , "tracker response too large");
 
209
                                        return true;
 
210
                                }
 
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();
 
214
 
 
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;
 
218
                        }
 
219
 
 
220
                        ret = inflate(&str, Z_SYNC_FLUSH);
 
221
                }
 
222
 
 
223
                inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
 
224
                inflateEnd(&str);
 
225
 
 
226
                if (ret != Z_STREAM_END)
 
227
                {
 
228
                        requester->tracker_request_error(req, 200, "gzip error");
 
229
                        return true;
 
230
                }
 
231
 
 
232
                // commit the resulting buffer
 
233
                std::swap(buffer, inflate_buffer);
 
234
                return false;
 
235
        }
 
236
 
 
237
        std::string base64encode(const std::string& s)
 
238
        {
 
239
                static const char base64_table[] =
 
240
                {
 
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', '+', '/'
 
249
                };
 
250
 
 
251
                unsigned char inbuf[3];
 
252
                unsigned char outbuf[4];
 
253
        
 
254
                std::string ret;
 
255
                for (std::string::const_iterator i = s.begin(); i != s.end();)
 
256
                {
 
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()));
 
260
 
 
261
                        // clear input buffer
 
262
                        std::fill(inbuf, inbuf+3, 0);
 
263
 
 
264
                        // read a chunk of input into inbuf
 
265
                        for (int j = 0; j < available_input; ++j)
 
266
                        {
 
267
                                inbuf[j] = *i;
 
268
                                ++i;
 
269
                        }
 
270
 
 
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;
 
276
 
 
277
                        // write output
 
278
                        for (int j = 0; j < available_input+1; ++j)
 
279
                        {
 
280
                                ret += base64_table[outbuf[j]];
 
281
                        }
 
282
 
 
283
                        // write pad
 
284
                        for (int j = 0; j < 3 - available_input; ++j)
 
285
                        {
 
286
                                ret += '=';
 
287
                        }
 
288
                }
 
289
                return ret;
 
290
        }
 
291
 
 
292
        timeout_handler::timeout_handler(asio::strand& str)
 
293
                : m_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)
 
298
                , m_read_timeout(0)
 
299
        {}
 
300
 
 
301
        void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
 
302
        {
 
303
                m_completion_timeout = completion_timeout;
 
304
                m_read_timeout = read_timeout;
 
305
                m_start_time = time_now();
 
306
                m_read_time = time_now();
 
307
 
 
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)));
 
313
        }
 
314
 
 
315
        void timeout_handler::restart_read_timeout()
 
316
        {
 
317
                m_read_time = time_now();
 
318
        }
 
319
 
 
320
        void timeout_handler::cancel()
 
321
        {
 
322
                m_completion_timeout = 0;
 
323
                m_timeout.cancel();
 
324
        }
 
325
 
 
326
        void timeout_handler::timeout_callback(asio::error_code const& error) try
 
327
        {
 
328
                if (error) return;
 
329
                if (m_completion_timeout == 0) return;
 
330
                
 
331
                ptime now(time_now());
 
332
                time_duration receive_timeout = now - m_read_time;
 
333
                time_duration completion_timeout = now - m_start_time;
 
334
                
 
335
                if (m_read_timeout
 
336
                        < total_seconds(receive_timeout)
 
337
                        || m_completion_timeout
 
338
                        < total_seconds(completion_timeout))
 
339
                {
 
340
                        on_timeout();
 
341
                        return;
 
342
                }
 
343
 
 
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)));
 
349
        }
 
350
        catch (std::exception& e)
 
351
        {
 
352
                TORRENT_ASSERT(false);
 
353
        }
 
354
 
 
355
        tracker_connection::tracker_connection(
 
356
                tracker_manager& man
 
357
                , tracker_request req
 
358
                , asio::strand& str
 
359
                , address bind_interface_
 
360
                , boost::weak_ptr<request_callback> r)
 
361
                : timeout_handler(str)
 
362
                , m_requester(r)
 
363
                , m_bind_interface(bind_interface_)
 
364
                , m_man(man)
 
365
                , m_req(req)
 
366
        {}
 
367
 
 
368
        boost::shared_ptr<request_callback> tracker_connection::requester()
 
369
        {
 
370
                return m_requester.lock();
 
371
        }
 
372
 
 
373
        void tracker_connection::fail(int code, char const* msg)
 
374
        {
 
375
                boost::shared_ptr<request_callback> cb = requester();
 
376
                if (cb) cb->tracker_request_error(m_req, code, msg);
 
377
                close();
 
378
        }
 
379
 
 
380
        void tracker_connection::fail_timeout()
 
381
        {
 
382
                boost::shared_ptr<request_callback> cb = requester();
 
383
                if (cb) cb->tracker_request_timed_out(m_req);
 
384
                close();
 
385
        }
 
386
        
 
387
        void tracker_connection::close()
 
388
        {
 
389
                cancel();
 
390
                m_man.remove_request(this);
 
391
        }
 
392
 
 
393
        void tracker_manager::remove_request(tracker_connection const* c)
 
394
        {
 
395
                mutex_t::scoped_lock l(m_mutex);
 
396
 
 
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;
 
400
 
 
401
                m_connections.erase(i);
 
402
        }
 
403
 
 
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)
 
407
        {
 
408
                std::string hostname; // hostname only
 
409
                std::string auth; // user:pass
 
410
                std::string protocol; // should be http
 
411
                int port = 80;
 
412
 
 
413
                // PARSE URL
 
414
                std::string::iterator start = url.begin();
 
415
                // remove white spaces in front of the url
 
416
                while (start != url.end() && (*start == ' ' || *start == '\t'))
 
417
                        ++start;
 
418
                std::string::iterator end
 
419
                        = std::find(url.begin(), url.end(), ':');
 
420
                protocol.assign(start, end);
 
421
 
 
422
                if (end == url.end()) throw std::runtime_error("invalid url");
 
423
                ++end;
 
424
                if (end == url.end()) throw std::runtime_error("invalid url");
 
425
                if (*end != '/') throw std::runtime_error("invalid url");
 
426
                ++end;
 
427
                if (end == url.end()) throw std::runtime_error("invalid url");
 
428
                if (*end != '/') throw std::runtime_error("invalid url");
 
429
                ++end;
 
430
                start = end;
 
431
 
 
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(), '/');
 
435
 
 
436
                if (at != url.end()
 
437
                        && colon != url.end()
 
438
                        && colon < at
 
439
                        && at < end)
 
440
                {
 
441
                        auth.assign(start, at);
 
442
                        start = at;
 
443
                        ++start;
 
444
                }
 
445
 
 
446
                std::string::iterator port_pos;
 
447
 
 
448
                // this is for IPv6 addresses
 
449
                if (start != url.end() && *start == '[')
 
450
                {
 
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(), ':');
 
454
                }
 
455
                else
 
456
                {
 
457
                        port_pos = std::find(start, url.end(), ':');
 
458
                }
 
459
 
 
460
                if (port_pos < end)
 
461
                {
 
462
                        hostname.assign(start, port_pos);
 
463
                        ++port_pos;
 
464
                        try
 
465
                        {
 
466
                                port = boost::lexical_cast<int>(std::string(port_pos, end));
 
467
                        }
 
468
                        catch(boost::bad_lexical_cast&)
 
469
                        {
 
470
                                throw std::runtime_error("invalid url: \"" + url
 
471
                                        + "\", port number expected");
 
472
                        }
 
473
                }
 
474
                else
 
475
                {
 
476
                        hostname.assign(start, end);
 
477
                }
 
478
 
 
479
                start = end;
 
480
                return make_tuple(protocol, auth, hostname, port
 
481
                        , std::string(start, url.end()));
 
482
        }
 
483
 
 
484
        void tracker_manager::queue_request(
 
485
                asio::strand& str
 
486
                , connection_queue& cc
 
487
                , tracker_request req
 
488
                , std::string const& auth
 
489
                , address bind_infc
 
490
                , boost::weak_ptr<request_callback> c)
 
491
        {
 
492
                mutex_t::scoped_lock l(m_mutex);
 
493
                TORRENT_ASSERT(req.num_want >= 0);
 
494
                if (req.event == tracker_request::stopped)
 
495
                        req.num_want = 0;
 
496
 
 
497
                TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped);
 
498
                if (m_abort && req.event != tracker_request::stopped)
 
499
                        return;
 
500
 
 
501
                try
 
502
                {
 
503
                        std::string protocol;
 
504
                        std::string hostname;
 
505
                        int port;
 
506
                        std::string request_string;
 
507
 
 
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);
 
512
 
 
513
                        boost::intrusive_ptr<tracker_connection> con;
 
514
 
 
515
                        if (protocol == "http")
 
516
                        {
 
517
                                con = new http_tracker_connection(
 
518
                                        str
 
519
                                        , cc
 
520
                                        , *this
 
521
                                        , req
 
522
                                        , hostname
 
523
                                        , port
 
524
                                        , request_string
 
525
                                        , bind_infc
 
526
                                        , c
 
527
                                        , m_settings
 
528
                                        , m_proxy
 
529
                                        , auth);
 
530
                        }
 
531
                        else if (protocol == "udp")
 
532
                        {
 
533
                                con = new udp_tracker_connection(
 
534
                                        str
 
535
                                        , *this
 
536
                                        , req
 
537
                                        , hostname
 
538
                                        , port
 
539
                                        , bind_infc
 
540
                                        , c
 
541
                                        , m_settings);
 
542
                        }
 
543
                        else
 
544
                        {
 
545
                                throw std::runtime_error("unkown protocol in tracker url");
 
546
                        }
 
547
 
 
548
                        m_connections.push_back(con);
 
549
 
 
550
                        boost::shared_ptr<request_callback> cb = con->requester();
 
551
                        if (cb) cb->m_manager = this;
 
552
                }
 
553
                catch (std::exception& e)
 
554
                {
 
555
                        if (boost::shared_ptr<request_callback> r = c.lock())
 
556
                                r->tracker_request_error(req, -1, e.what());
 
557
                }
 
558
        }
 
559
 
 
560
        void tracker_manager::abort_all_requests()
 
561
        {
 
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);
 
566
 
 
567
                m_abort = true;
 
568
                tracker_connections_t keep_connections;
 
569
 
 
570
                for (tracker_connections_t::const_iterator i =
 
571
                        m_connections.begin(); i != m_connections.end(); ++i)
 
572
                {
 
573
                        tracker_request const& req = (*i)->tracker_req();
 
574
                        if (req.event == tracker_request::stopped)
 
575
                                keep_connections.push_back(*i);
 
576
                }
 
577
 
 
578
                std::swap(m_connections, keep_connections);
 
579
        }
 
580
        
 
581
        bool tracker_manager::empty() const
 
582
        {
 
583
                mutex_t::scoped_lock l(m_mutex);
 
584
                return m_connections.empty();
 
585
        }
 
586
 
 
587
        int tracker_manager::num_requests() const
 
588
        {
 
589
                mutex_t::scoped_lock l(m_mutex);
 
590
                return m_connections.size();
 
591
        }
 
592
}