~cmiller/ubuntu/quantal/deluge/fix-parameter-move-storage

« back to all changes in this revision

Viewing changes to libtorrent/src/http_tracker_connection.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Cristian Greco
  • Date: 2009-11-13 02:39:45 UTC
  • mfrom: (4.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091113023945-te1bybo2912ejzuc
Tags: 1.2.0~rc3-4
* debian/control: bump build-dep on python-setuptools to (>= 0.6c9).
* debian/patches:
  - 25_r5921_fastresume_files.patch
    new, should fix problems with fresh configs;
  - 30_r5931_ipc_lockfile.patch:
    new, should fix an issue where Deluge will fail to start if there is a
    stale ipc lockfile. (Closes: #555849)

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
 
#include <algorithm>
41
 
 
42
 
#include "libtorrent/config.hpp"
43
 
#include "libtorrent/gzip.hpp"
44
 
 
45
 
#ifdef _MSC_VER
46
 
#pragma warning(push, 1)
47
 
#endif
48
 
 
49
 
#include <boost/bind.hpp>
50
 
 
51
 
#ifdef _MSC_VER
52
 
#pragma warning(pop)
53
 
#endif
54
 
 
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
 
 
64
 
using namespace libtorrent;
65
 
using boost::bind;
66
 
 
67
 
namespace libtorrent
68
 
{
69
 
        
70
 
        http_tracker_connection::http_tracker_connection(
71
 
                io_service& ios
72
 
                , connection_queue& cc
73
 
                , tracker_manager& man
74
 
                , tracker_request const& req
75
 
                , address bind_infc
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)
81
 
                , m_man(man)
82
 
                , m_settings(stn)
83
 
                , m_bind_iface(bind_infc)
84
 
                , m_ps(ps)
85
 
                , m_cc(cc)
86
 
                , m_ios(ios)
87
 
        {}
88
 
 
89
 
        void http_tracker_connection::start()
90
 
        {
91
 
                // TODO: authentication
92
 
                std::string url = tracker_req().url;
93
 
 
94
 
                if (tracker_req().kind == tracker_request::scrape_request)
95
 
                {
96
 
                        // find and replace "announce" with "scrape"
97
 
                        // in request
98
 
 
99
 
                        std::size_t pos = url.find("announce");
100
 
                        if (pos == std::string::npos)
101
 
                        {
102
 
                                fail(-1, ("scrape is not available on url: '"
103
 
                                        + tracker_req().url +"'").c_str());
104
 
                                return;
105
 
                        }
106
 
                        url.replace(pos, 8, "scrape");
107
 
                }
108
 
                
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)
114
 
                        url += "&";
115
 
                else
116
 
                        url += "?";
117
 
 
118
 
                url += "info_hash=";
119
 
                url += escape_string(
120
 
                        reinterpret_cast<const char*>(tracker_req().info_hash.begin()), 20);
121
 
                
122
 
                if (tracker_req().kind == tracker_request::announce_request)
123
 
                {
124
 
                        url += "&peer_id=";
125
 
                        url += escape_string(
126
 
                                reinterpret_cast<const char*>(tracker_req().pid.begin()), 20);
127
 
 
128
 
                        url += "&port=";
129
 
                        url += to_string(tracker_req().listen_port).elems;
130
 
 
131
 
                        url += "&uploaded=";
132
 
                        url += to_string(tracker_req().uploaded).elems;
133
 
 
134
 
                        url += "&downloaded=";
135
 
                        url += to_string(tracker_req().downloaded).elems;
136
 
 
137
 
                        url += "&left=";
138
 
                        url += to_string(tracker_req().left).elems;
139
 
 
140
 
                        if (tracker_req().event != tracker_request::none)
141
 
                        {
142
 
                                const char* event_string[] = {"completed", "started", "stopped"};
143
 
                                url += "&event=";
144
 
                                url += event_string[tracker_req().event - 1];
145
 
                        }
146
 
 
147
 
                        url += "&key=";
148
 
                        std::stringstream key_string;
149
 
                        key_string << std::hex << tracker_req().key;
150
 
                        url += key_string.str();
151
 
 
152
 
                        url += "&compact=1";
153
 
 
154
 
                        url += "&numwant=";
155
 
                        url += to_string((std::min)(tracker_req().num_want, 999)).elems;
156
 
 
157
 
                        if (m_settings.announce_ip != address())
158
 
                        {
159
 
                                error_code ec;
160
 
                                std::string ip = m_settings.announce_ip.to_string(ec);
161
 
                                if (!ec) url += "&ip=" + ip;
162
 
                        }
163
 
 
164
 
#ifndef TORRENT_DISABLE_ENCRYPTION
165
 
                        url += "&supportcrypto=1";
166
 
#endif
167
 
                        if (!tracker_req().ipv6.empty())
168
 
                        {
169
 
                                url += "&ipv6=";
170
 
                                url += tracker_req().ipv6;
171
 
                        }
172
 
 
173
 
                        if (!tracker_req().ipv4.empty())
174
 
                        {
175
 
                                url += "&ipv4=";
176
 
                                url += tracker_req().ipv4;
177
 
                        }
178
 
 
179
 
                        // extension that tells the tracker that
180
 
                        // we don't need any peer_id's in the response
181
 
                        url += "&no_peer_id=1";
182
 
                }
183
 
 
184
 
                m_tracker_connection.reset(new http_connection(m_ios, m_cc
185
 
                        , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)));
186
 
 
187
 
                int timeout = tracker_req().event==tracker_request::stopped
188
 
                        ?m_settings.stop_tracker_timeout
189
 
                        :m_settings.tracker_completion_timeout;
190
 
 
191
 
                m_tracker_connection->get(url, seconds(timeout)
192
 
                        , 1, &m_ps, 5, m_settings.user_agent, m_bind_iface);
193
 
 
194
 
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
195
 
 
196
 
                boost::shared_ptr<request_callback> cb = requester();
197
 
                if (cb)
198
 
                {
199
 
                        cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
200
 
                }
201
 
#endif
202
 
        }
203
 
 
204
 
        void http_tracker_connection::close()
205
 
        {
206
 
                if (m_tracker_connection)
207
 
                {
208
 
                        m_tracker_connection->close();
209
 
                        m_tracker_connection.reset();
210
 
                }
211
 
                tracker_connection::close();
212
 
        }
213
 
 
214
 
        void http_tracker_connection::on_response(error_code const& ec
215
 
                , http_parser const& parser, char const* data, int size)
216
 
        {
217
 
                // keep this alive
218
 
                boost::intrusive_ptr<http_tracker_connection> me(this);
219
 
 
220
 
                if (ec && ec != asio::error::eof)
221
 
                {
222
 
                        fail(-1, ec.message().c_str());
223
 
                        return;
224
 
                }
225
 
                
226
 
                if (!parser.header_finished())
227
 
                {
228
 
                        fail(-1, "premature end of file");
229
 
                        return;
230
 
                }
231
 
 
232
 
                if (parser.status_code() != 200)
233
 
                {
234
 
                        fail(parser.status_code(), parser.message().c_str());
235
 
                        return;
236
 
                }
237
 
        
238
 
                if (ec && ec != asio::error::eof)
239
 
                {
240
 
                        fail(parser.status_code(), ec.message().c_str());
241
 
                        return;
242
 
                }
243
 
                
244
 
                // handle tracker response
245
 
                entry e;
246
 
                e = bdecode(data, data + size);
247
 
 
248
 
                if (e.type() == entry::dictionary_t)
249
 
                {
250
 
                        parse(parser.status_code(), e);
251
 
                }
252
 
                else
253
 
                {
254
 
                        std::string error_str("invalid bencoding of tracker response: \"");
255
 
                        for (char const* i = data, *end(data + size); i != end; ++i)
256
 
                        {
257
 
                                if (*i >= ' ' && *i <= '~') error_str += *i;
258
 
                                else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
259
 
                        }
260
 
                        error_str += "\"";
261
 
                        fail(parser.status_code(), error_str.c_str());
262
 
                }
263
 
                close();
264
 
        }
265
 
 
266
 
        bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret)
267
 
        {
268
 
                // extract peer id (if any)
269
 
                if (info.type() != entry::dictionary_t)
270
 
                {
271
 
                        fail(-1, "invalid response from tracker (invalid peer entry)");
272
 
                        return false;
273
 
                }
274
 
                entry const* i = info.find_key("peer id");
275
 
                if (i != 0)
276
 
                {
277
 
                        if (i->type() != entry::string_t || i->string().length() != 20)
278
 
                        {
279
 
                                fail(-1, "invalid response from tracker (invalid peer id)");
280
 
                                return false;
281
 
                        }
282
 
                        std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
283
 
                }
284
 
                else
285
 
                {
286
 
                        // if there's no peer_id, just initialize it to a bunch of zeroes
287
 
                        std::fill_n(ret.pid.begin(), 20, 0);
288
 
                }
289
 
 
290
 
                // extract ip
291
 
                i = info.find_key("ip");
292
 
                if (i == 0 || i->type() != entry::string_t)
293
 
                {
294
 
                        fail(-1, "invalid response from tracker");
295
 
                        return false;
296
 
                }
297
 
                ret.ip = i->string();
298
 
 
299
 
                // extract port
300
 
                i = info.find_key("port");
301
 
                if (i == 0 || i->type() != entry::int_t)
302
 
                {
303
 
                        fail(-1, "invalid response from tracker");
304
 
                        return false;
305
 
                }
306
 
                ret.port = (unsigned short)i->integer();
307
 
 
308
 
                return true;
309
 
        }
310
 
 
311
 
        void http_tracker_connection::parse(int status_code, entry const& e)
312
 
        {
313
 
                boost::shared_ptr<request_callback> cb = requester();
314
 
                if (!cb) return;
315
 
 
316
 
                // parse the response
317
 
                entry const* failure = e.find_key("failure reason");
318
 
                if (failure && failure->type() == entry::string_t)
319
 
                {
320
 
                        fail(status_code, failure->string().c_str());
321
 
                        return;
322
 
                }
323
 
 
324
 
                entry const* warning = e.find_key("warning message");
325
 
                if (warning && warning->type() == entry::string_t)
326
 
                {
327
 
                        cb->tracker_warning(tracker_req(), warning->string());
328
 
                }
329
 
 
330
 
                std::vector<peer_entry> peer_list;
331
 
 
332
 
                if (tracker_req().kind == tracker_request::scrape_request)
333
 
                {
334
 
                        std::string ih = tracker_req().info_hash.to_string();
335
 
 
336
 
                        entry const* files = e.find_key("files");
337
 
                        if (files == 0 || files->type() != entry::dictionary_t)
338
 
                        {
339
 
                                fail(-1, "invalid or missing 'files' entry in scrape response");
340
 
                                return;
341
 
                        }
342
 
 
343
 
                        entry const* scrape_data = files->find_key(ih);
344
 
                        if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t)
345
 
                        {
346
 
                                fail(-1, "missing or invalid info-hash entry in scrape response");
347
 
                                return;
348
 
                        }
349
 
                        entry const* complete = scrape_data->find_key("complete");
350
 
                        entry const* incomplete = scrape_data->find_key("incomplete");
351
 
                        entry const* downloaded = scrape_data->find_key("downloaded");
352
 
                        if (complete == 0 || incomplete == 0 || downloaded == 0
353
 
                                || complete->type() != entry::int_t
354
 
                                || incomplete->type() != entry::int_t
355
 
                                || downloaded->type() != entry::int_t)
356
 
                        {
357
 
                                fail(-1, "missing 'complete' or 'incomplete' entries in scrape response");
358
 
                                return;
359
 
                        }
360
 
                        cb->tracker_scrape_response(tracker_req(), int(complete->integer())
361
 
                                , int(incomplete->integer()), int(downloaded->integer()));
362
 
                        return;
363
 
                }
364
 
 
365
 
                entry const* interval = e.find_key("interval");
366
 
                if (interval == 0 || interval->type() != entry::int_t)
367
 
                {
368
 
                        fail(-1, "missing or invalid 'interval' entry in tracker response");
369
 
                        return;
370
 
                }
371
 
 
372
 
                entry const* peers_ent = e.find_key("peers");
373
 
                if (peers_ent && peers_ent->type() == entry::string_t)
374
 
                {
375
 
                        std::string const& peers = peers_ent->string();
376
 
                        for (std::string::const_iterator i = peers.begin();
377
 
                                i != peers.end();)
378
 
                        {
379
 
                                if (std::distance(i, peers.end()) < 6) break;
380
 
 
381
 
                                peer_entry p;
382
 
                                p.pid.clear();
383
 
                                error_code ec;
384
 
                                p.ip = detail::read_v4_address(i).to_string(ec);
385
 
                                if (ec) continue;
386
 
                                p.port = detail::read_uint16(i);
387
 
                                peer_list.push_back(p);
388
 
                        }
389
 
                }
390
 
                else if (peers_ent && peers_ent->type() == entry::list_t)
391
 
                {
392
 
                        entry::list_type const& l = peers_ent->list();
393
 
                        for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
394
 
                        {
395
 
                                peer_entry p;
396
 
                                if (!extract_peer_info(*i, p)) return;
397
 
                                peer_list.push_back(p);
398
 
                        }
399
 
                }
400
 
                else
401
 
                {
402
 
                        peers_ent = 0;
403
 
                }
404
 
 
405
 
                entry const* ipv6_peers = e.find_key("peers6");
406
 
                if (ipv6_peers && ipv6_peers->type() == entry::string_t)
407
 
                {
408
 
                        std::string const& peers = ipv6_peers->string();
409
 
                        for (std::string::const_iterator i = peers.begin();
410
 
                                i != peers.end();)
411
 
                        {
412
 
                                if (std::distance(i, peers.end()) < 18) break;
413
 
 
414
 
                                peer_entry p;
415
 
                                p.pid.clear();
416
 
                                error_code ec;
417
 
                                p.ip = detail::read_v6_address(i).to_string(ec);
418
 
                                if (ec) continue;
419
 
                                p.port = detail::read_uint16(i);
420
 
                                peer_list.push_back(p);
421
 
                        }
422
 
                }
423
 
                else
424
 
                {
425
 
                        ipv6_peers = 0;
426
 
                }
427
 
 
428
 
                if (peers_ent == 0 && ipv6_peers == 0)
429
 
                {
430
 
                        fail(-1, "missing 'peers' and 'peers6' entry in tracker response");
431
 
                        return;
432
 
                }
433
 
 
434
 
 
435
 
                // look for optional scrape info
436
 
                int complete = -1;
437
 
                int incomplete = -1;
438
 
                address external_ip;
439
 
 
440
 
                entry const* ip_ent = e.find_key("external ip");
441
 
                if (ip_ent && ip_ent->type() == entry::string_t)
442
 
                {
443
 
                        std::string const& ip = ip_ent->string();
444
 
                        char const* p = &ip[0];
445
 
                        if (ip.size() == address_v4::bytes_type::static_size)
446
 
                                external_ip = detail::read_v4_address(p);
447
 
                        else if (ip.size() == address_v6::bytes_type::static_size)
448
 
                                external_ip = detail::read_v6_address(p);
449
 
                }
450
 
                
451
 
                entry const* complete_ent = e.find_key("complete");
452
 
                if (complete_ent && complete_ent->type() == entry::int_t)
453
 
                        complete = int(complete_ent->integer());
454
 
 
455
 
                entry const* incomplete_ent = e.find_key("incomplete");
456
 
                if (incomplete_ent && incomplete_ent->type() == entry::int_t)
457
 
                        incomplete = int(incomplete_ent->integer());
458
 
 
459
 
                cb->tracker_response(tracker_req(), peer_list, interval->integer(), complete
460
 
                        , incomplete, external_ip);
461
 
        }
462
 
 
463
 
}
464