最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - Getting info hash from .torrrent file - Stack Overflow

programmeradmin4浏览0评论

I dont understand how to sha1 hash the info dictionary of a .torrent info dictionary in rust. I know i cant extract the info dictionary dicrectly cause it has invalid ut8 characters so i use serde::bencode to extract that info.


#[derive(Deserialize,Clone,Debug)]
struct Info {
    name:String, // Name of the torrent file
    #[serde(rename = "piece length")]
    length:usize, // Size of each piece as bytes     
    pieces:ByteBuf, // raw SHA1 Hashes of pieces
    #[serde(flatten)]
    keys:Keys // Can be lenght if its single file torrent or 'files' if its a multi file torrent
}

#[derive(Clone,Debug)]
struct Torrent {
    announce:String,
    raw_info:Vec<u8>,
    info:Info,
}


#[derive(Deserialize,Clone,Debug)]
#[serde(untagged)]
enum Keys{

    SingleFile {
        length:usize
    },
    MultiFile {
        files:Vec<File>
    }
}


#[derive(Deserialize,Clone,Debug)]
struct File{
    length:usize, // File size in bytes
    path:Vec<String> //File path split in directories
}


impl<'de> Deserialize<'de> for Torrent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let mut map = BTreeMap::<String, Value>::deserialize(deserializer)?;

        // Extract announce
        let announce_value = map
            .remove("announce")
            .ok_or_else(|| serde::de::Error::missing_field("announce"))?;

        let announce = match announce_value {
            Value::Bytes(bytes) => String::from_utf8(bytes)
                .map_err(|e| serde::de::Error::custom(e.to_string()))?,
            _ => return Err(serde::de::Error::custom("announce must be a bencoded string")),
        };

        // Extract raw info as bencoded dictionary (not bytes)
        let raw_info_value = map
            .remove("info")
            .ok_or_else(|| serde::de::Error::missing_field("info"))?;

        // Debugging raw_info_value to see the structure
        //println!("Raw info value: {:?}", raw_info_value);

        let raw_info_bytes = match raw_info_value {
            Value::Dict(_) => {
                 // Serialize the `info` dictionary to bencoded bytes
                 serde_bencode::to_bytes(&raw_info_value)
                 .map_err(|e| serde::de::Error::custom(format!("Bencode serialization error: {}", e)))?
            }
            _ => return Err(serde::de::Error::custom("info must be a bencoded dictionary")),
        };

        //println!("raw_info_byts: {:?}",raw_info_bytes);

        // Return the Torrent struct with the correct info_hash
        let info: Info = serde_bencode::from_bytes(&raw_info_bytes)
            .map_err(|e| serde::de::Error::custom(format!("Bencode deserialization error: {}", e)))?;


        Ok(Torrent {
            announce,
            raw_info: raw_info_bytes,
            info,
        })
    }
}

from this i can get the info about the torrent but getting the correct info hash has been a struggle as i cant make torrent get request without the correct info hash. Here is how i have tried it :

fn makequery(path: String) -> TorrentGetRequest {
    let torrent_file = std::fs::read(path).expect("Failed to read torrent file");
    let data: Torrent = serde_bencode::from_bytes(&torrent_file).expect("Parsing failed");

    let url1 = data.announce;

    println!("info hash raw:{:?}",data.raw_info);
    let info_hash = compute_info_hash(&data.raw_info);
    println!("compute info hash:{:?}",info_hash);
    let encoded_info_hash = url_encode_info_hash(&info_hash);
    println!("econded_info_hash:{:?}",encoded_info_hash);

    TorrentGetRequest {
        url: url1,
        info_hash: encoded_info_hash, 
        peer_id: "11111222223333344444".to_string(),
        port: 6881,
        uploaded: 0,
        dowloaded: 0,
        left: 0,
        compact: 1,
    }
}

pub fn compute_info_hash(raw_info: &[u8]) -> [u8; 20] {
    let hash_result = Sha1::digest(raw_info);
    let mut info_hash = [0u8; 20];
    info_hash.copy_from_slice(&hash_result[..20]);
    
    println!("Computed Info Hash (Hex): {}", hex::encode(&info_hash));
    info_hash
}
fn url_encode_info_hash(info_hash: &[u8]) -> String {
    form_urlencoded::byte_serialize(info_hash).collect::<String>()
}

along with my termnial response :

info hash raw:[100, 54, 58, 108, 101, 110, 103, 116, 104, 105, 57, 50, 48, 54, 51, 101, 52, 58, 110, 97, 109, 101, 49, 48, 58, 115, 97, 109, 112, 108, 101, 46, 116, 120, 116, 49, 50, 58, 112, 105, 101, 99, 101, 32, 108, 101, 110, 
103, 116, 104, 105, 51, 50, 55, 54, 56, 101, 54, 58, 112, 105, 101, 99, 101, 115, 54, 48, 58, 232, 118, 246, 122, 42, 136, 134, 232, 243, 107, 19, 103, 38, 195, 15, 162, 151, 3, 2, 45, 110, 34, 117, 230, 4, 160, 118, 102, 86, 115, 110, 129, 255, 16, 181, 82, 4, 173, 141, 53, 240, 13, 147, 122, 2, 19, 223, 25, 130, 188, 141, 9, 114, 39, 173, 158, 144, 154, 204, 23, 101]
Computed Info Hash (Hex): d69f91e6b2ae4c542468d1073a71d4ea13879a7f
compute info hash:[214, 159, 145, 230, 178, 174, 76, 84, 36, 104, 209, 7, 58, 113, 212, 234, 19, 135, 154, 127]
econded_info_hash:"%D6%9F%91%E6%B2%AELT%24h%D1%07%3Aq%D4%EA%13%87%9A%7F"
Url request to 
info hash: %D6%9F%91%E6%B2%AELT%24h%D1%07%3Aq%D4%EA%13%87%9A%7F
Response: d14:failure reason25:provided invalid infohashe

I am not to sure if its how im deserializing the torrent file but i have seen other people take this same approach but mine just doesnt work and im not sure why.

发布评论

评论列表(0)

  1. 暂无评论