Retry only for https protocol
[elinks.git] / src / protocol / bittorrent / common.c
blob24ab7555dfcf85043b4360c088a5849f7fd55df4
1 /* Library of common BitTorrent code */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
8 #include <string.h>
10 #include "elinks.h"
12 #include "main/select.h"
13 #include "network/connection.h"
14 #include "protocol/bittorrent/common.h"
15 #include "session/download.h"
16 #include "util/conv.h"
17 #include "util/error.h"
18 #include "util/lists.h"
19 #include "util/memory.h"
20 #include "util/random.h"
21 #include "util/sha1.h"
22 #include "util/string.h"
23 #include "util/snprintf.h"
25 const bittorrent_id_T BITTORRENT_NULL_ID;
27 /* Debug function which returns printable peer ID. */
28 unsigned char *
29 get_peer_id(bittorrent_id_T peer_id)
31 static unsigned char hex[41];
32 int i, j;
34 if (bittorrent_id_is_empty(peer_id)) {
35 snprintf(hex, sizeof(hex), "unknown id %p", peer_id);
36 return hex;
39 for (i = 0, j = 0; i < sizeof(bittorrent_id_T); i++, j++) {
40 unsigned char value = peer_id[i];
42 if (isprint(value)) {
43 hex[j] = value;
44 } else {
45 hex[j++] = hx(value >> 4 & 0xF);
46 hex[j] = hx(value & 0xF);
50 hex[j] = 0;
52 return hex;
55 unsigned char *
56 get_peer_message(enum bittorrent_message_id message_id)
58 static struct {
59 enum bittorrent_message_id message_id;
60 unsigned char *name;
61 } messages[] = {
62 { BITTORRENT_MESSAGE_INCOMPLETE, "incomplete" },
63 { BITTORRENT_MESSAGE_KEEP_ALIVE, "keep-alive" },
64 { BITTORRENT_MESSAGE_CHOKE, "choke" },
65 { BITTORRENT_MESSAGE_UNCHOKE, "unchoke" },
66 { BITTORRENT_MESSAGE_INTERESTED, "interested" },
67 { BITTORRENT_MESSAGE_NOT_INTERESTED, "not-interested" },
68 { BITTORRENT_MESSAGE_HAVE, "have" },
69 { BITTORRENT_MESSAGE_BITFIELD, "bitfield" },
70 { BITTORRENT_MESSAGE_REQUEST, "request" },
71 { BITTORRENT_MESSAGE_PIECE, "piece" },
72 { BITTORRENT_MESSAGE_CANCEL, "cancel" },
74 { 0, NULL },
76 int i;
78 for (i = 0; messages[i].name; i++)
79 if (messages[i].message_id == message_id)
80 return messages[i].name;
82 return "unknown";
86 unsigned char *
87 get_hexed_bittorrent_id(bittorrent_id_T id)
89 static unsigned char hex[SHA_DIGEST_LENGTH * 2 + 1];
90 int i;
92 for (i = 0; i < sizeof(bittorrent_id_T); i++) {
93 int j = i * 2;
95 hex[j++] = hx(id[i] >> 4 & 0xF);
96 hex[j] = hx(id[i] & 0xF);
99 hex[SHA_DIGEST_LENGTH * 2] = 0;
101 return hex;
105 bittorrent_piece_is_valid(struct bittorrent_meta *meta,
106 uint32_t piece, unsigned char *data, uint32_t datalen)
108 unsigned char *piece_hash;
109 bittorrent_id_T data_hash;
111 assert(piece < meta->pieces);
113 SHA1(data, datalen, data_hash);
114 piece_hash = &meta->piece_hash[piece * SHA_DIGEST_LENGTH];
116 return !memcmp(data_hash, piece_hash, SHA_DIGEST_LENGTH);
119 void
120 done_bittorrent_meta(struct bittorrent_meta *meta)
122 free_uri_list(&meta->tracker_uris);
123 mem_free_if(meta->name);
124 mem_free_if(meta->comment);
125 mem_free_if(meta->piece_hash);
126 free_list(meta->files);
129 void
130 done_bittorrent_message(struct bittorrent_message *message)
132 del_from_list(message);
133 done_uri(message->uri);
134 mem_free(message);
138 /* ************************************************************************** */
139 /* Peer information management: */
140 /* ************************************************************************** */
142 /* Generate a peer ID with of the form: 'E' <version> '-' <random> */
143 void
144 init_bittorrent_peer_id(bittorrent_id_T peer_id)
146 unsigned char *version = VERSION;
147 int dots = 0;
148 int i = 0;
150 srand(time(NULL));
152 peer_id[i++] = 'E';
153 peer_id[i++] = 'L';
155 for (; *version && i < sizeof(bittorrent_id_T); version++) {
156 if (isdigit(*version)) {
157 peer_id[i++] = *version;
159 } else if (*version == '.' && !dots) {
160 dots = 1;
162 } else {
163 peer_id[i++] = '-';
164 break;
167 peer_id[i++] = *version;
170 /* Hmm, sizeof(peer_id) don't work here. */
171 random_nonce(peer_id + i, sizeof(bittorrent_id_T) - i);
172 while (i < sizeof(bittorrent_id_T)) {
173 peer_id[i] = hx(peer_id[i] & 0xF);
174 i++;
179 bittorrent_id_is_known(struct bittorrent_connection *bittorrent,
180 bittorrent_id_T id)
182 struct bittorrent_peer_connection *peer;
184 /* The peer ID matches the client ID? */
185 if (!memcmp(bittorrent->peer_id, id, sizeof(bittorrent->peer_id)))
186 return 1;
188 foreach (peer, bittorrent->peers)
189 if (!memcmp(peer->id, id, sizeof(peer->id)))
190 return 1;
192 if (get_peer_from_bittorrent_pool(bittorrent, id))
193 return 1;
195 return 0;
198 struct bittorrent_peer *
199 get_peer_from_bittorrent_pool(struct bittorrent_connection *bittorrent,
200 bittorrent_id_T id)
202 struct bittorrent_peer *peer_info;
204 foreach (peer_info, bittorrent->peer_pool)
205 if (!memcmp(peer_info->id, id, sizeof(peer_info->id)))
206 return peer_info;
208 return NULL;
211 enum bittorrent_state
212 add_peer_to_bittorrent_pool(struct bittorrent_connection *bittorrent,
213 bittorrent_id_T id, int port,
214 const unsigned char *ip, int iplen)
216 struct bittorrent_peer *peer;
218 /* Check sanity. Don't error out here since entries in the tracker
219 * responses can contain garbage and we don't want to bring down the
220 * whole connection for that. */
221 if (iplen <= 0 || !uri_port_is_valid(port))
222 return BITTORRENT_STATE_OK;
224 /* The ID can be NULL for the compact format. */
225 if (id) {
226 enum bittorrent_blacklist_flags flags;
228 if (bittorrent_id_is_empty(id))
229 return BITTORRENT_STATE_ERROR;
231 /* Check if the peer is already known. */
232 if (bittorrent_id_is_known(bittorrent, id))
233 return BITTORRENT_STATE_OK;
235 flags = get_bittorrent_blacklist_flags(id);
236 if (flags != BITTORRENT_BLACKLIST_NONE)
237 return BITTORRENT_STATE_OK;
240 /* Really add the peer. */
242 peer = mem_calloc(1, sizeof(*peer) + iplen);
243 if (!peer) return BITTORRENT_STATE_OUT_OF_MEM;
245 peer->port = port;
246 if (iplen) memcpy(peer->ip, ip, iplen);
247 if (id) memcpy(peer->id, id, sizeof(peer->id));
249 add_to_list(bittorrent->peer_pool, peer);
251 return BITTORRENT_STATE_OK;
255 /* ************************************************************************** */
256 /* Peer request management: */
257 /* ************************************************************************** */
259 struct bittorrent_peer_request *
260 get_bittorrent_peer_request(struct bittorrent_peer_status *status,
261 uint32_t piece, uint32_t offset, uint32_t length)
263 struct bittorrent_peer_request *request;
265 foreach (request, status->requests) {
266 if (request->piece == piece
267 && request->offset == offset
268 && request->length == length)
269 return request;
272 return NULL;
275 void
276 add_bittorrent_peer_request(struct bittorrent_peer_status *status,
277 uint32_t piece, uint32_t offset, uint32_t length)
279 struct bittorrent_peer_request *request;
281 request = get_bittorrent_peer_request(status, piece, offset, length);
282 if (request) return;
284 request = mem_alloc(sizeof(*request));
285 if (!request) return;
287 request->piece = piece;
288 request->offset = offset;
289 request->length = length;
291 /* FIXME: Rather insert the request so that we atleast try to get
292 * some sort of sequential access to piece data. */
293 add_to_list_end(status->requests, request);
296 void
297 del_bittorrent_peer_request(struct bittorrent_peer_status *status,
298 uint32_t piece, uint32_t offset, uint32_t length)
300 struct bittorrent_peer_request *request;
302 request = get_bittorrent_peer_request(status, piece, offset, length);
303 if (!request) return;
305 del_from_list(request);
306 mem_free(request);
310 /* ************************************************************************** */
311 /* Generic URI downloader: */
312 /* ************************************************************************** */
314 struct bittorrent_fetcher {
315 struct bittorrent_fetcher **ref;
316 bittorrent_fetch_callback_T callback;
317 void *data;
318 int redirects;
319 unsigned int delete_:1;
320 struct download download;
323 /* This part of the code is very lowlevel and ELinks specific. The download
324 * callback is called each time there is some progress, such as new data. So
325 * first it basically checks the state and tries to handle it accordingly.
326 * Redirects are also handled here. */
327 static void
328 bittorrent_fetch_callback(struct download *download, void *data)
330 struct bittorrent_fetcher *fetcher = data;
331 struct fragment *fragment;
332 struct bittorrent_const_string response;
333 struct cache_entry *cached = download->cached;
335 /* If the callback was removed we should shutdown ASAP. */
336 if (!fetcher->callback || is_in_state(download->state, S_INTERRUPTED)) {
337 if (is_in_state(download->state, S_INTERRUPTED))
338 mem_free(fetcher);
339 return;
342 if (is_in_result_state(download->state)
343 && !is_in_state(download->state, S_OK)) {
344 fetcher->callback(fetcher->data, download->state, NULL);
345 if (fetcher->ref)
346 *fetcher->ref = NULL;
347 mem_free(fetcher);
348 return;
351 if (!cached || is_in_queued_state(download->state))
352 return;
354 if (cached->redirect && fetcher->redirects++ < MAX_REDIRECTS) {
355 cancel_download(download, 0);
357 download->state = connection_state(S_WAIT_REDIR);
359 load_uri(cached->redirect, cached->uri, download,
360 PRI_DOWNLOAD, CACHE_MODE_NORMAL,
361 download->progress ? download->progress->start : 0);
363 return;
366 if (is_in_progress_state(download->state))
367 return;
369 assert(is_in_state(download->state, S_OK));
371 /* If the entry is chunked defragment it and grab the single, remaining
372 * fragment. */
373 fragment = get_cache_fragment(cached);
374 if (!fragment) {
375 fetcher->callback(fetcher->data, connection_state(S_OUT_OF_MEM), NULL);
376 if (fetcher->ref)
377 *fetcher->ref = NULL;
378 mem_free(fetcher);
379 return;
382 response.source = fragment->data;
383 response.length = fragment->length;
385 fetcher->callback(fetcher->data, connection_state(S_OK), &response);
387 if (fetcher->delete_)
388 delete_cache_entry(cached);
389 if (fetcher->ref)
390 *fetcher->ref = NULL;
391 mem_free(fetcher);
394 struct bittorrent_fetcher *
395 init_bittorrent_fetch(struct bittorrent_fetcher **fetcher_ref,
396 struct uri *uri, bittorrent_fetch_callback_T callback,
397 void *data, int delete_)
399 struct bittorrent_fetcher *fetcher;
401 fetcher = mem_calloc(1, sizeof(*fetcher));
402 if (!fetcher) {
403 callback(data, connection_state(S_OUT_OF_MEM), NULL);
404 return NULL;
407 if (fetcher_ref)
408 *fetcher_ref = fetcher;
410 fetcher->ref = fetcher_ref;
411 fetcher->callback = callback;
412 fetcher->data = data;
413 fetcher->delete_ = delete_;
414 fetcher->download.callback = bittorrent_fetch_callback;
415 fetcher->download.data = fetcher;
417 load_uri(uri, NULL, &fetcher->download, PRI_MAIN, CACHE_MODE_NORMAL, -1);
419 return fetcher;
422 static void
423 end_bittorrent_fetch(void *fetcher_data)
425 struct bittorrent_fetcher *fetcher = fetcher_data;
427 assert(fetcher && !fetcher->callback);
429 /* Stop any running connections. */
430 cancel_download(&fetcher->download, 0);
432 mem_free(fetcher);
435 void
436 done_bittorrent_fetch(struct bittorrent_fetcher **fetcher_ref)
438 struct bittorrent_fetcher *fetcher;
440 assert(fetcher_ref);
442 fetcher = *fetcher_ref;
443 *fetcher_ref = NULL;
445 assert(fetcher);
447 if (fetcher->ref)
448 *fetcher->ref = NULL;
450 /* Nuke the callback so nothing gets called */
451 fetcher->callback = NULL;
452 register_bottom_half(end_bittorrent_fetch, fetcher);
456 /* ************************************************************************** */
457 /* Blacklist management: */
458 /* ************************************************************************** */
460 struct bittorrent_blacklist_item {
461 LIST_HEAD(struct bittorrent_blacklist_item);
463 enum bittorrent_blacklist_flags flags;
464 bittorrent_id_T id;
467 static INIT_LIST_OF(struct bittorrent_blacklist_item, bittorrent_blacklist);
470 static struct bittorrent_blacklist_item *
471 get_bittorrent_blacklist_item(bittorrent_id_T peer_id)
473 struct bittorrent_blacklist_item *item;
475 foreach (item, bittorrent_blacklist)
476 if (!memcmp(item->id, peer_id, sizeof(bittorrent_id_T)))
477 return item;
479 return NULL;
482 void
483 add_bittorrent_blacklist_flags(bittorrent_id_T peer_id,
484 enum bittorrent_blacklist_flags flags)
486 struct bittorrent_blacklist_item *item;
488 item = get_bittorrent_blacklist_item(peer_id);
489 if (item) {
490 item->flags |= flags;
491 return;
494 item = mem_calloc(1, sizeof(*item));
495 if (!item) return;
497 item->flags = flags;
498 memcpy(item->id, peer_id, sizeof(bittorrent_id_T));
500 add_to_list(bittorrent_blacklist, item);
503 void
504 del_bittorrent_blacklist_flags(bittorrent_id_T peer_id,
505 enum bittorrent_blacklist_flags flags)
507 struct bittorrent_blacklist_item *item;
509 item = get_bittorrent_blacklist_item(peer_id);
510 if (!item) return;
512 item->flags &= ~flags;
513 if (item->flags) return;
515 del_from_list(item);
516 mem_free(item);
519 enum bittorrent_blacklist_flags
520 get_bittorrent_blacklist_flags(bittorrent_id_T peer_id)
522 struct bittorrent_blacklist_item *item;
524 item = get_bittorrent_blacklist_item(peer_id);
526 return item ? item->flags : BITTORRENT_BLACKLIST_NONE;
529 void
530 done_bittorrent_blacklist(void)
532 free_list(bittorrent_blacklist);