You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中Torrent文件Info哈希计算与Tracker请求异常排查

在Rust中计算Torrent info字典SHA1哈希及Tracker请求问题解决

问题背景

处理.torrent文件时,由于info字典包含无效UTF-8字符,使用serde::bencode反序列化能正常获取Torrent信息,但生成的info_hash用于Tracker GET请求时,始终返回provided invalid infohash错误。

核心问题分析

  1. info_hash计算逻辑错误:若先反序列化整个Torrent结构,再将info字段重新序列化后计算哈希,会改变原始bencode的字节结构(比如键的排序、空白字符等),导致哈希值与Tracker期望的不一致。
  2. URL编码方式错误:将info_hash与其他参数统一传入请求构建方法(如reqwest的query)时,库会把二进制的info_hash当作UTF-8字符串处理,造成编码错误,Tracker无法识别。

解决步骤

1. 正确计算info_hash

必须直接对info字典的原始bencode字节流进行SHA1哈希计算,确保哈希值与Torrent文件原始定义一致:

use serde_bencode::{de::from_bytes, ser::to_bytes, value::Value};
use sha1::{Digest, Sha1};
use std::fs;

fn get_info_hash(torrent_path: &str) -> Result<[u8; 20], Box<dyn std::error::Error>> {
    // 读取torrent文件原始字节数据
    let torrent_data = fs::read(torrent_path)?;
    // 解析为bencode通用Value类型
    let bencode_value: Value = from_bytes(&torrent_data)?;
    
    // 提取info字段并序列化回原始bencode字节(保证与原文件结构完全一致)
    let info_bytes = to_bytes(&bencode_value["info"])?;
    
    // 计算SHA1哈希
    let mut hasher = Sha1::new();
    hasher.update(&info_bytes);
    let hash_result = hasher.finalize();
    
    // 转换为固定长度数组返回
    Ok(hash_result.into())
}

2. 正确构建Tracker请求URL

手动拆分参数编码逻辑,避免info_hash被错误处理:

  • 对除info_hash外的所有参数进行常规URL编码
  • 将info_hash的每个二进制字节转换为%XX的十六进制格式
  • 手动拼接完整请求URL

示例代码:

use reqwest::Client;
use url::form_urlencoded;

fn build_announce_url(
    tracker_base: &str,
    info_hash: [u8; 20],
    peer_id: &str,
    port: u16,
) -> String {
    // 编码普通请求参数
    let encoded_params = form_urlencoded::Serializer::new(String::new())
        .append_pair("peer_id", peer_id)
        .append_pair("port", &port.to_string())
        .append_pair("uploaded", "0")
        .append_pair("downloaded", "0")
        .append_pair("left", "0")
        .append_pair("compact", "1")
        .finish();
    
    // 按字节编码info_hash为%XX格式
    let encoded_info_hash = info_hash
        .iter()
        .map(|byte| format!("%{:02X}", byte))
        .collect::<String>();
    
    // 拼接完整请求URL
    format!("{}?{}&info_hash={}", tracker_base, encoded_params, encoded_info_hash)
}

// 发起Tracker请求示例
async fn fetch_tracker_peers() -> Result<(), Box<dyn std::error::Error>> {
    let info_hash = get_info_hash("example.torrent")?;
    let tracker_url = "http://tracker.example.com/announce";
    let peer_id = "-RUSTTOR-000000000000";
    let announce_url = build_announce_url(tracker_url, info_hash, peer_id, 6881);
    
    let client = Client::new();
    let response = client.get(&announce_url).send().await?;
    let response_body = response.text().await?;
    
    println!("Tracker response: {}", response_body);
    Ok(())
}

关键注意事项

  • 必须使用info字典的原始bencode字节计算哈希,避免反序列化再序列化导致的字节结构变化
  • info_hash是二进制数据,必须按字节逐个编码为%XX格式,不能当作普通字符串处理
  • 手动拼接URL时,确保参数之间的&分隔符正确,避免编码冲突

内容的提问来源于stack exchange,提问作者loki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:53:29