File Coverage

_torrent.c
Criterion Covered Total %
statement 128 403 31.7
branch 41 248 16.5
condition n/a
subroutine n/a
pod n/a
total 169 651 25.9


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 "torrent.h"
18             #include "hex.h"
19             #include "util.h"
20             #include
21             #include
22             #include
23             #include
24             #include /* time() */
25              
26             #ifdef USE_OPENSSL
27             #define SHA1_INIT(ctx) ((pinit_t)ctx->sha1_methods.init)(&ctx->sha1_context)
28             #define SHA1_UPDATE(ctx, msg, size) ((pupdate_t)ctx->sha1_methods.update)(&ctx->sha1_context, (msg), (size))
29             #define SHA1_FINAL(ctx, result) ((pfinal_t)ctx->sha1_methods.final)(&ctx->sha1_context, (result))
30             #else
31             #define SHA1_INIT(ctx) rhash_sha1_init(&ctx->sha1_context)
32             #define SHA1_UPDATE(ctx, msg, size) rhash_sha1_update(&ctx->sha1_context, (msg), (size))
33             #define SHA1_FINAL(ctx, result) rhash_sha1_final(&ctx->sha1_context, (result))
34             #endif
35              
36             #define BT_MIN_PIECE_LENGTH 16384
37             /** size of a SHA1 hash in bytes */
38             #define BT_HASH_SIZE 20
39             /** number of SHA1 hashes to store together in one block */
40             #define BT_BLOCK_SIZE 256
41             #define BT_BLOCK_SIZE_IN_BYTES (BT_BLOCK_SIZE * BT_HASH_SIZE)
42              
43             /**
44             * Initialize torrent context before calculating hash.
45             *
46             * @param ctx context to initialize
47             */
48 2           void bt_init(torrent_ctx* ctx)
49             {
50 2           memset(ctx, 0, sizeof(torrent_ctx));
51 2           ctx->piece_length = BT_MIN_PIECE_LENGTH;
52 2 50         assert(BT_MIN_PIECE_LENGTH == bt_default_piece_length(0, 0));
53              
54             #ifdef USE_OPENSSL
55             /* get the methods of the selected SHA1 algorithm */
56             assert(rhash_info_table[3].info->hash_id == RHASH_SHA1);
57             assert(rhash_info_table[3].context_size <= (sizeof(sha1_ctx) + sizeof(unsigned long)));
58             rhash_load_sha1_methods(&ctx->sha1_methods, METHODS_SELECTED);
59             #endif
60              
61 2           SHA1_INIT(ctx);
62 2           }
63              
64             /**
65             * Free memory allocated by properties of torrent_vect structure.
66             *
67             * @param vect vector to clean
68             */
69 3           static void bt_vector_clean(torrent_vect* vect)
70             {
71             size_t i;
72 4 100         for (i = 0; i < vect->size; i++) {
73 1           free(vect->array[i]);
74             }
75 3           free(vect->array);
76 3           }
77              
78             /**
79             * Clean up torrent context by freeing all dynamically
80             * allocated memory.
81             *
82             * @param ctx torrent algorithm context
83             */
84 1           void bt_cleanup(torrent_ctx* ctx)
85             {
86 1 50         assert(ctx != NULL);
87              
88             /* destroy arrays */
89 1           bt_vector_clean(&ctx->hash_blocks);
90 1           bt_vector_clean(&ctx->files);
91 1           bt_vector_clean(&ctx->announce);
92              
93 1           free(ctx->program_name);
94 1           free(ctx->content.str);
95 1           ctx->program_name = 0;
96 1           ctx->content.str = 0;
97 1           }
98              
99             static void bt_generate_torrent(torrent_ctx* ctx);
100              
101             /**
102             * Add an item to vector.
103             *
104             * @param vect vector to add item to
105             * @param item the item to add
106             * @return non-zero on success, zero on fail
107             */
108 2           static int bt_vector_add_ptr(torrent_vect* vect, void* item)
109             {
110             /* check if vector contains enough space for the next item */
111 2 50         if (vect->size >= vect->allocated) {
112 2 50         size_t size = (vect->allocated == 0 ? 128 : vect->allocated * 2);
113 2           void* new_array = realloc(vect->array, size * sizeof(void*));
114 2 50         if (new_array == NULL) return 0; /* failed: no memory */
115 2           vect->array = (void**)new_array;
116 2           vect->allocated = size;
117             }
118             /* add new item to the vector */
119 2           vect->array[vect->size] = item;
120 2           vect->size++;
121 2           return 1;
122             }
123              
124             /**
125             * Store a SHA1 hash of a processed file piece.
126             *
127             * @param ctx torrent algorithm context
128             * @return non-zero on success, zero on fail
129             */
130 2           static int bt_store_piece_sha1(torrent_ctx* ctx)
131             {
132             unsigned char* block;
133             unsigned char* hash;
134              
135 2 50         if ((ctx->piece_count % BT_BLOCK_SIZE) == 0) {
136 2           block = (unsigned char*)malloc(BT_BLOCK_SIZE_IN_BYTES);
137 2 50         if (!block)
138 0           return 0;
139 2 50         if (!bt_vector_add_ptr(&ctx->hash_blocks, block)) {
140 0           free(block);
141 0           return 0;
142             }
143             } else {
144 0           block = (unsigned char*)(ctx->hash_blocks.array[ctx->piece_count / BT_BLOCK_SIZE]);
145             }
146              
147 2           hash = &block[BT_HASH_SIZE * (ctx->piece_count % BT_BLOCK_SIZE)];
148 2           SHA1_FINAL(ctx, hash); /* write the hash */
149 2           ctx->piece_count++;
150 2           return 1;
151             }
152              
153             /**
154             * A filepath and filesize information.
155             */
156             typedef struct bt_file_info
157             {
158             uint64_t size;
159             char path[];
160             } bt_file_info;
161              
162             /**
163             * Add a file info into the batch of files of given torrent.
164             *
165             * @param ctx torrent algorithm context
166             * @param path file path
167             * @param filesize file size
168             * @return non-zero on success, zero on fail
169             */
170 0           int bt_add_file(torrent_ctx* ctx, const char* path, uint64_t filesize)
171             {
172 0           size_t len = strlen(path);
173 0           bt_file_info* info = (bt_file_info*)malloc(sizeof(uint64_t) + len + 1);
174 0 0         if (info == NULL) {
175 0           ctx->error = 1;
176 0           return 0;
177             }
178              
179 0           info->size = filesize;
180 0           memcpy(info->path, path, len + 1);
181 0 0         if (!bt_vector_add_ptr(&ctx->files, info)) {
182 0           free(info);
183 0           return 0;
184             }
185              
186             /* recalculate piece length (but only if hashing not started yet) */
187 0 0         if (ctx->piece_count == 0 && ctx->index == 0) {
    0          
188             /* note: in case of batch of files should use a total batch size */
189 0           ctx->piece_length = bt_default_piece_length(filesize, ctx->options & BT_OPT_TRANSMISSION);
190             }
191 0           return 1;
192             }
193              
194             /**
195             * Calculate message hash.
196             * Can be called repeatedly with chunks of the message to be hashed.
197             *
198             * @param ctx the algorithm context containing current hashing state
199             * @param msg message chunk
200             * @param size length of the message chunk
201             */
202 2           void bt_update(torrent_ctx* ctx, const void* msg, size_t size)
203             {
204 2           const unsigned char* pmsg = (const unsigned char*)msg;
205 2           size_t rest = (size_t)(ctx->piece_length - ctx->index);
206 2 50         assert(ctx->index < ctx->piece_length);
207              
208 2 50         while (size > 0) {
209 2           size_t left = (size < rest ? size : rest);
210 2           SHA1_UPDATE(ctx, pmsg, left);
211 2 50         if (size < rest) {
212 2           ctx->index += left;
213 2           break;
214             }
215 0           bt_store_piece_sha1(ctx);
216 0           SHA1_INIT(ctx);
217 0           ctx->index = 0;
218              
219 0           pmsg += rest;
220 0           size -= rest;
221 0           rest = ctx->piece_length;
222             }
223 2           }
224              
225             /**
226             * Finalize hashing and optionally store calculated hash into the given array.
227             * If the result parameter is NULL, the hash is not stored, but it is
228             * accessible by bt_get_btih().
229             *
230             * @param ctx the algorithm context containing current hashing state
231             * @param result pointer to the array store message hash into
232             */
233 2           void bt_final(torrent_ctx* ctx, unsigned char result[20])
234             {
235 2 50         if (ctx->index > 0) {
236 2           bt_store_piece_sha1(ctx); /* flush buffered data */
237             }
238              
239 2           bt_generate_torrent(ctx);
240 2 50         if (result) memcpy(result, ctx->btih, btih_hash_size);
241 2           }
242              
243             /* BitTorrent functions */
244              
245             /**
246             * Grow, if needed, the torrent_str buffer to ensure it contains
247             * at least (length + 1) characters.
248             *
249             * @param ctx the torrent algorithm context
250             * @param length length of the string, the allocated buffer must contain
251             * @return 1 on success, 0 on error
252             */
253 20           static int bt_str_ensure_length(torrent_ctx* ctx, size_t length)
254             {
255             char* new_str;
256 20 50         if (ctx->error)
257 0           return 0;
258 20 100         if (length >= ctx->content.allocated) {
259 4           length++; /* allocate one character more */
260 4 100         if (length < 64) length = 64;
261 2           else length = (length + 255) & ~255;
262 4           new_str = (char*)realloc(ctx->content.str, length);
263 4 50         if (new_str == NULL) {
264 0           ctx->error = 1;
265 0           ctx->content.allocated = 0;
266 0           return 0;
267             }
268 4           ctx->content.str = new_str;
269 4           ctx->content.allocated = length;
270             }
271 20           return 1;
272             }
273              
274             /**
275             * Append a null-terminated string to the string string buffer.
276             *
277             * @param ctx the torrent algorithm context
278             * @param text the null-terminated string to append
279             */
280 14           static void bt_str_append(torrent_ctx* ctx, const char* text)
281             {
282 14           size_t length = strlen(text);
283 14 50         if (!bt_str_ensure_length(ctx, ctx->content.length + length + 1))
284 0           return;
285 14 50         assert(ctx->content.str != 0);
286 14           memcpy(ctx->content.str + ctx->content.length, text, length + 1);
287 14           ctx->content.length += length;
288             }
289              
290             /**
291             * B-encode given integer.
292             *
293             * @param ctx the torrent algorithm context
294             * @param name B-encoded string to prepend the number or NULL
295             * @param number the integer to output
296             */
297 4           static void bt_bencode_int(torrent_ctx* ctx, const char* name, uint64_t number)
298             {
299             char* p;
300 4 50         if (name)
301 4           bt_str_append(ctx, name);
302              
303             /* add up to 20 digits and 2 letters */
304 4 50         if (!bt_str_ensure_length(ctx, ctx->content.length + 22))
305 0           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 name B-encoded string to prepend or NULL
320             * @param str the string to encode
321             */
322 0           static void bt_bencode_str(torrent_ctx* ctx, const char* name, const char* str)
323             {
324 0           const size_t string_length = strlen(str);
325             int number_length;
326             char* p;
327              
328 0 0         if (name)
329 0           bt_str_append(ctx, name);
330 0 0         if (!bt_str_ensure_length(ctx, ctx->content.length + string_length + 21))
331 0           return;
332 0           p = ctx->content.str + ctx->content.length;
333 0           p += (number_length = rhash_sprintI64(p, string_length));
334 0           ctx->content.length += string_length + number_length + 1;
335              
336 0           *(p++) = ':';
337 0           memcpy(p, str, string_length + 1); /* copy with trailing '\0' */
338             }
339              
340             /**
341             * B-encode array of SHA1 hashes of file pieces.
342             *
343             * @param ctx pointer to the torrent structure containing SHA1 hashes
344             */
345 2           static void bt_bencode_pieces(torrent_ctx* ctx)
346             {
347 2           const size_t pieces_length = ctx->piece_count * BT_HASH_SIZE;
348             size_t bytes_left, i;
349             int number_length;
350             char* p;
351              
352 2 50         if (!bt_str_ensure_length(ctx, ctx->content.length + pieces_length + 21))
353 0           return;
354 2           p = ctx->content.str + ctx->content.length;
355 2           p += (number_length = rhash_sprintI64(p, pieces_length));
356 2           ctx->content.length += pieces_length + number_length + 1;
357              
358 2           *(p++) = ':';
359 2           p[pieces_length] = '\0'; /* terminate with \0 just in case */
360              
361 4 100         for (bytes_left = pieces_length, i = 0; bytes_left > 0; i++)
362             {
363 2           size_t size = (bytes_left < BT_BLOCK_SIZE_IN_BYTES ? bytes_left : BT_BLOCK_SIZE_IN_BYTES);
364 2           memcpy(p, ctx->hash_blocks.array[i], size);
365 2           bytes_left -= size;
366 2           p += size;
367             }
368             }
369              
370             /**
371             * Calculate default torrent piece length, using uTorrent algorithm.
372             * Algorithm:
373             * piece_length = 16K for total_size < 16M,
374             * piece_length = 8M for total_size >= 4G,
375             * piece_length = top_bit(total_size) / 512 otherwise.
376             *
377             * @param total_size total torrent batch size
378             * @return piece length used by torrent file
379             */
380 2           static size_t utorr_piece_length(uint64_t total_size)
381             {
382 2           size_t size = (size_t)(total_size >> 9) | 16384;
383             size_t hi_bit;
384 20 100         for (hi_bit = 8388608; hi_bit > size; hi_bit >>= 1);
385 2           return hi_bit;
386             }
387              
388             #define MB I64(1048576)
389              
390             /**
391             * Calculate default torrent piece length, using transmission algorithm.
392             * Algorithm:
393             * piece_length = (size >= 2G ? 2M : size >= 1G ? 1M :
394             * size >= 512M ? 512K : size >= 350M ? 256K :
395             * size >= 150M ? 128K : size >= 50M ? 64K : 32K);
396             *
397             * @param total_size total torrent batch size
398             * @return piece length used by torrent file
399             */
400 0           static size_t transmission_piece_length(uint64_t total_size)
401             {
402             static const uint64_t sizes[6] = { 50 * MB, 150 * MB, 350 * MB, 512 * MB, 1024 * MB, 2048 * MB };
403             int i;
404 0 0         for (i = 0; i < 6 && total_size >= sizes[i]; i++);
    0          
405 0           return (32 * 1024) << i;
406             }
407              
408 2           size_t bt_default_piece_length(uint64_t total_size, int transmission)
409             {
410             return (transmission ?
411 2 50         transmission_piece_length(total_size) : utorr_piece_length(total_size));
412             }
413              
414             /* get file basename */
415 0           static const char* bt_get_basename(const char* path)
416             {
417 0           const char* p = strchr(path, '\0') - 1;
418 0 0         for (; p >= path && *p != '/' && *p != '\\'; p--);
    0          
    0          
419 0           return (p + 1);
420             }
421              
422             /* extract batchname from the path, modifies the path buffer */
423 0           static const char* get_batch_name(char* path)
424             {
425 0           char* p = (char*)bt_get_basename(path) - 1;
426 0 0         for (; p > path && (*p == '/' || *p == '\\'); p--) *p = 0;
    0          
    0          
427 0 0         if (p <= path) return "BATCH_DIR";
428 0           return bt_get_basename(path);
429             }
430              
431             /* write file size and path */
432 0           static void bt_file_info_append(torrent_ctx* ctx, const char* length_name,
433             const char* path_name, bt_file_info* info)
434             {
435 0           bt_bencode_int(ctx, length_name, info->size);
436             /* store the file basename */
437 0           bt_bencode_str(ctx, path_name, bt_get_basename(info->path));
438 0           }
439              
440             /**
441             * Generate torrent file content
442             * @see http://wiki.theory.org/BitTorrentSpecification
443             *
444             * @param ctx the torrent algorithm context
445             */
446 2           static void bt_generate_torrent(torrent_ctx* ctx)
447             {
448 2           uint64_t total_size = 0;
449             size_t info_start_pos;
450              
451 2 50         assert(ctx->content.str == NULL);
452              
453 2 50         if (ctx->piece_length == 0) {
454 0 0         if (ctx->files.size == 1) {
455 0           total_size = ((bt_file_info*)ctx->files.array[0])->size;
456             }
457 0           ctx->piece_length = bt_default_piece_length(total_size, ctx->options & BT_OPT_TRANSMISSION);
458             }
459              
460 2 50         if ((ctx->options & BT_OPT_INFOHASH_ONLY) == 0) {
461             /* write the torrent header */
462 2           bt_str_append(ctx, "d");
463 2 50         if (ctx->announce.array && ctx->announce.size > 0) {
    0          
464 0           bt_bencode_str(ctx, "8:announce", ctx->announce.array[0]);
465              
466             /* if more than one announce url */
467 0 0         if (ctx->announce.size > 1) {
468             /* add the announce-list key-value pair */
469             size_t i;
470 0           bt_str_append(ctx, "13:announce-listll");
471              
472 0 0         for (i = 0; i < ctx->announce.size; i++) {
473 0 0         if (i > 0) {
474 0           bt_str_append(ctx, "el");
475             }
476 0           bt_bencode_str(ctx, 0, ctx->announce.array[i]);
477             }
478 0           bt_str_append(ctx, "ee");
479             }
480             }
481              
482 2 50         if (ctx->program_name) {
483 0           bt_bencode_str(ctx, "10:created by", ctx->program_name);
484             }
485 2           bt_bencode_int(ctx, "13:creation date", (uint64_t)time(NULL));
486              
487 2           bt_str_append(ctx, "8:encoding5:UTF-8");
488             }
489              
490             /* write the essential for BTIH part of the torrent file */
491              
492 2           bt_str_append(ctx, "4:infod"); /* start the info dictionary */
493 2           info_start_pos = ctx->content.length - 1;
494              
495 2 50         if (ctx->files.size > 1) {
496             size_t i;
497              
498             /* process batch torrent */
499 0           bt_str_append(ctx, "5:filesl"); /* start list of files */
500              
501             /* write length and path for each file in the batch */
502 0 0         for (i = 0; i < ctx->files.size; i++) {
503 0           bt_file_info_append(ctx, "d6:length", "4:pathl",
504 0           (bt_file_info*)ctx->files.array[i]);
505 0           bt_str_append(ctx, "ee");
506             }
507             /* note: get_batch_name modifies path, so should be called here */
508 0           bt_bencode_str(ctx, "e4:name", get_batch_name(
509 0           ((bt_file_info*)ctx->files.array[0])->path));
510             }
511 2 50         else if (ctx->files.size > 0) {
512             /* write size and basename of the first file */
513             /* in the non-batch mode other files are ignored */
514 0           bt_file_info_append(ctx, "6:length", "4:name",
515 0           (bt_file_info*)ctx->files.array[0]);
516             }
517              
518 2           bt_bencode_int(ctx, "12:piece length", ctx->piece_length);
519 2           bt_str_append(ctx, "6:pieces");
520 2           bt_bencode_pieces(ctx);
521              
522 2 50         if (ctx->options & BT_OPT_PRIVATE) {
523 0           bt_str_append(ctx, "7:privatei1e");
524 2 50         } else if (ctx->options & BT_OPT_TRANSMISSION) {
525 0           bt_str_append(ctx, "7:privatei0e");
526             }
527 2           bt_str_append(ctx, "ee");
528              
529             /* calculate BTIH */
530 2           SHA1_INIT(ctx);
531 2 50         if (ctx->content.str) {
532 2           SHA1_UPDATE(ctx, (unsigned char*)ctx->content.str + info_start_pos,
533             ctx->content.length - info_start_pos - 1);
534             }
535 2           SHA1_FINAL(ctx, ctx->btih);
536 2           }
537              
538             /* Getters/Setters */
539              
540             /**
541             * Get BTIH (BitTorrent Info Hash) value.
542             *
543             * @param ctx the torrent algorithm context
544             * @return the 20-bytes long BTIH value
545             */
546 0           unsigned char* bt_get_btih(torrent_ctx* ctx)
547             {
548 0           return ctx->btih;
549             }
550              
551             /**
552             * Set the torrent algorithm options.
553             *
554             * @param ctx the torrent algorithm context
555             * @param options the options to set
556             */
557 0           void bt_set_options(torrent_ctx* ctx, unsigned options)
558             {
559 0           ctx->options = options;
560 0           }
561              
562             #if defined(__STRICT_ANSI__)
563             /* define strdup for gcc -ansi */
564             static char* bt_strdup(const char* str)
565             {
566             size_t len = strlen(str);
567             char* res = (char*)malloc(len + 1);
568             if (res) memcpy(res, str, len + 1);
569             return res;
570             }
571             #define strdup bt_strdup
572             #endif /* __STRICT_ANSI__ */
573              
574             /**
575             * Set optional name of the program generating the torrent
576             * for storing into torrent file.
577             *
578             * @param ctx the torrent algorithm context
579             * @param name the program name
580             * @return non-zero on success, zero on error
581             */
582 0           int bt_set_program_name(torrent_ctx* ctx, const char* name)
583             {
584 0           ctx->program_name = strdup(name);
585 0           return (ctx->program_name != NULL);
586             }
587              
588             /**
589             * Set length of a file piece.
590             *
591             * @param ctx the torrent algorithm context
592             * @param piece_length the piece length in bytes
593             */
594 0           void bt_set_piece_length(torrent_ctx* ctx, size_t piece_length)
595             {
596 0           ctx->piece_length = piece_length;
597 0           }
598              
599             /**
600             * Set length of a file piece by the total batch size.
601             *
602             * @param ctx the torrent algorithm context
603             * @param total_size total batch size
604             */
605 0           void bt_set_total_batch_size(torrent_ctx* ctx, uint64_t total_size)
606             {
607 0           ctx->piece_length = bt_default_piece_length(total_size, ctx->options & BT_OPT_TRANSMISSION);
608 0           }
609              
610             /**
611             * Add a tracker announce-URL to the torrent file.
612             *
613             * @param ctx the torrent algorithm context
614             * @param announce_url the announce URL of the tracker
615             * @return non-zero on success, zero on error
616             */
617 0           int bt_add_announce(torrent_ctx* ctx, const char* announce_url)
618             {
619             char* url_copy;
620 0 0         if (!announce_url || announce_url[0] == '\0') return 0;
    0          
621 0           url_copy = strdup(announce_url);
622 0 0         if (!url_copy) return 0;
623 0 0         if (bt_vector_add_ptr(&ctx->announce, url_copy))
624 0           return 1;
625 0           free(url_copy);
626 0           return 0;
627             }
628              
629             /**
630             * Get the content of generated torrent file.
631             *
632             * @param ctx the torrent algorithm context
633             * @param pstr pointer to pointer receiving the buffer with file content
634             * @return length of the torrent file content
635             */
636 0           size_t bt_get_text(torrent_ctx* ctx, char** pstr)
637             {
638 0 0         assert(ctx->content.str);
639 0           *pstr = ctx->content.str;
640 0           return ctx->content.length;
641             }
642              
643             #if !defined(NO_IMPORT_EXPORT)
644              
645             # define EXPORT_ALIGNER 7
646             # define GET_EXPORT_ALIGNED(size) (((size) + EXPORT_ALIGNER) & ~EXPORT_ALIGNER)
647             # define GET_EXPORT_PADDING(size) (-(size) & EXPORT_ALIGNER)
648             # define GET_EXPORT_STR_LEN(length) GET_EXPORT_ALIGNED((length) + 1)
649             # define GET_EXPORT_SIZED_STR_LEN(length) GET_EXPORT_STR_LEN((length) + sizeof(size_t))
650             # define IS_EXPORT_ALIGNED(size) (((size) & EXPORT_ALIGNER) == 0)
651             # define BT_CTX_OSSL_FLAG 0x10
652              
653 0           static void bt_export_str(char* out, const char* str, size_t length)
654             {
655 0 0         assert(!!out);
656 0           *(size_t*)(out) = length;
657 0           out += sizeof(size_t);
658 0           memcpy(out, str, length + 1);
659 0           }
660              
661             typedef struct bt_export_header {
662             size_t torrent_ctx_size;
663             size_t files_size;
664             size_t announce_size;
665             size_t program_name_length;
666             size_t content_length;
667             } bt_export_header;
668              
669             /**
670             * Export algorithm context to a memory region, or calculate the
671             * size required for context export.
672             *
673             * @param ctx the algorithm context containing current hashing state
674             * @param out pointer to the memory region or NULL
675             * @param size size of memory region
676             * @return the size of the exported data on success, 0 on fail.
677             */
678 0           size_t bt_export(const torrent_ctx* ctx, void* out, size_t size)
679             {
680 0           const size_t head_size = sizeof(bt_export_header);
681 0           const size_t ctx_head_size = offsetof(torrent_ctx, hash_blocks);
682 0           const size_t hashes_size = ctx->piece_count * BT_HASH_SIZE;
683 0           size_t exported_size = head_size + ctx_head_size + hashes_size;
684 0           const size_t padding_size = GET_EXPORT_PADDING(exported_size);
685 0 0         const size_t program_name_length = (ctx->program_name ? strlen(ctx->program_name) : 0);
686 0           char* out_ptr = (char*)out;
687             size_t i;
688 0 0         assert((exported_size + padding_size) == GET_EXPORT_ALIGNED(exported_size));
689 0 0         if (out_ptr) {
690 0           bt_export_header* header = (bt_export_header*)out_ptr;
691 0           size_t hash_data_left = hashes_size;
692 0 0         if (size < exported_size)
693 0           return 0;
694 0           header->torrent_ctx_size = sizeof(torrent_ctx);
695 0           header->files_size = ctx->files.size;
696 0           header->announce_size = ctx->announce.size;
697 0           header->program_name_length = program_name_length;
698 0           header->content_length = ctx->content.length;
699 0           out_ptr += head_size;
700              
701 0           memcpy(out_ptr, ctx, ctx_head_size);
702 0           out_ptr += ctx_head_size;
703              
704 0 0         for (i = 0; i < ctx->hash_blocks.size && hash_data_left; i++) {
    0          
705 0           size_t left = (hash_data_left < BT_BLOCK_SIZE_IN_BYTES ? hash_data_left : BT_BLOCK_SIZE_IN_BYTES);
706 0           memcpy(out_ptr, ctx->hash_blocks.array[i], left);
707 0           out_ptr += left;
708 0           hash_data_left -= left;
709             }
710 0           out_ptr += padding_size;
711             }
712 0           exported_size += padding_size;
713 0 0         assert(IS_EXPORT_ALIGNED(exported_size));
714              
715 0 0         for (i = 0; i < ctx->files.size; i++) {
716 0           bt_file_info* info = (bt_file_info*)(ctx->files.array[i]);
717 0           size_t length = strlen(info->path);
718 0           const size_t aligned_length = GET_EXPORT_SIZED_STR_LEN(length);
719 0 0         if (!length)
720 0           continue;
721 0           exported_size += sizeof(uint64_t) + aligned_length;
722 0 0         if (out_ptr) {
723 0 0         if (size < exported_size)
724 0           return 0;
725 0           *(uint64_t*)out_ptr = info->size;
726 0           out_ptr += sizeof(uint64_t);
727 0           bt_export_str(out_ptr, info->path, length);
728 0           out_ptr += aligned_length;
729             }
730             }
731 0 0         assert(IS_EXPORT_ALIGNED(exported_size));
732              
733 0 0         for (i = 0; i < ctx->announce.size; i++) {
734 0           size_t length = strlen(ctx->announce.array[i]);
735 0           const size_t aligned_length = GET_EXPORT_SIZED_STR_LEN(length);
736 0 0         if (!length)
737 0           continue;
738 0           exported_size += aligned_length;
739 0 0         if (out_ptr) {
740 0 0         if (size < exported_size)
741 0           return 0;
742 0           bt_export_str(out_ptr, ctx->announce.array[i], length);
743 0           out_ptr += aligned_length;
744             }
745             }
746 0 0         assert(IS_EXPORT_ALIGNED(exported_size));
747              
748 0 0         if (program_name_length > 0) {
749 0           const size_t aligned_length = GET_EXPORT_STR_LEN(program_name_length);
750 0           exported_size += aligned_length;
751 0 0         if (out_ptr) {
752 0 0         if (size < exported_size)
753 0           return 0;
754 0           strcpy(out_ptr, ctx->program_name);
755 0           out_ptr += aligned_length;
756             }
757 0 0         assert(IS_EXPORT_ALIGNED(exported_size));
758             }
759              
760 0 0         if (ctx->content.length > 0) {
761 0           const size_t aligned_length = GET_EXPORT_STR_LEN(ctx->content.length);
762 0           exported_size += aligned_length;
763 0 0         if (out_ptr) {
764 0 0         if (size < exported_size)
765 0           return 0;
766 0 0         assert(ctx->content.str != NULL);
767 0           memcpy(out_ptr, ctx->content.str, ctx->content.length + 1);
768 0           out_ptr += aligned_length;
769             }
770 0 0         assert(IS_EXPORT_ALIGNED(exported_size));
771             }
772 0 0         assert(!out || (size_t)(out_ptr - (char*)out) == exported_size);
    0          
773              
774             #if defined(USE_OPENSSL)
775             if (out_ptr && ARE_OPENSSL_METHODS(ctx->sha1_methods)) {
776             size_t* error_ptr = (size_t*)((char*)out + head_size + offsetof(torrent_ctx, error));
777             *error_ptr |= BT_CTX_OSSL_FLAG;
778             RHASH_ASSERT(sizeof(*error_ptr) == sizeof(ctx->error));
779             }
780             #endif
781 0           return exported_size;
782             }
783              
784             /**
785             * Import algorithm context from a memory region.
786             *
787             * @param ctx pointer to the algorithm context
788             * @param in pointer to the data to import
789             * @param size size of data to import
790             * @return the size of the imported data on success, 0 on fail.
791             */
792 0           size_t bt_import(torrent_ctx* ctx, const void* in, size_t size)
793             {
794 0           const size_t head_size = sizeof(bt_export_header);
795 0           const size_t ctx_head_size = offsetof(torrent_ctx, hash_blocks);
796 0           size_t imported_size = head_size + ctx_head_size;
797 0           const char* in_ptr = (const char*)in;
798             size_t padding_size;
799             size_t hash_data_left;
800             size_t length;
801             size_t i;
802 0           const bt_export_header* header = (const bt_export_header*)in_ptr;
803 0 0         if (size < imported_size)
804 0           return 0;
805 0 0         if (header->torrent_ctx_size != sizeof(torrent_ctx))
806 0           return 0;
807 0           in_ptr += sizeof(bt_export_header);
808              
809 0           memset(ctx, 0, sizeof(torrent_ctx));
810 0           memcpy(ctx, in_ptr, ctx_head_size);
811 0           in_ptr += ctx_head_size;
812              
813 0           hash_data_left = ctx->piece_count * BT_HASH_SIZE;
814 0           imported_size += hash_data_left;
815 0           padding_size = GET_EXPORT_PADDING(imported_size);
816 0           imported_size += padding_size;
817 0 0         assert(IS_EXPORT_ALIGNED(imported_size));
818 0 0         if (size < imported_size)
819 0           return 0;
820              
821 0 0         while (hash_data_left) {
822 0           size_t left = (hash_data_left < BT_BLOCK_SIZE_IN_BYTES ? hash_data_left : BT_BLOCK_SIZE_IN_BYTES);
823 0           unsigned char* block = (unsigned char*)malloc(BT_BLOCK_SIZE_IN_BYTES);
824 0 0         if (!block)
825 0           return 0;
826 0 0         if (!bt_vector_add_ptr(&ctx->hash_blocks, block)) {
827 0           free(block);
828 0           return 0;
829             }
830 0           memcpy(block, in_ptr, left);
831 0           in_ptr += left;
832 0           hash_data_left -= left;
833             }
834 0           in_ptr += padding_size;
835 0 0         assert((size_t)(in_ptr - (char*)in) == imported_size);
836 0 0         assert(IS_EXPORT_ALIGNED(imported_size));
837              
838 0 0         for (i = 0; i < header->files_size; i++) {
839             uint64_t filesize;
840 0           imported_size += sizeof(uint64_t);
841 0 0         if (size < (imported_size + sizeof(size_t)))
842 0           return 0;
843 0           filesize = *(uint64_t*)in_ptr;
844 0           in_ptr += sizeof(uint64_t);
845 0           length = *(size_t*)in_ptr;
846 0           imported_size += GET_EXPORT_SIZED_STR_LEN(length);
847 0 0         if (!length || size < imported_size)
    0          
848 0           return 0;
849 0 0         if (!bt_add_file(ctx, in_ptr + sizeof(size_t), filesize))
850 0           return 0;
851 0           in_ptr += GET_EXPORT_SIZED_STR_LEN(length);
852             }
853 0 0         assert((size_t)(in_ptr - (char*)in) == imported_size);
854 0 0         assert(IS_EXPORT_ALIGNED(imported_size));
855              
856 0 0         for (i = 0; i < header->announce_size; i++) {
857 0 0         if (size < (imported_size + sizeof(size_t)))
858 0           return 0;
859 0           length = *(size_t*)in_ptr;
860 0           imported_size += GET_EXPORT_SIZED_STR_LEN(length);
861 0 0         if (!length || size < imported_size)
    0          
862 0           return 0;
863 0 0         if (!bt_add_announce(ctx, in_ptr + sizeof(size_t)))
864 0           return 0;
865 0           in_ptr += GET_EXPORT_SIZED_STR_LEN(length);
866             }
867 0 0         assert((size_t)(in_ptr - (char*)in) == imported_size);
868 0 0         assert(IS_EXPORT_ALIGNED(imported_size));
869              
870 0           length = header->program_name_length;
871 0 0         if (length) {
872 0           imported_size += GET_EXPORT_STR_LEN(length);
873 0 0         if (size < imported_size)
874 0           return 0;
875 0 0         if (!bt_set_program_name(ctx, in_ptr))
876 0           return 0;
877 0           in_ptr += GET_EXPORT_STR_LEN(length);
878 0 0         assert((size_t)(in_ptr - (char*)in) == imported_size);
879 0 0         assert(IS_EXPORT_ALIGNED(imported_size));
880             }
881              
882             #if defined(USE_OPENSSL)
883             /* must restore ctx->error flag before calling bt_str_ensure_length() */
884             if ((ctx->error & BT_CTX_OSSL_FLAG) != 0) {
885             ctx->error &= ~BT_CTX_OSSL_FLAG;
886             rhash_load_sha1_methods(&ctx->sha1_methods, METHODS_OPENSSL);
887             } else {
888             rhash_load_sha1_methods(&ctx->sha1_methods, METHODS_RHASH);
889             }
890             #endif
891              
892 0           length = header->content_length;
893 0 0         if (length) {
894 0           imported_size += GET_EXPORT_STR_LEN(length);
895 0 0         if (size < imported_size)
896 0           return 0;
897 0 0         if (!bt_str_ensure_length(ctx, length))
898 0           return 0;
899 0           memcpy(ctx->content.str, in_ptr, length);
900 0           in_ptr += GET_EXPORT_STR_LEN(length);
901 0 0         assert((size_t)(in_ptr - (char*)in) == imported_size);
902 0 0         assert(IS_EXPORT_ALIGNED(imported_size));
903             }
904 0           return imported_size;
905             }
906             #endif /* !defined(NO_IMPORT_EXPORT) */