这个BitTorrent客户端的部分验证过程中的错误在哪里?

时间:2011-09-27 16:40:14

标签: c bittorrent

在我的业余时间,我一直在用C语言写一个BitTorrent客户端。然而 - 看似随意 - 我的客户将无法通过消息验证收到的一条洪流是否正确:

Failed to verify piece: #4

背景:.torrent文件包含要下载的文件的相关元数据,包括每个部分的SHA1哈希值(要下载的文件块),所以 - 下载后piece - 我将其SHA1哈希值与torrent的元数据文件提供的哈希值进行比较。

以下是处理验证的代码:

 int verify_piece (void* addr, uint64_t piece_length, unsigned char* piece_sha)
 {
     SHA_CTX sha;
     unsigned char sha1result[20];

     SHA1_Init(&sha);
     SHA1_Update(&sha, addr, piece_length);
     SHA1_Final(sha1result, &sha);

     if (memcmp(sha1result, piece_sha, 20)) {
          printf("Expected: ");
          print_sha1(piece_sha);
          printf("Received: ");
          print_sha1(sha1result);
          return 0;
     } else
          return 1;
  }

为了追踪错误,我操纵了两个功能:write_incorrect_piece()write_correct_piece()write_incorrect_piece()失败时会调用第一个函数verify_piece()。它将验证失败的文件写入文件,以便我可以使用hexdump进行检查。

 void write_incorrect_piece (void* piece, uint32_t piece_length, uint32_t index)
 {
     FILE* failed_piece = fopen("failed_piece.out", "w+");

     write(fileno(failed_piece), piece, piece_length);
     fclose(failed_piece);
     printf("ERROR: Wrote piece #%d to failed_piece.out for inspection with hexdump.\n",
            index);

     write_correct_piece(piece_length, index);
     exit(1);
 }

正如您所看到的,write_incorrect_piece()采用参数void* piece,这是指向验证失败的作品的指针,uint32_t piece_length,即作品的长度,uint32_t index 1}}这是验证失败的部分的索引。然后它将不正确的部分复制到名为failed_piece.out的文件中。

您会注意到,在致电exit()之前,write_incorrect_piece()会调用函数write_correct_piece()。这是我写的第二个帮助调试此错误的函数。它需要torrent客户端尝试下载的文件的已知良好版本(known_good.iso),然后将相关部分复制到文件(correct_piece.out),以便我可以将其与包含不正确部分的文件进行比较。

void write_correct_piece (uint32_t piece_length, uint32_t index)
{
     FILE* known_good = fopen("known_good.iso", "r+");
     FILE* correct_piece = fopen("correct_piece.out", "w+");

     /* get file size for MMAPing */
     struct stat st;
     stat("known_good.iso", &st);
     long size = st.st_size;

     void* addr = mmap(0,
                       size,
                       PROT_READ | PROT_WRITE,
                       MAP_SHARED,
                       fileno(known_good),
                       0);

     write(fileno(correct_piece), addr + piece_length * index, piece_length);
     munmap(addr, size);
     fclose(known_good);
     fclose(correct_piece);
}

然后我拿了两个写入的文件并用hexdump格式化它们,这样输出就是人类可读的,然后尝试通过diff比较它们,就像这样

$ hexdump failed_piece.out > failed_piece.dump
$ hexdump correct_piece.out > correct_piece.dump
$ diff correct_piece.dump failed_piece.dump

令我惊讶的是,diff没有输出任何东西。文件完全一样。那么,为什么这个片段的SHA1哈希值不同于torrent文件所期望的那个?也许,我当时认为,元数据文件中的SHA1哈希是不正确的。如果是这种情况,我希望客户端始终无法验证第4条,所以我修改了torrent客户端的调度程序,只是尝试下载第4条,看它是否确实,实际上总是验证失败。

Successfully downloaded piece: #4

跆拳道?现在我陷入了僵局。我认为有几件不同的事情可能导致这种情况:

  • 客户端的网络代码中确实存在错误,但我在write_incorrect_piece()write_correct_piece()中引入了一个错误,因此无论输入的完整性如何,它都会生成两个相同的文件。
  • verify_piece()中存在一个错误,导致其偶尔失败。
  • 我没有考虑过什么。

但是,对于我的生活,我发现任何上述代码都没有任何问题,所以我希望你们好的人可以指出我所缺少的东西。

客户的整个来源位于https://github.com/robertseaton/slug

修改:将strncmp()替换为memcmp()中的verify_piece()并清除了write_incorrect_piece()write_correct_piece()

编辑:以下是我手动对其进行区分时出现故障片段的SHA1哈希示例。如您所见,哈希完全不相似:

Expected: 239 66 216 164 16 120 73 24 1 236 116 173 144 85 243 152 160 165 64 231 
Received: 214 94 49 185 54 159 255 201 214 137 102 23 223 76 102 138 89 94 154 69 
Failed to verify piece: #13

1 个答案:

答案 0 :(得分:1)

好的,如果我们看一下verify_piece()的调用,我们就会看到:

 if (verify_piece(p->torrent->mmap + index * p->torrent->piece_length,
     p->torrent->piece_length, p->torrent->pieces[index].sha1)) {

现在,我们知道第一个和第二个参数是正确的,因为它稍后在程序调用{​​{1}}时重复使用,并且我们已经验证了它的输出是正确的。因此,我们可以专注于第三个参数write_incorrect_piece()

乍一看,参数看起来是正确的,因为我们在整个函数中使用p->torrent->pieces[index].sha1。但是,考虑一下,如果index - 虽然在正确排序的数组上完全有效 - 被用作不再包含假定顺序的数组的数组的索引,该怎么办?

Viola!这是罪魁祸首(在index中):

__schedule()

注释掉对qsort(t->pieces, t->num_pieces, sizeof(struct Piece), rarest); 的调用,客户端按预期运行。