File Coverage

_torrent.c
Criterion Covered Total %
statement 120 217 55.3
branch 37 120 30.8
condition n/a
subroutine n/a
pod n/a
total 157 337 46.5


line stmt bran cond sub pod time code
1             /* torrent.c - create BitTorrent files and calculate BitTorrent InfoHash (BTIH).
2             *
3             * Copyright (c) 2010, Aleksey Kravchenko
4             *
5             * Permission to use, copy, modify, and/or distribute this software for any
6             * purpose with or without fee is hereby granted.
7             *
8             * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9             * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10             * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11             * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12             * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13             * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14             * PERFORMANCE OF THIS SOFTWARE.
15             */
16              
17             #include
18             #include
19             #include
20             #include
21             #include /* time() */
22              
23             #include "byte_order.h"
24             #include "algorithms.h"
25             #include "hex.h"
26             #include "torrent.h"
27              
28             #ifdef USE_OPENSSL
29             #define SHA1_INIT(ctx) ((pinit_t)ctx->sha_init)(&ctx->sha1_context)
30             #define SHA1_UPDATE(ctx, msg, size) ((pupdate_t)ctx->sha_update)(&ctx->sha1_context, (msg), (size))
31             #define SHA1_FINAL(ctx, result) ((pfinal_t)ctx->sha_final)(&ctx->sha1_context, (result))
32             #else
33             #define SHA1_INIT(ctx) rhash_sha1_init(&ctx->sha1_context)
34             #define SHA1_UPDATE(ctx, msg, size) rhash_sha1_update(&ctx->sha1_context, (msg), (size))
35             #define SHA1_FINAL(ctx, result) rhash_sha1_final(&ctx->sha1_context, (result))
36             #endif
37              
38             #define BT_MIN_HASH_LENGTH 16384
39             /** size of a SHA1 hash in bytes */
40             #define BT_HASH_SIZE 20
41             /** number of SHA1 hashes to store together in one block */
42             #define BT_BLOCK_SIZE 256
43              
44             /**
45             * Initialize torrent context before calculating hash.
46             *
47             * @param ctx context to initialize
48             */
49 2           void bt_init(torrent_ctx* ctx)
50             {
51 2           memset(ctx, 0, sizeof(torrent_ctx));
52 2           ctx->piece_length = BT_MIN_HASH_LENGTH;
53 2 50         assert(BT_MIN_HASH_LENGTH == bt_default_piece_length(0));
54              
55             #ifdef USE_OPENSSL
56             {
57             /* get the methods of the selected SHA1 algorithm */
58             rhash_hash_info* sha1_info = &rhash_info_table[3];
59             assert(sha1_info->info->hash_id == RHASH_SHA1);
60             assert(sha1_info->context_size <= (sizeof(sha1_ctx) + sizeof(unsigned long)));
61             ctx->sha_init = sha1_info->init;
62             ctx->sha_update = sha1_info->update;
63             ctx->sha_final = sha1_info->final;
64             }
65             #endif
66              
67 2           SHA1_INIT(ctx);
68 2           }
69              
70             /**
71             * Free memory allocated by properties of torrent_vect structure.
72             *
73             * @param vect vector to clean
74             */
75 3           static void bt_vector_clean(torrent_vect* vect)
76             {
77             size_t i;
78 4 100         for (i = 0; i < vect->size; i++) {
79 1           free(vect->array[i]);
80             }
81 3           free(vect->array);
82 3           }
83              
84             /**
85             * Clean up torrent context by freeing all dynamically
86             * allocated memory.
87             *
88             * @param ctx torrent algorithm context
89             */
90 1           void bt_cleanup(torrent_ctx* ctx)
91             {
92 1 50         assert(ctx != NULL);
93              
94             /* destroy arrays */
95 1           bt_vector_clean(&ctx->hash_blocks);
96 1           bt_vector_clean(&ctx->files);
97 1           bt_vector_clean(&ctx->announce);
98              
99 1           free(ctx->program_name);
100 1           free(ctx->content.str);
101 1           ctx->program_name = 0;
102 1           ctx->content.str = 0;
103 1           }
104              
105             static void bt_generate_torrent(torrent_ctx* ctx);
106              
107             /**
108             * Add an item to vector.
109             *
110             * @param vect vector to add item to
111             * @param item the item to add
112             * @return non-zero on success, zero on fail
113             */
114 2           static int bt_vector_add_ptr(torrent_vect* vect, void* item)
115             {
116             /* check if vector contains enough space for the next item */
117 2 50         if (vect->size >= vect->allocated) {
118 2 50         size_t size = (vect->allocated == 0 ? 128 : vect->allocated * 2);
119 2           void* new_array = realloc(vect->array, size * sizeof(void*));
120 2 50         if (new_array == NULL) return 0; /* failed: no memory */
121 2           vect->array = (void**)new_array;
122 2           vect->allocated = size;
123             }
124             /* add new item to the vector */
125 2           vect->array[vect->size] = item;
126 2           vect->size++;
127 2           return 1;
128             }
129              
130             /**
131             * Store a SHA1 hash of a processed file piece.
132             *
133             * @param ctx torrent algorithm context
134             * @return non-zero on success, zero on fail
135             */
136 2           static int bt_store_piece_sha1(torrent_ctx* ctx)
137             {
138             unsigned char* block;
139             unsigned char* hash;
140              
141 2 50         if ((ctx->piece_count % BT_BLOCK_SIZE) == 0) {
142 2           block = (unsigned char*)malloc(BT_HASH_SIZE * BT_BLOCK_SIZE);
143 2 50         if (block == NULL || !bt_vector_add_ptr(&ctx->hash_blocks, block)) {
    50          
144 0 0         if (block) free(block);
145 0           return 0;
146             }
147             } else {
148 0           block = (unsigned char*)(ctx->hash_blocks.array[ctx->piece_count / BT_BLOCK_SIZE]);
149             }
150              
151 2           hash = &block[BT_HASH_SIZE * (ctx->piece_count % BT_BLOCK_SIZE)];
152 2           SHA1_FINAL(ctx, hash); /* write the hash */
153 2           ctx->piece_count++;
154 2           return 1;
155             }
156              
157             /**
158             * A filepath and filesize information.
159             */
160             typedef struct bt_file_info
161             {
162             uint64_t size;
163             char path[1];
164             } bt_file_info;
165              
166             /**
167             * Add a file info into the batch of files of given torrent.
168             *
169             * @param ctx torrent algorithm context
170             * @param path file path
171             * @param filesize file size
172             * @return non-zero on success, zero on fail
173             */
174 0           int bt_add_file(torrent_ctx* ctx, const char* path, uint64_t filesize)
175             {
176 0           size_t len = strlen(path);
177 0           bt_file_info* info = (bt_file_info*)malloc(sizeof(uint64_t) + len + 1);
178 0 0         if (info == NULL) {
179 0           ctx->error = 1;
180 0           return 0;
181             }
182              
183 0           info->size = filesize;
184 0           memcpy(info->path, path, len + 1);
185 0 0         if (!bt_vector_add_ptr(&ctx->files, info)) {
186 0           free(info);
187 0           return 0;
188             }
189              
190             /* recalculate piece length (but only if hashing not started yet) */
191 0 0         if (ctx->piece_count == 0 && ctx->index == 0) {
    0          
192             /* note: in case of batch of files should use a total batch size */
193 0           ctx->piece_length = bt_default_piece_length(filesize);
194             }
195 0           return 1;
196             }
197              
198             /**
199             * Calculate message hash.
200             * Can be called repeatedly with chunks of the message to be hashed.
201             *
202             * @param ctx the algorithm context containing current hashing state
203             * @param msg message chunk
204             * @param size length of the message chunk
205             */
206 2           void bt_update(torrent_ctx* ctx, const void* msg, size_t size)
207             {
208 2           const unsigned char* pmsg = (const unsigned char*)msg;
209 2           size_t rest = (size_t)(ctx->piece_length - ctx->index);
210 2 50         assert(ctx->index < ctx->piece_length);
211              
212 2 50         while (size > 0) {
213 2           size_t left = (size < rest ? size : rest);
214 2           SHA1_UPDATE(ctx, pmsg, left);
215 2 50         if (size < rest) {
216 2           ctx->index += left;
217 2           break;
218             }
219 0           bt_store_piece_sha1(ctx);
220 0           SHA1_INIT(ctx);
221 0           ctx->index = 0;
222              
223 0           pmsg += rest;
224 0           size -= rest;
225 0           rest = ctx->piece_length;
226             }
227 2           }
228              
229             /**
230             * Finalize hashing and optionally store calculated hash into the given array.
231             * If the result parameter is NULL, the hash is not stored, but it is
232             * accessible by bt_get_btih().
233             *
234             * @param ctx the algorithm context containing current hashing state
235             * @param result pointer to the array store message hash into
236             */
237 2           void bt_final(torrent_ctx* ctx, unsigned char result[20])
238             {
239 2 50         if (ctx->index > 0) {
240 2           bt_store_piece_sha1(ctx); /* flush buffered data */
241             }
242              
243 2           bt_generate_torrent(ctx);
244 2 50         if (result) memcpy(result, ctx->btih, btih_hash_size);
245 2           }
246              
247             /* BitTorrent functions */
248              
249             /**
250             * Grow, if needed, the torrent_str buffer to ensure it contains
251             * at least (length + 1) characters.
252             *
253             * @param ctx the torrent algorithm context
254             * @param length length of the string, the allocated buffer must contain
255             * @return 1 on success, 0 on error
256             */
257 20           static int bt_str_ensure_length(torrent_ctx* ctx, size_t length)
258             {
259             char* new_str;
260 20 100         if (length >= ctx->content.allocated && !ctx->error) {
    50          
261 4           length++; /* allocate one character more */
262 4 100         if (length < 64) length = 64;
263 2           else length = (length + 255) & ~255;
264 4           new_str = (char*)realloc(ctx->content.str, length);
265 4 50         if (new_str == NULL) {
266 0           ctx->error = 1;
267 0           ctx->content.allocated = 0;
268 0           return 0;
269             }
270 4           ctx->content.str = new_str;
271 4           ctx->content.allocated = length;
272             }
273 20           return 1;
274             }
275              
276             /**
277             * Append a null-terminated string to the string string buffer.
278             *
279             * @param ctx the torrent algorithm context
280             * @param text the null-terminated string to append
281             */
282 14           static void bt_str_append(torrent_ctx* ctx, const char* text)
283             {
284 14           size_t length = strlen(text);
285              
286 14 50         if (!bt_str_ensure_length(ctx, ctx->content.length + length)) return;
287 14 50         assert(ctx->content.str != 0);
288 14           memcpy(ctx->content.str + ctx->content.length, text, length);
289 14           ctx->content.length += length;
290 14           ctx->content.str[ctx->content.length] = '\0';
291             }
292              
293             /**
294             * B-encode given integer.
295             *
296             * @param ctx the torrent algorithm context
297             * @param number the integer to output
298             */
299 4           static void bt_bencode_int(torrent_ctx* ctx, const char* name, uint64_t number)
300             {
301             char* p;
302 4 50         if (name) bt_str_append(ctx, name);
303              
304             /* add up to 20 digits and 2 letters */
305 4 50         if (!bt_str_ensure_length(ctx, ctx->content.length + 22)) return;
306 4           p = ctx->content.str + ctx->content.length;
307 4           *(p++) = 'i';
308 4           p += rhash_sprintI64(p, number);
309 4           *(p++) = 'e';
310 4           *p = '\0'; /* terminate string with \0 */
311              
312 4           ctx->content.length = (p - ctx->content.str);
313             }
314              
315             /**
316             * B-encode a string.
317             *
318             * @param ctx the torrent algorithm context
319             * @param str the string to encode
320             */
321 0           static void bt_bencode_str(torrent_ctx* ctx, const char* name, const char* str)
322             {
323 0           size_t len = strlen(str);
324             int num_len;
325             char* p;
326              
327 0 0         if (name) bt_str_append(ctx, name);
328 0 0         if (!bt_str_ensure_length(ctx, ctx->content.length + len + 21)) return;
329              
330 0           p = ctx->content.str + ctx->content.length;
331 0           p += (num_len = rhash_sprintI64(p, len));
332 0           ctx->content.length += len + num_len + 1;
333              
334 0           *(p++) = ':';
335 0           memcpy(p, str, len + 1); /* copy with trailing '\0' */
336             }
337              
338             /**
339             * B-encode array of SHA1 hashes of file pieces.
340             *
341             * @param ctx pointer to the torrent structure containing SHA1 hashes
342             */
343 2           static void bt_bencode_pieces(torrent_ctx* ctx)
344             {
345 2           size_t pieces_length = ctx->piece_count * BT_HASH_SIZE;
346             int num_len;
347             int size, i;
348             char* p;
349              
350 2 50         if (!bt_str_ensure_length(ctx, ctx->content.length + pieces_length + 21))
351 0           return;
352              
353 2           p = ctx->content.str + ctx->content.length;
354 2           p += (num_len = rhash_sprintI64(p, pieces_length));
355 2           ctx->content.length += pieces_length + num_len + 1;
356              
357 2           *(p++) = ':';
358 2           p[pieces_length] = '\0'; /* terminate with \0 just in case */
359              
360 4 100         for (size = (int)ctx->piece_count, i = 0; size > 0;
361 2           size -= BT_BLOCK_SIZE, i++)
362             {
363 2           memcpy(p, ctx->hash_blocks.array[i],
364 2           (size < BT_BLOCK_SIZE ? size : BT_BLOCK_SIZE) * BT_HASH_SIZE);
365 2           p += BT_BLOCK_SIZE * BT_HASH_SIZE;
366             }
367             }
368              
369             /**
370             * Calculate default torrent piece length, using uTorrent algorithm.
371             * Algorithm:
372             * length = 16K for total_size < 16M,
373             * length = 8M for total_size >= 4G,
374             * length = top_bit(total_size) / 1024 otherwise.
375             *
376             * @param total_size total hashed batch size of torrent file
377             * @return piece length used by torrent file
378             */
379 2           size_t bt_default_piece_length(uint64_t total_size)
380             {
381             uint64_t hi_bit;
382 2 50         if (total_size < 16777216) return BT_MIN_HASH_LENGTH;
383 0 0         if (total_size >= I64(4294967296) ) return 8388608;
384 0 0         for (hi_bit = 16777216 << 1; hi_bit <= total_size; hi_bit <<= 1);
385 0           return (size_t)(hi_bit >> 10);
386             }
387              
388             /* get file basename */
389 0           static const char* bt_get_basename(const char* path)
390             {
391 0           const char* p = strchr(path, '\0') - 1;
392 0 0         for (; p >= path && *p != '/' && *p != '\\'; p--);
    0          
    0          
393 0           return (p + 1);
394             }
395              
396             /* extract batchname from the path, modifies the path buffer */
397 0           static const char* get_batch_name(char* path)
398             {
399 0           char* p = (char*)bt_get_basename(path) - 1;
400 0 0         for (; p > path && (*p == '/' || *p == '\\'); p--) *p = 0;
    0          
    0          
401 0 0         if (p <= path) return "BATCH_DIR";
402 0           return bt_get_basename(path);
403             }
404              
405             /* write file size and path */
406 0           static void bt_file_info_append(torrent_ctx* ctx, const char* length_name,
407             const char* path_name, bt_file_info* info)
408             {
409 0           bt_bencode_int(ctx, length_name, info->size);
410             /* store the file basename */
411 0           bt_bencode_str(ctx, path_name, bt_get_basename(info->path));
412 0           }
413              
414             /**
415             * Generate torrent file content
416             * @see http://wiki.theory.org/BitTorrentSpecification
417             *
418             * @param ctx the torrent algorithm context
419             */
420 2           static void bt_generate_torrent(torrent_ctx* ctx)
421             {
422 2           uint64_t total_size = 0;
423             size_t info_start_pos;
424              
425 2 50         assert(ctx->content.str == NULL);
426              
427 2 50         if (ctx->piece_length == 0) {
428 0 0         if (ctx->files.size == 1) {
429 0           total_size = ((bt_file_info*)ctx->files.array[0])->size;
430             }
431 0           ctx->piece_length = bt_default_piece_length(total_size);
432             }
433              
434 2 50         if ((ctx->options & BT_OPT_INFOHASH_ONLY) == 0) {
435             /* write the torrent header */
436 2           bt_str_append(ctx, "d");
437 2 50         if (ctx->announce.array && ctx->announce.size > 0) {
    0          
438 0           bt_bencode_str(ctx, "8:announce", ctx->announce.array[0]);
439              
440             /* if more than one announce url */
441 0 0         if (ctx->announce.size > 1) {
442             /* add the announce-list key-value pair */
443             size_t i;
444 0           bt_str_append(ctx, "13:announce-listll");
445              
446 0 0         for (i = 0; i < ctx->announce.size; i++) {
447 0 0         if (i > 0) {
448 0           bt_str_append(ctx, "el");
449             }
450 0           bt_bencode_str(ctx, 0, ctx->announce.array[i]);
451             }
452 0           bt_str_append(ctx, "ee");
453             }
454             }
455              
456 2 50         if (ctx->program_name) {
457 0           bt_bencode_str(ctx, "10:created by", ctx->program_name);
458             }
459 2           bt_bencode_int(ctx, "13:creation date", (uint64_t)time(NULL));
460              
461 2           bt_str_append(ctx, "8:encoding5:UTF-8");
462             }
463              
464             /* write the essential for BTIH part of the torrent file */
465              
466 2           bt_str_append(ctx, "4:infod"); /* start the info dictionary */
467 2           info_start_pos = ctx->content.length - 1;
468              
469 2 50         if (ctx->files.size > 1) {
470             size_t i;
471              
472             /* process batch torrent */
473 0           bt_str_append(ctx, "5:filesl"); /* start list of files */
474              
475             /* write length and path for each file in the batch */
476 0 0         for (i = 0; i < ctx->files.size; i++) {
477 0           bt_file_info_append(ctx, "d6:length", "4:pathl",
478 0           (bt_file_info*)ctx->files.array[i]);
479 0           bt_str_append(ctx, "ee");
480             }
481             /* note: get_batch_name modifies path, so should be called here */
482 0           bt_bencode_str(ctx, "e4:name", get_batch_name(
483 0           ((bt_file_info*)ctx->files.array[0])->path));
484             }
485 2 50         else if (ctx->files.size > 0) {
486             /* write size and basename of the first file */
487             /* in the non-batch mode other files are ignored */
488 0           bt_file_info_append(ctx, "6:length", "4:name",
489 0           (bt_file_info*)ctx->files.array[0]);
490             }
491              
492 2           bt_bencode_int(ctx, "12:piece length", ctx->piece_length);
493 2           bt_str_append(ctx, "6:pieces");
494 2           bt_bencode_pieces(ctx);
495              
496 2 50         if (ctx->options & BT_OPT_PRIVATE) {
497 0           bt_str_append(ctx, "7:privatei1e");
498             }
499 2           bt_str_append(ctx, "ee");
500              
501             /* calculate BTIH */
502 2           SHA1_INIT(ctx);
503 2           SHA1_UPDATE(ctx, (unsigned char*)ctx->content.str + info_start_pos,
504             ctx->content.length - info_start_pos - 1);
505 2           SHA1_FINAL(ctx, ctx->btih);
506 2           }
507              
508             /* Getters/Setters */
509              
510             /**
511             * Get BTIH (BitTorrent Info Hash) value.
512             *
513             * @param ctx the torrent algorithm context
514             * @return the 20-bytes long BTIH value
515             */
516 0           unsigned char* bt_get_btih(torrent_ctx* ctx)
517             {
518 0           return ctx->btih;
519             }
520              
521             /**
522             * Set the torrent algorithm options.
523             *
524             * @param ctx the torrent algorithm context
525             * @param options the options to set
526             */
527 0           void bt_set_options(torrent_ctx* ctx, unsigned options)
528             {
529 0           ctx->options = options;
530 0           }
531              
532             #if defined(__STRICT_ANSI__)
533             /* define strdup for gcc -ansi */
534             static char* bt_strdup(const char* str)
535             {
536             size_t len = strlen(str);
537             char* res = (char*)malloc(len + 1);
538             if (res) memcpy(res, str, len + 1);
539             return res;
540             }
541             #define strdup bt_strdup
542             #endif /* __STRICT_ANSI__ */
543              
544             /**
545             * Set optional name of the program generating the torrent
546             * for storing into torrent file.
547             *
548             * @param ctx the torrent algorithm context
549             * @param name the program name
550             * @return non-zero on success, zero on error
551             */
552 0           int bt_set_program_name(torrent_ctx* ctx, const char* name)
553             {
554 0           ctx->program_name = strdup(name);
555 0           return (ctx->program_name != NULL);
556             }
557              
558             /**
559             * Set length of a file piece.
560             *
561             * @param ctx the torrent algorithm context
562             * @param piece_length the piece length in bytes
563             */
564 0           void bt_set_piece_length(torrent_ctx* ctx, size_t piece_length)
565             {
566 0           ctx->piece_length = piece_length;
567 0           }
568              
569             /**
570             * Add a tracker announce-URL to the torrent file.
571             *
572             * @param ctx the torrent algorithm context
573             * @param announce_url the announce URL of the tracker
574             * @return non-zero on success, zero on error
575             */
576 0           int bt_add_announce(torrent_ctx* ctx, const char* announce_url)
577             {
578             char* url_copy;
579 0 0         if (!announce_url || announce_url[0] == '\0') return 0;
    0          
580 0           url_copy = strdup(announce_url);
581 0 0         if (!url_copy) return 0;
582 0 0         if (bt_vector_add_ptr(&ctx->announce, url_copy))
583 0           return 1;
584 0           free(url_copy);
585 0           return 0;
586             }
587              
588             /**
589             * Get the content of generated torrent file.
590             *
591             * @param ctx the torrent algorithm context
592             * @param pstr pointer to pointer receiving the buffer with file content
593             * @return length of the torrent file content
594             */
595 0           size_t bt_get_text(torrent_ctx* ctx, char** pstr)
596             {
597 0 0         assert(ctx->content.str);
598 0           *pstr = ctx->content.str;
599 0           return ctx->content.length;
600             }