/** * Initialize torrent context before calculating hash. * * @param ctx context to initialize */ void bt_init(torrent_ctx* ctx) { memset(ctx, 0, sizeof(torrent_ctx)); ctx->piece_length = BT_MIN_HASH_LENGTH; assert(BT_MIN_HASH_LENGTH == bt_default_piece_length(0)); #ifdef USE_OPENSSL { /* get the methods of the selected SHA1 algorithm */ rhash_hash_info *sha1_info = &rhash_info_table[3]; assert(sha1_info->info->hash_id == RHASH_SHA1); assert(sha1_info->context_size <= (sizeof(sha1_ctx) + sizeof(unsigned long))); ctx->sha_init = sha1_info->init; ctx->sha_update = sha1_info->update; ctx->sha_final = sha1_info->final; } #endif SHA1_INIT(ctx); }
/** * Initialize torrent context before calculaing hash. * * @param ctx context to initalize */ void torrent_init(torrent_ctx* ctx) { memset(ctx, 0, sizeof(torrent_ctx)); ctx->blocks_hashes.blocks.destructor = free; ctx->files.destructor = free; ctx->piece_length = 65536; #ifdef USE_OPENSSL { /* get the methods of the selected SHA1 algorithm */ rhash_hash_info *sha1_info = &rhash_info_table[3]; assert(sha1_info->info.hash_id == RHASH_SHA1); assert(sha1_info->context_size <= (sizeof(sha1_ctx) + sizeof(unsigned long))); ctx->sha_init = sha1_info->init; ctx->sha_update = sha1_info->update; ctx->sha_final = sha1_info->final; } #endif SHA1_INIT(ctx); }
/** * 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 bt_update(torrent_ctx *ctx, const void* msg, size_t size) { 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; } bt_store_piece_sha1(ctx); SHA1_INIT(ctx); ctx->index = 0; pmsg += rest; size -= rest; rest = ctx->piece_length; } }
/** * 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); }