Bug 1013: Don't assume errno is between 0 and 100000
[elinks.git] / src / protocol / bittorrent / common.c
blobeaeb5a1e273fd44652dc47a3ee3c2021760788df
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/sha1.h"
21 #include "util/string.h"
22 #include "util/snprintf.h"
24 const bittorrent_id_T BITTORRENT_NULL_ID;
26 /* Debug function which returns printable peer ID. */
27 unsigned char *
28 get_peer_id(bittorrent_id_T peer_id)
30 static unsigned char hex[41];
31 int i, j;
33 if (bittorrent_id_is_empty(peer_id)) {
34 snprintf(hex, sizeof(hex), "unknown id %p", peer_id);
35 return hex;
38 for (i = 0, j = 0; i < sizeof(bittorrent_id_T); i++, j++) {
39 unsigned char value = peer_id[i];
41 if (isprint(value)) {
42 hex[j] = value;
43 } else {
44 hex[j++] = hx(value >> 4 & 0xF);
45 hex[j] = hx(value & 0xF);
49 hex[j] = 0;
51 return hex;
54 unsigned char *
55 get_peer_message(enum bittorrent_message_id message_id)
57 static struct {
58 enum bittorrent_message_id message_id;
59 unsigned char *name;
60 } messages[] = {
61 { BITTORRENT_MESSAGE_INCOMPLETE, "incomplete" },
62 { BITTORRENT_MESSAGE_KEEP_ALIVE, "keep-alive" },
63 { BITTORRENT_MESSAGE_CHOKE, "choke" },
64 { BITTORRENT_MESSAGE_UNCHOKE, "unchoke" },
65 { BITTORRENT_MESSAGE_INTERESTED, "interested" },
66 { BITTORRENT_MESSAGE_NOT_INTERESTED, "not-interested" },
67 { BITTORRENT_MESSAGE_HAVE, "have" },
68 { BITTORRENT_MESSAGE_BITFIELD, "bitfield" },
69 { BITTORRENT_MESSAGE_REQUEST, "request" },
70 { BITTORRENT_MESSAGE_PIECE, "piece" },
71 { BITTORRENT_MESSAGE_CANCEL, "cancel" },
73 { 0, NULL },
75 int i;
77 for (i = 0; messages[i].name; i++)
78 if (messages[i].message_id == message_id)
79 return messages[i].name;
81 return "unknown";
85 unsigned char *
86 get_hexed_bittorrent_id(bittorrent_id_T id)
88 static unsigned char hex[SHA_DIGEST_LENGTH * 2 + 1];
89 int i;
91 for (i = 0; i < sizeof(bittorrent_id_T); i++) {
92 int j = i * 2;
94 hex[j++] = hx(id[i] >> 4 & 0xF);
95 hex[j] = hx(id[i] & 0xF);
98 hex[SHA_DIGEST_LENGTH * 2] = 0;
100 return hex;
104 bittorrent_piece_is_valid(struct bittorrent_meta *meta,
105 uint32_t piece, unsigned char *data, uint32_t datalen)
107 unsigned char *piece_hash;
108 bittorrent_id_T data_hash;
110 assert(piece < meta->pieces);
112 SHA1(data, datalen, data_hash);
113 piece_hash = &meta->piece_hash[piece * SHA_DIGEST_LENGTH];
115 return !memcmp(data_hash, piece_hash, SHA_DIGEST_LENGTH);
118 void
119 done_bittorrent_meta(struct bittorrent_meta *meta)
121 free_uri_list(&meta->tracker_uris);
122 mem_free_if(meta->name);
123 mem_free_if(meta->comment);
124 mem_free_if(meta->piece_hash);
125 free_list(meta->files);
128 void
129 done_bittorrent_message(struct bittorrent_message *message)
131 del_from_list(message);
132 done_uri(message->uri);
133 mem_free(message);
137 /* ************************************************************************** */
138 /* Peer information management: */
139 /* ************************************************************************** */
141 /* Generate a peer ID with of the form: 'E' <version> '-' <random> */
142 void
143 init_bittorrent_peer_id(bittorrent_id_T peer_id)
145 unsigned char *version = VERSION;
146 int dots = 0;
147 int i = 0;
149 srand(time(NULL));
151 peer_id[i++] = 'E';
152 peer_id[i++] = 'L';
154 for (; *version && i < sizeof(bittorrent_id_T); version++) {
155 if (isdigit(*version)) {
156 peer_id[i++] = *version;
158 } else if (*version == '.' && !dots) {
159 dots = 1;
161 } else {
162 peer_id[i++] = '-';
163 break;
166 peer_id[i++] = *version;
169 /* Hmm, sizeof(peer_id) don't work here. */
170 while (i < sizeof(bittorrent_id_T)) {
171 int random = rand();
173 while (i < sizeof(bittorrent_id_T) && (random & 0xF)) {
174 peer_id[i++] = hx(random & 0xF);
175 random >>= 4;
181 bittorrent_id_is_known(struct bittorrent_connection *bittorrent,
182 bittorrent_id_T id)
184 struct bittorrent_peer_connection *peer;
186 /* The peer ID matches the client ID? */
187 if (!memcmp(bittorrent->peer_id, id, sizeof(bittorrent->peer_id)))
188 return 1;
190 foreach (peer, bittorrent->peers)
191 if (!memcmp(peer->id, id, sizeof(peer->id)))
192 return 1;
194 if (get_peer_from_bittorrent_pool(bittorrent, id))
195 return 1;
197 return 0;
200 struct bittorrent_peer *
201 get_peer_from_bittorrent_pool(struct bittorrent_connection *bittorrent,
202 bittorrent_id_T id)
204 struct bittorrent_peer *peer_info;
206 foreach (peer_info, bittorrent->peer_pool)
207 if (!memcmp(peer_info->id, id, sizeof(peer_info->id)))
208 return peer_info;
210 return NULL;
213 enum bittorrent_state
214 add_peer_to_bittorrent_pool(struct bittorrent_connection *bittorrent,
215 bittorrent_id_T id, int port,
216 const unsigned char *ip, int iplen)
218 struct bittorrent_peer *peer;
220 /* Check sanity. Don't error out here since entries in the tracker
221 * responses can contain garbage and we don't want to bring down the
222 * whole connection for that. */
223 if (iplen <= 0 || !uri_port_is_valid(port))
224 return BITTORRENT_STATE_OK;
226 /* The ID can be NULL for the compact format. */
227 if (id) {
228 enum bittorrent_blacklist_flags flags;
230 if (bittorrent_id_is_empty(id))
231 return BITTORRENT_STATE_ERROR;
233 /* Check if the peer is already known. */
234 if (bittorrent_id_is_known(bittorrent, id))
235 return BITTORRENT_STATE_OK;
237 flags = get_bittorrent_blacklist_flags(id);
238 if (flags != BITTORRENT_BLACKLIST_NONE)
239 return BITTORRENT_STATE_OK;
242 /* Really add the peer. */
244 peer = mem_calloc(1, sizeof(*peer) + iplen);
245 if (!peer) return BITTORRENT_STATE_OUT_OF_MEM;
247 peer->port = port;
248 if (iplen) memcpy(peer->ip, ip, iplen);
249 if (id) memcpy(peer->id, id, sizeof(peer->id));
251 add_to_list(bittorrent->peer_pool, peer);
253 return BITTORRENT_STATE_OK;
257 /* ************************************************************************** */
258 /* Peer request management: */
259 /* ************************************************************************** */
261 struct bittorrent_peer_request *
262 get_bittorrent_peer_request(struct bittorrent_peer_status *status,
263 uint32_t piece, uint32_t offset, uint32_t length)
265 struct bittorrent_peer_request *request;
267 foreach (request, status->requests) {
268 if (request->piece == piece
269 && request->offset == offset
270 && request->length == length)
271 return request;
274 return NULL;
277 void
278 add_bittorrent_peer_request(struct bittorrent_peer_status *status,
279 uint32_t piece, uint32_t offset, uint32_t length)
281 struct bittorrent_peer_request *request;
283 request = get_bittorrent_peer_request(status, piece, offset, length);
284 if (request) return;
286 request = mem_alloc(sizeof(*request));
287 if (!request) return;
289 request->piece = piece;
290 request->offset = offset;
291 request->length = length;
293 /* FIXME: Rather insert the request so that we atleast try to get
294 * some sort of sequential access to piece data. */
295 add_to_list_end(status->requests, request);
298 void
299 del_bittorrent_peer_request(struct bittorrent_peer_status *status,
300 uint32_t piece, uint32_t offset, uint32_t length)
302 struct bittorrent_peer_request *request;
304 request = get_bittorrent_peer_request(status, piece, offset, length);
305 if (!request) return;
307 del_from_list(request);
308 mem_free(request);
312 /* ************************************************************************** */
313 /* Generic URI downloader: */
314 /* ************************************************************************** */
316 struct bittorrent_fetcher {
317 struct bittorrent_fetcher **ref;
318 bittorrent_fetch_callback_T callback;
319 void *data;
320 int redirects;
321 unsigned int delete:1;
322 struct download download;
325 /* This part of the code is very lowlevel and ELinks specific. The download
326 * callback is called each time there is some progress, such as new data. So
327 * first it basically checks the state and tries to handle it accordingly.
328 * Redirects are also handled here. */
329 static void
330 bittorrent_fetch_callback(struct download *download, void *data)
332 struct bittorrent_fetcher *fetcher = data;
333 struct fragment *fragment;
334 struct string response;
335 struct cache_entry *cached = download->cached;
337 /* If the callback was removed we should shutdown ASAP. */
338 if (!fetcher->callback || is_in_state(download->state, S_INTERRUPTED)) {
339 if (is_in_state(download->state, S_INTERRUPTED))
340 mem_free(fetcher);
341 return;
344 if (is_in_result_state(download->state)
345 && !is_in_state(download->state, S_OK)) {
346 fetcher->callback(fetcher->data, download->state, NULL);
347 if (fetcher->ref)
348 *fetcher->ref = NULL;
349 mem_free(fetcher);
350 return;
353 if (!cached || is_in_queued_state(download->state))
354 return;
356 if (cached->redirect && fetcher->redirects++ < MAX_REDIRECTS) {
357 cancel_download(download, 0);
359 download->state = connection_state(S_WAIT_REDIR);
361 load_uri(cached->redirect, cached->uri, download,
362 PRI_DOWNLOAD, CACHE_MODE_NORMAL,
363 download->progress ? download->progress->start : 0);
365 return;
368 if (is_in_progress_state(download->state))
369 return;
371 assert(is_in_state(download->state, S_OK));
373 /* If the entry is chunked defragment it and grab the single, remaining
374 * fragment. */
375 fragment = get_cache_fragment(cached);
376 if (!fragment) {
377 fetcher->callback(fetcher->data, connection_state(S_OUT_OF_MEM), NULL);
378 if (fetcher->ref)
379 *fetcher->ref = NULL;
380 mem_free(fetcher);
381 return;
384 response.source = fragment->data;
385 response.length = fragment->length;
387 fetcher->callback(fetcher->data, connection_state(S_OK), &response);
389 if (fetcher->delete)
390 delete_cache_entry(cached);
391 if (fetcher->ref)
392 *fetcher->ref = NULL;
393 mem_free(fetcher);
396 struct bittorrent_fetcher *
397 init_bittorrent_fetch(struct bittorrent_fetcher **fetcher_ref,
398 struct uri *uri, bittorrent_fetch_callback_T callback,
399 void *data, int delete)
401 struct bittorrent_fetcher *fetcher;
403 fetcher = mem_calloc(1, sizeof(*fetcher));
404 if (!fetcher) {
405 callback(data, connection_state(S_OUT_OF_MEM), NULL);
406 return NULL;
409 if (fetcher_ref)
410 *fetcher_ref = fetcher;
412 fetcher->ref = fetcher_ref;
413 fetcher->callback = callback;
414 fetcher->data = data;
415 fetcher->delete = delete;
416 fetcher->download.callback = bittorrent_fetch_callback;
417 fetcher->download.data = fetcher;
419 load_uri(uri, NULL, &fetcher->download, PRI_MAIN, CACHE_MODE_NORMAL, -1);
421 return fetcher;
424 static void
425 end_bittorrent_fetch(void *fetcher_data)
427 struct bittorrent_fetcher *fetcher = fetcher_data;
429 assert(fetcher && !fetcher->callback);
431 /* Stop any running connections. */
432 cancel_download(&fetcher->download, 0);
434 mem_free(fetcher);
437 void
438 done_bittorrent_fetch(struct bittorrent_fetcher **fetcher_ref)
440 struct bittorrent_fetcher *fetcher;
442 assert(fetcher_ref);
444 fetcher = *fetcher_ref;
445 *fetcher_ref = NULL;
447 assert(fetcher);
449 if (fetcher->ref)
450 *fetcher->ref = NULL;
452 /* Nuke the callback so nothing gets called */
453 fetcher->callback = NULL;
454 register_bottom_half(end_bittorrent_fetch, fetcher);
458 /* ************************************************************************** */
459 /* Blacklist management: */
460 /* ************************************************************************** */
462 struct bittorrent_blacklist_item {
463 LIST_HEAD(struct bittorrent_blacklist_item);
465 enum bittorrent_blacklist_flags flags;
466 bittorrent_id_T id;
469 static INIT_LIST_OF(struct bittorrent_blacklist_item, bittorrent_blacklist);
472 static struct bittorrent_blacklist_item *
473 get_bittorrent_blacklist_item(bittorrent_id_T peer_id)
475 struct bittorrent_blacklist_item *item;
477 foreach (item, bittorrent_blacklist)
478 if (!memcmp(item->id, peer_id, sizeof(bittorrent_id_T)))
479 return item;
481 return NULL;
484 void
485 add_bittorrent_blacklist_flags(bittorrent_id_T peer_id,
486 enum bittorrent_blacklist_flags flags)
488 struct bittorrent_blacklist_item *item;
490 item = get_bittorrent_blacklist_item(peer_id);
491 if (item) {
492 item->flags |= flags;
493 return;
496 item = mem_calloc(1, sizeof(*item));
497 if (!item) return;
499 item->flags = flags;
500 memcpy(item->id, peer_id, sizeof(bittorrent_id_T));
502 add_to_list(bittorrent_blacklist, item);
505 void
506 del_bittorrent_blacklist_flags(bittorrent_id_T peer_id,
507 enum bittorrent_blacklist_flags flags)
509 struct bittorrent_blacklist_item *item;
511 item = get_bittorrent_blacklist_item(peer_id);
512 if (!item) return;
514 item->flags &= ~flags;
515 if (item->flags) return;
517 del_from_list(item);
518 mem_free(item);
521 enum bittorrent_blacklist_flags
522 get_bittorrent_blacklist_flags(bittorrent_id_T peer_id)
524 struct bittorrent_blacklist_item *item;
526 item = get_bittorrent_blacklist_item(peer_id);
528 return item ? item->flags : BITTORRENT_BLACKLIST_NONE;
531 void
532 done_bittorrent_blacklist(void)
534 free_list(bittorrent_blacklist);