~ubuntu-branches/ubuntu/feisty/elinks/feisty-updates

« back to all changes in this revision

Viewing changes to src/protocol/bittorrent/connection.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2006-06-30 08:57:43 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060630085743-l81fgbw9dehvl1ds
Tags: 0.11.1-1ubuntu1
* Merge to Debian unstable.
* Removed gnutls12 porting, this is upstream now.
* Only Ubuntu changes left:
  - Killed type-handling.
  - Add X-Ubuntu-Gettext-Domain to .desktop files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Internal "bittorrent" protocol implementation */
 
2
 
 
3
#ifdef HAVE_CONFIG_H
 
4
#include "config.h"
 
5
#endif
 
6
 
 
7
#include <stdlib.h>
 
8
#include <string.h>
 
9
 
 
10
#include "elinks.h"
 
11
 
 
12
#include "cache/cache.h"
 
13
#include "config/options.h"
 
14
#include "main/timer.h"
 
15
#include "network/connection.h"
 
16
#include "network/progress.h"
 
17
#include "network/socket.h"
 
18
#include "protocol/bittorrent/bencoding.h"
 
19
#include "protocol/bittorrent/bittorrent.h"
 
20
#include "protocol/bittorrent/common.h"
 
21
#include "protocol/bittorrent/connection.h"
 
22
#include "protocol/bittorrent/tracker.h"
 
23
#include "protocol/bittorrent/peerconnect.h"
 
24
#include "protocol/bittorrent/peerwire.h"
 
25
#include "protocol/bittorrent/piececache.h"
 
26
#include "protocol/protocol.h"
 
27
#include "protocol/uri.h"
 
28
#include "session/download.h"
 
29
#include "util/bitfield.h"
 
30
#include "util/conv.h"
 
31
#include "util/memory.h"
 
32
#include "util/string.h"
 
33
#include "util/time.h"
 
34
 
 
35
 
 
36
/* ************************************************************************** */
 
37
/* Peer selection and connection scheduling: */
 
38
/* ************************************************************************** */
 
39
 
 
40
/* Reschedule updating of the connection state. */
 
41
static void
 
42
set_bittorrent_connection_timer(struct connection *conn)
 
43
{
 
44
        struct bittorrent_connection *bittorrent = conn->info;
 
45
        milliseconds_T interval = sec_to_ms(get_opt_int("protocol.bittorrent.choke_interval"));
 
46
 
 
47
        install_timer(&bittorrent->timer, interval,
 
48
                      (void (*)(void *)) update_bittorrent_connection_state,
 
49
                      conn);
 
50
}
 
51
 
 
52
/* Sort the peers based on the stats rate, bubbaly style! */
 
53
static void
 
54
sort_bittorrent_peer_connections(struct bittorrent_connection *bittorrent)
 
55
{
 
56
        struct bittorrent_peer_connection *peer, *prev;
 
57
 
 
58
        while (1) {
 
59
                struct bittorrent_peer_connection *next;
 
60
                int resort = 0;
 
61
 
 
62
                prev = NULL;
 
63
 
 
64
                foreachsafe (peer, next, bittorrent->peers) {
 
65
                        if (prev && prev->stats.download_rate < peer->stats.download_rate) {
 
66
                                resort = 1;
 
67
                                del_from_list(prev);
 
68
                                add_at_pos(peer, prev);
 
69
                        }
 
70
 
 
71
                        prev = peer;
 
72
                }
 
73
 
 
74
                if (!resort) break;
 
75
        };
 
76
 
 
77
#ifdef CONFIG_DEBUG
 
78
        prev = NULL;
 
79
        foreach (peer, bittorrent->peers) {
 
80
                assert(!prev || prev->stats.download_rate >= peer->stats.download_rate);
 
81
                prev = peer;
 
82
        }
 
83
#endif
 
84
}
 
85
 
 
86
/* This is basically the choke period handler. */
 
87
void
 
88
update_bittorrent_connection_state(struct connection *conn)
 
89
{
 
90
        struct bittorrent_connection *bittorrent = conn->info;
 
91
        struct bittorrent_peer_connection *peer, *next_peer;
 
92
        int peer_conns, max_peer_conns;
 
93
        int min_uploads = get_opt_int("protocol.bittorrent.min_uploads");
 
94
        int max_uploads = get_opt_int("protocol.bittorrent.max_uploads");
 
95
 
 
96
        set_bittorrent_connection_timer(conn);
 
97
        set_connection_timeout(conn);
 
98
 
 
99
        peer_conns = list_size(&bittorrent->peers);
 
100
        max_peer_conns = get_opt_int("protocol.bittorrent.peerwire.connections");
 
101
 
 
102
        /* First ``age'' the peer rates _before_ the sorting. */
 
103
        foreach (peer, bittorrent->peers)
 
104
                update_bittorrent_peer_connection_stats(peer, 0, 0, 0);
 
105
 
 
106
        /* Sort the peers so that the best peers are at the list start. */
 
107
        sort_bittorrent_peer_connections(bittorrent);
 
108
 
 
109
        /* Unchoke all the optimal peers. In good spirit, also unchoke all
 
110
         * uninterested peers until the maximum number of interested peers have
 
111
         * been unchoked. The rest is choked. */
 
112
        foreachsafe (peer, next_peer, bittorrent->peers) {
 
113
                if (!peer->remote.handshake)
 
114
                        continue;
 
115
 
 
116
                if (min_uploads < max_uploads) {
 
117
                        if (peer->remote.choked)
 
118
                                unchoke_bittorrent_peer(peer);
 
119
 
 
120
                        peer->remote.choked = 0;
 
121
 
 
122
                        /* Uninterested peers are not counted as uploads. */
 
123
                        if (peer->remote.interested)
 
124
                                max_uploads--;
 
125
 
 
126
                } else {
 
127
                        if (!peer->remote.choked)
 
128
                                choke_bittorrent_peer(peer);
 
129
 
 
130
                        peer->remote.choked = 1;
 
131
                }
 
132
 
 
133
                /* Can remove the peer so we use foreachsafe(). */
 
134
                update_bittorrent_peer_connection_state(peer);
 
135
        }
 
136
 
 
137
        /* FIXME: Find peer(s) to optimistically unchoke. */
 
138
 
 
139
        update_bittorrent_piece_cache_state(bittorrent);
 
140
 
 
141
        /* Close or open peers connections. */
 
142
        if (peer_conns > max_peer_conns) {
 
143
                struct bittorrent_peer_connection *prev;
 
144
 
 
145
                foreachsafe (peer, prev, bittorrent->peers) {
 
146
                        done_bittorrent_peer_connection(peer);
 
147
                        if (--peer_conns <= max_peer_conns)
 
148
                                break;
 
149
                }
 
150
 
 
151
        } else if (peer_conns < max_peer_conns) {
 
152
                struct bittorrent_peer *peer_info, *next_peer_info;
 
153
 
 
154
                foreachsafe (peer_info, next_peer_info, bittorrent->peer_pool) {
 
155
                        enum bittorrent_state state;
 
156
 
 
157
                        state = make_bittorrent_peer_connection(bittorrent, peer_info);
 
158
                        if (state != BITTORRENT_STATE_OK)
 
159
                                break;
 
160
 
 
161
                        del_from_list(peer_info);
 
162
                        mem_free(peer_info);
 
163
                        if (++peer_conns >= max_peer_conns)
 
164
                                break;
 
165
                }
 
166
        }
 
167
 
 
168
        assert(peer_conns <= max_peer_conns);
 
169
 
 
170
        /* Shrink the peer pool. */
 
171
        if (!list_empty(bittorrent->peers)) {
 
172
                struct bittorrent_peer *peer_info, *next_peer_info;
 
173
                int pool_size = get_opt_int("protocol.bittorrent.peerwire.pool_size");
 
174
                int pool_peers = 0;
 
175
 
 
176
                foreachsafe (peer_info, next_peer_info, bittorrent->peer_pool) {
 
177
                        /* Unlimited. */
 
178
                        if (!pool_size) break;
 
179
 
 
180
                        if (pool_peers < pool_size) {
 
181
                                pool_peers++;
 
182
                                continue;
 
183
                        }
 
184
 
 
185
                        del_from_list(peer_info);
 
186
                        mem_free(peer_info);
 
187
                }
 
188
        }
 
189
}
 
190
 
 
191
static void
 
192
update_bittorrent_connection_upload(void *data)
 
193
{
 
194
        struct bittorrent_connection *bittorrent = data;
 
195
 
 
196
        update_progress(&bittorrent->upload_progress,
 
197
                        bittorrent->uploaded,
 
198
                        bittorrent->downloaded,
 
199
                        bittorrent->uploaded);
 
200
 
 
201
}
 
202
 
 
203
void
 
204
update_bittorrent_connection_stats(struct bittorrent_connection *bittorrent,
 
205
                                   off_t downloaded, off_t uploaded,
 
206
                                   off_t received)
 
207
{
 
208
        struct bittorrent_meta *meta = &bittorrent->meta;
 
209
 
 
210
        if (bittorrent->conn->est_length == -1) {
 
211
                off_t length = (off_t) (meta->pieces - 1) * meta->piece_length
 
212
                             + meta->last_piece_length;
 
213
 
 
214
                bittorrent->conn->est_length = length;
 
215
                bittorrent->left = length;
 
216
                start_update_progress(&bittorrent->upload_progress,
 
217
                                      update_bittorrent_connection_upload,
 
218
                                      bittorrent);
 
219
        }
 
220
 
 
221
        if (bittorrent->upload_progress.timer == TIMER_ID_UNDEF
 
222
            && bittorrent->uploaded)
 
223
                update_bittorrent_connection_upload(bittorrent);
 
224
 
 
225
        bittorrent->conn->received += received;
 
226
        bittorrent->conn->from     += downloaded;
 
227
        if (downloaded > 0)
 
228
                bittorrent->downloaded     += downloaded;
 
229
        bittorrent->uploaded       += uploaded;
 
230
        bittorrent->left           -= downloaded;
 
231
 
 
232
        if (!bittorrent->downloaded) return;
 
233
 
 
234
        bittorrent->sharing_rate    = (double) bittorrent->uploaded
 
235
                                             / bittorrent->downloaded;
 
236
}
 
237
 
 
238
 
 
239
/* ************************************************************************** */
 
240
/* The ``main'' BitTorrent connection setup: */
 
241
/* ************************************************************************** */
 
242
 
 
243
/* Callback which is attached to the ELinks connection and invoked when the
 
244
 * connection is shutdown. */
 
245
static void
 
246
done_bittorrent_connection(struct connection *conn)
 
247
{
 
248
        struct bittorrent_connection *bittorrent = conn->info;
 
249
        struct bittorrent_peer_connection *peer, *next;
 
250
 
 
251
        assert(bittorrent);
 
252
 
 
253
        /* We don't want the tracker to see the fetch. */
 
254
        if (bittorrent->fetch)
 
255
                done_bittorrent_fetch(&bittorrent->fetch);
 
256
 
 
257
        foreachsafe (peer, next, bittorrent->peers)
 
258
                done_bittorrent_peer_connection(peer);
 
259
 
 
260
        done_bittorrent_tracker_connection(conn);
 
261
        done_bittorrent_listening_socket(conn);
 
262
        if (bittorrent->cache)
 
263
                done_bittorrent_piece_cache(bittorrent);
 
264
        done_bittorrent_meta(&bittorrent->meta);
 
265
 
 
266
        kill_timer(&bittorrent->timer);
 
267
        kill_timer(&bittorrent->upload_progress.timer);
 
268
 
 
269
        free_list(bittorrent->peer_pool);
 
270
 
 
271
        mem_free_set(&conn->info, NULL);
 
272
}
 
273
 
 
274
static struct bittorrent_connection *
 
275
init_bittorrent_connection(struct connection *conn)
 
276
{
 
277
        struct bittorrent_connection *bittorrent;
 
278
 
 
279
        bittorrent = mem_calloc(1, sizeof(*bittorrent));
 
280
        if (!bittorrent) return NULL;
 
281
 
 
282
        init_list(bittorrent->peers);
 
283
        init_list(bittorrent->peer_pool);
 
284
 
 
285
        conn->info = bittorrent;
 
286
        conn->done = done_bittorrent_connection;
 
287
 
 
288
        init_bittorrent_peer_id(bittorrent->peer_id);
 
289
 
 
290
        bittorrent->conn = conn;
 
291
        bittorrent->tracker.timer = TIMER_ID_UNDEF;
 
292
 
 
293
        return bittorrent;
 
294
}
 
295
 
 
296
void
 
297
bittorrent_resume_callback(struct bittorrent_connection *bittorrent)
 
298
{
 
299
        enum connection_state state;
 
300
 
 
301
        /* Failing to create the listening socket is fatal. */
 
302
        state = init_bittorrent_listening_socket(bittorrent->conn);
 
303
        if (state != S_OK) {
 
304
                retry_connection(bittorrent->conn, state);
 
305
                return;
 
306
        }
 
307
 
 
308
        set_connection_state(bittorrent->conn, S_CONN_TRACKER);
 
309
        send_bittorrent_tracker_request(bittorrent->conn);
 
310
}
 
311
 
 
312
/* Metainfo file download callback */
 
313
static void
 
314
bittorrent_metainfo_callback(void *data, enum connection_state state,
 
315
                             struct string *response)
 
316
{
 
317
        struct connection *conn = data;
 
318
        struct bittorrent_connection *bittorrent = conn->info;
 
319
 
 
320
        bittorrent->fetch = NULL;
 
321
 
 
322
        if (state != S_OK) {
 
323
                abort_connection(conn, state);
 
324
                return;
 
325
        }
 
326
 
 
327
        switch (parse_bittorrent_metafile(&bittorrent->meta, response)) {
 
328
        case BITTORRENT_STATE_OK:
 
329
        {
 
330
                size_t size = list_size(&bittorrent->meta.files);
 
331
                int *selection;
 
332
 
 
333
                assert(bittorrent->tracker.event == BITTORRENT_EVENT_STARTED);
 
334
 
 
335
                selection = get_bittorrent_selection(conn->uri, size);
 
336
                if (selection) {
 
337
                        struct bittorrent_file *file;
 
338
                        int index = 0;
 
339
 
 
340
                        foreach (file, bittorrent->meta.files)
 
341
                                file->selected = selection[index++];
 
342
 
 
343
                        mem_free(selection);
 
344
                }
 
345
 
 
346
                switch (init_bittorrent_piece_cache(bittorrent, response)) {
 
347
                case BITTORRENT_STATE_OK:
 
348
                        bittorrent_resume_callback(bittorrent);
 
349
                        return;
 
350
 
 
351
                case BITTORRENT_STATE_CACHE_RESUME:
 
352
                        set_connection_state(bittorrent->conn, S_RESUME);
 
353
                        return;
 
354
 
 
355
                case BITTORRENT_STATE_OUT_OF_MEM:
 
356
                        state = S_OUT_OF_MEM;
 
357
                        break;
 
358
 
 
359
                default:
 
360
                        state = S_BITTORRENT_ERROR;
 
361
                }
 
362
 
 
363
                break;
 
364
        }
 
365
        case BITTORRENT_STATE_OUT_OF_MEM:
 
366
                state = S_OUT_OF_MEM;
 
367
                break;
 
368
 
 
369
        case BITTORRENT_STATE_ERROR:
 
370
        default:
 
371
                /* XXX: This can also happen when passing bittorrent:<uri> and
 
372
                 * <uri> gives an HTTP 404 response. It might be worth fixing by
 
373
                 * looking at the protocol header, however, direct usage of the
 
374
                 * internal bittorrent: is at your own risk ... at least for
 
375
                 * now. --jonas */
 
376
                state = S_BITTORRENT_METAINFO;
 
377
        }
 
378
 
 
379
        abort_connection(conn, state);
 
380
}
 
381
 
 
382
/* The entry point for BitTorrent downloads. */
 
383
void
 
384
bittorrent_protocol_handler(struct connection *conn)
 
385
{
 
386
        struct uri *uri;
 
387
        struct bittorrent_connection *bittorrent;
 
388
 
 
389
        bittorrent = init_bittorrent_connection(conn);
 
390
        if (!bittorrent) {
 
391
                abort_connection(conn, S_OUT_OF_MEM);
 
392
                return;
 
393
        }
 
394
 
 
395
        assert(conn->uri->datalen);
 
396
 
 
397
        uri = get_uri(conn->uri->data, 0);
 
398
        if (!uri) {
 
399
                abort_connection(conn, S_OUT_OF_MEM);
 
400
                return;
 
401
        }
 
402
 
 
403
        set_connection_state(conn, S_CONN);
 
404
        set_connection_timeout(conn);
 
405
        conn->from = 0;
 
406
 
 
407
        init_bittorrent_fetch(&bittorrent->fetch, uri,
 
408
                              bittorrent_metainfo_callback, conn, 0);
 
409
        done_uri(uri);
 
410
}