1 /* Library of common BitTorrent code */
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. */
28 get_peer_id(bittorrent_id_T peer_id
)
30 static unsigned char hex
[41];
33 if (bittorrent_id_is_empty(peer_id
)) {
34 snprintf(hex
, sizeof(hex
), "unknown id %p", peer_id
);
38 for (i
= 0, j
= 0; i
< sizeof(bittorrent_id_T
); i
++, j
++) {
39 unsigned char value
= peer_id
[i
];
44 hex
[j
++] = hx(value
>> 4 & 0xF);
45 hex
[j
] = hx(value
& 0xF);
55 get_peer_message(enum bittorrent_message_id message_id
)
58 enum bittorrent_message_id message_id
;
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" },
77 for (i
= 0; messages
[i
].name
; i
++)
78 if (messages
[i
].message_id
== message_id
)
79 return messages
[i
].name
;
86 get_hexed_bittorrent_id(bittorrent_id_T id
)
88 static unsigned char hex
[SHA_DIGEST_LENGTH
* 2 + 1];
91 for (i
= 0; i
< sizeof(bittorrent_id_T
); i
++) {
94 hex
[j
++] = hx(id
[i
] >> 4 & 0xF);
95 hex
[j
] = hx(id
[i
] & 0xF);
98 hex
[SHA_DIGEST_LENGTH
* 2] = 0;
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
);
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
);
129 done_bittorrent_message(struct bittorrent_message
*message
)
131 del_from_list(message
);
132 done_uri(message
->uri
);
137 /* ************************************************************************** */
138 /* Peer information management: */
139 /* ************************************************************************** */
141 /* Generate a peer ID with of the form: 'E' <version> '-' <random> */
143 init_bittorrent_peer_id(bittorrent_id_T peer_id
)
145 unsigned char *version
= VERSION
;
154 for (; *version
&& i
< sizeof(bittorrent_id_T
); version
++) {
155 if (isdigit(*version
)) {
156 peer_id
[i
++] = *version
;
158 } else if (*version
== '.' && !dots
) {
166 peer_id
[i
++] = *version
;
169 /* Hmm, sizeof(peer_id) don't work here. */
170 while (i
< sizeof(bittorrent_id_T
)) {
173 while (i
< sizeof(bittorrent_id_T
) && (random
& 0xF)) {
174 peer_id
[i
++] = hx(random
& 0xF);
181 bittorrent_id_is_known(struct bittorrent_connection
*bittorrent
,
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
)))
190 foreach (peer
, bittorrent
->peers
)
191 if (!memcmp(peer
->id
, id
, sizeof(peer
->id
)))
194 if (get_peer_from_bittorrent_pool(bittorrent
, id
))
200 struct bittorrent_peer
*
201 get_peer_from_bittorrent_pool(struct bittorrent_connection
*bittorrent
,
204 struct bittorrent_peer
*peer_info
;
206 foreach (peer_info
, bittorrent
->peer_pool
)
207 if (!memcmp(peer_info
->id
, id
, sizeof(peer_info
->id
)))
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. */
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
;
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
)
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
);
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
);
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
);
312 /* ************************************************************************** */
313 /* Generic URI downloader: */
314 /* ************************************************************************** */
316 struct bittorrent_fetcher
{
317 struct bittorrent_fetcher
**ref
;
318 bittorrent_fetch_callback_T callback
;
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. */
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
))
344 if (is_in_result_state(download
->state
)
345 && !is_in_state(download
->state
, S_OK
)) {
346 fetcher
->callback(fetcher
->data
, download
->state
, NULL
);
348 *fetcher
->ref
= NULL
;
353 if (!cached
|| is_in_queued_state(download
->state
))
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);
368 if (is_in_progress_state(download
->state
))
371 assert(is_in_state(download
->state
, S_OK
));
373 /* If the entry is chunked defragment it and grab the single, remaining
375 fragment
= get_cache_fragment(cached
);
377 fetcher
->callback(fetcher
->data
, connection_state(S_OUT_OF_MEM
), NULL
);
379 *fetcher
->ref
= NULL
;
384 response
.source
= fragment
->data
;
385 response
.length
= fragment
->length
;
387 fetcher
->callback(fetcher
->data
, connection_state(S_OK
), &response
);
390 delete_cache_entry(cached
);
392 *fetcher
->ref
= NULL
;
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
));
405 callback(data
, connection_state(S_OUT_OF_MEM
), NULL
);
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);
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);
438 done_bittorrent_fetch(struct bittorrent_fetcher
**fetcher_ref
)
440 struct bittorrent_fetcher
*fetcher
;
444 fetcher
= *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
;
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
)))
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
);
492 item
->flags
|= flags
;
496 item
= mem_calloc(1, sizeof(*item
));
500 memcpy(item
->id
, peer_id
, sizeof(bittorrent_id_T
));
502 add_to_list(bittorrent_blacklist
, item
);
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
);
514 item
->flags
&= ~flags
;
515 if (item
->flags
) return;
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
;
532 done_bittorrent_blacklist(void)
534 free_list(bittorrent_blacklist
);