/** * Finalize hashing and optionally store calculated hash into the given array. * If the result parameter is NULL, the hash is not stored, but it is * accessible by torrent_get_btih(). * * @param ctx the algorithm context containing current hashing state * @param result pointer to the array store message hash into */ void torrent_final(torrent_ctx *ctx, unsigned char result[20]) { unsigned char hash[20]; if(ctx->index > 0) { SHA1_FINAL(ctx, hash); BT_ADD_HASH(ctx, hash); } make_torrent(ctx); if(result) memcpy(result, ctx->btih, btih_hash_size); }
/** * Store a SHA1 hash of a processed file piece. * * @param ctx torrent algorithm context * @return non-zero on success, zero on fail */ static int bt_store_piece_sha1(torrent_ctx *ctx) { unsigned char* block; unsigned char* hash; if((ctx->piece_count % BT_BLOCK_SIZE) == 0) { block = (unsigned char*)malloc(BT_HASH_SIZE * BT_BLOCK_SIZE); if(block == NULL || !bt_vector_add_ptr(&ctx->hash_blocks, block)) { if(block) free(block); return 0; } } else { block = (unsigned char*)(ctx->hash_blocks.array[ctx->piece_count / BT_BLOCK_SIZE]); } hash = &block[BT_HASH_SIZE * (ctx->piece_count % BT_BLOCK_SIZE)]; SHA1_FINAL(ctx, hash); /* write the hash */ ctx->piece_count++; return 1; }
/** * Calculate message hash. * Can be called repeatedly with chunks of the message to be hashed. * * @param ctx the algorithm context containing current hashing state * @param msg message chunk * @param size length of the message chunk */ void torrent_update(torrent_ctx *ctx, const void* msg, size_t size) { unsigned char hash[20]; const unsigned char* pmsg = (const unsigned char*)msg; size_t rest = (size_t)(ctx->piece_length - ctx->index); assert(ctx->index < ctx->piece_length); while(size > 0) { size_t left = (size < rest ? size : rest); SHA1_UPDATE(ctx, pmsg, left); if(size < rest) { ctx->index += left; break; } SHA1_FINAL(ctx, hash); BT_ADD_HASH(ctx, hash); /* store a piece hash */ SHA1_INIT(ctx); ctx->index = 0; pmsg += rest; size -= rest; rest = ctx->piece_length; } }
/** * Generate torrent file content * @see http://wiki.theory.org/BitTorrentSpecification * * @param ctx the torrent algorithm context */ static void bt_generate_torrent(torrent_ctx *ctx) { uint64_t total_size = 0; size_t info_start_pos; assert(ctx->content.str == NULL); if(ctx->piece_length == 0) { if(ctx->files.size == 1) { total_size = ((bt_file_info*)ctx->files.array[0])->size; } ctx->piece_length = bt_default_piece_length(total_size); } /* write torrent header to the ctx->torrent string buffer */ if((ctx->options & BT_OPT_INFOHASH_ONLY) == 0) { bt_str_append(ctx, "d"); if(ctx->announce) { bt_bencode_str(ctx, "8:announce", ctx->announce); } if(ctx->program_name) { bt_bencode_str(ctx, "10:created by", ctx->program_name); } bt_bencode_int(ctx, "13:creation date", (uint64_t)time(NULL)); } bt_str_append(ctx, "8:encoding5:UTF-8"); bt_str_append(ctx, "4:infod"); /* start info dictionary */ info_start_pos = ctx->content.length - 1; if(ctx->files.size > 1) { size_t i; /* process batch torrent */ bt_str_append(ctx, "5:filesl"); /* start list of files */ /* write length and path for each file in the batch */ for(i = 0; i < ctx->files.size; i++) { bt_file_info_append(ctx, "d6:length", "4:pathl", (bt_file_info*)ctx->files.array[i]); bt_str_append(ctx, "ee"); } /* note: get_batch_name modifies path, so should be called here */ bt_bencode_str(ctx, "e4:name", get_batch_name( ((bt_file_info*)ctx->files.array[0])->path)); } else if(ctx->files.size > 0) { /* write size and basename of the first file */ /* in the non-batch mode other files are ignored */ bt_file_info_append(ctx, "6:length", "4:name", (bt_file_info*)ctx->files.array[0]); } bt_bencode_int(ctx, "12:piece length", ctx->piece_length); bt_str_append(ctx, "6:pieces"); bt_bencode_pieces(ctx); if(ctx->options & BT_OPT_PRIVATE) { bt_str_append(ctx, "7:privatei1e"); } bt_str_append(ctx, "ee"); /* calculate BTIH */ SHA1_INIT(ctx); SHA1_UPDATE(ctx, (unsigned char*)ctx->content.str + info_start_pos, ctx->content.length - info_start_pos - 1); SHA1_FINAL(ctx, ctx->btih); }
/** * Generate torrent file content * @see http://wiki.theory.org/BitTorrentSpecification * * @param ctx the torrent algorithm context */ static void make_torrent(torrent_ctx *ctx) { uint64_t total_size = 0; size_t info_start_pos; assert(ctx->torrent == NULL); assert(ctx->files.size <= 1); ctx->torrent = str_new(); if(ctx->piece_length == 0) { if(ctx->files.size == 1) { total_size = ((file_n_size_info*)ctx->files.array[0])->size; } ctx->piece_length = torrent_default_piece_length(total_size); } /* write torrent header to the ctx->torrent string bufer */ if((ctx->flags & BT_OPT_INFOHASH_ONLY) == 0) { str_append(ctx->torrent, "d"); if(ctx->announce) { str_append(ctx->torrent, "8:announce"); bt_bencode_str(ctx->torrent, ctx->announce); } if(ctx->program_name) { str_append(ctx->torrent, "10:created by"); bt_bencode_str(ctx->torrent, ctx->program_name); } str_append(ctx->torrent, "13:creation date"); bt_bencode_int(ctx->torrent, (uint64_t)time(NULL)); } str_append(ctx->torrent, "8:encoding5:UTF-8"); str_append(ctx->torrent, "4:infod"); /* start info dictionary */ info_start_pos = ctx->torrent->len - 1; if(ctx->files.size == 1) { file_n_size_info* f = (file_n_size_info*)ctx->files.array[0]; str_append(ctx->torrent, "6:length"); bt_bencode_int(ctx->torrent, f->size); /* note: for one file f->path must be a basename */ str_append(ctx->torrent, "4:name"); bt_bencode_str(ctx->torrent, f->path); } str_append(ctx->torrent, "12:piece length"); bt_bencode_int(ctx->torrent, ctx->piece_length); str_append(ctx->torrent, "6:pieces"); bt_bencode_pieces(ctx->torrent, ctx); if(ctx->flags & BT_OPT_PRIVATE) { str_append(ctx->torrent, "7:privatei1e"); } str_append(ctx->torrent, "ee"); SHA1_INIT(ctx); SHA1_UPDATE(ctx, (unsigned char*)ctx->torrent->str + info_start_pos, ctx->torrent->len - info_start_pos - 1); SHA1_FINAL(ctx, ctx->btih); }