arnaucube
arnaucube/torrent-rsPublic Self-hosted repository
torrent-rs/src/lib.rs
src/lib.rsRaw
  1use byteorder::{BigEndian, ByteOrder};
  2use rand::Rng;
  3use sha1::{Digest, Sha1};
  4
  5extern crate serde;
  6extern crate serde_bencode;
  7#[macro_use]
  8extern crate serde_derive;
  9extern crate serde_bytes;
 10
 11use serde_bencode::{de, ser};
 12use serde_bytes::ByteBuf;
 13
 14#[derive(Debug, Deserialize)]
 15struct Node(String, i64);
 16
 17#[derive(Debug, Serialize, Deserialize)]
 18struct File {
 19    path: Vec<String>,
 20    length: i64,
 21    #[serde(default)]
 22    md5sum: Option<String>,
 23}
 24
 25#[derive(Debug, Serialize, Deserialize)]
 26struct Info {
 27    name: String,
 28    pieces: ByteBuf,
 29    #[serde(rename = "piece length")]
 30    piece_length: i64,
 31    #[serde(default)]
 32    md5sum: Option<String>,
 33    #[serde(default)]
 34    length: Option<i64>,
 35    #[serde(default)]
 36    files: Option<Vec<File>>,
 37    #[serde(default)]
 38    private: Option<u8>,
 39    #[serde(default)]
 40    path: Option<Vec<String>>,
 41    #[serde(default)]
 42    #[serde(rename = "root hash")]
 43    root_hash: Option<String>,
 44}
 45
 46#[derive(Debug, Deserialize)]
 47struct Torrent {
 48    info: Info,
 49    #[serde(default)]
 50    announce: Option<String>,
 51    #[serde(default)]
 52    nodes: Option<Vec<Node>>,
 53    #[serde(default)]
 54    encoding: Option<String>,
 55    #[serde(default)]
 56    httpseeds: Option<Vec<String>>,
 57    #[serde(default)]
 58    #[serde(rename = "announce-list")]
 59    announce_list: Option<Vec<Vec<String>>>,
 60    #[serde(default)]
 61    #[serde(rename = "creation date")]
 62    creation_date: Option<i64>,
 63    #[serde(rename = "comment")]
 64    comment: Option<String>,
 65    #[serde(default)]
 66    #[serde(rename = "created by")]
 67    created_by: Option<String>,
 68}
 69
 70impl Torrent {
 71    pub fn open(path: &str) -> Torrent {
 72        let b = std::fs::read(path).unwrap();
 73        de::from_bytes::<Torrent>(&b).unwrap()
 74    }
 75    pub fn size(&self) -> u64 {
 76        let files = self.info.files.as_ref().unwrap();
 77        let mut size: u64 = 0;
 78        for i in 0..files.len() {
 79            size = size + files[i].length as u64;
 80        }
 81        size
 82    }
 83    pub fn info_hash(&self) -> Vec<u8> {
 84        let info_bytes: Vec<u8> = ser::to_bytes(&self.info).unwrap();
 85        let mut hasher = Sha1::new();
 86        hasher.update(info_bytes);
 87        let result = hasher.finalize();
 88        result.to_vec()
 89    }
 90}
 91
 92const CONNECT_MSG: u32 = 0;
 93const ANNOUNCE_MSG: u32 = 1;
 94
 95fn conn_req_msg() -> [u8; 16] {
 96    let mut b: [u8; 16] = [0; 16];
 97    BigEndian::write_u64(&mut b[0..8], 0x41727101980); // connection_id
 98    let random_bytes = rand::thread_rng().gen::<[u8; 4]>();
 99    b[12..16].clone_from_slice(&random_bytes[..]);
100    b
101}
102
103fn ann_req_msg(connection_id: u64, torrent: &Torrent, port: u16) -> Vec<u8> {
104    let mut b: [u8; 98] = [0; 98];
105    BigEndian::write_u64(&mut b[0..8], connection_id);
106    BigEndian::write_u32(&mut b[8..12], CONNECT_MSG); // action
107    let random_bytes = rand::thread_rng().gen::<[u8; 4]>();
108    b[12..16].clone_from_slice(&random_bytes[..]);
109    // println!("md5sum {:?}", torrent);
110    b[16..36].clone_from_slice(&torrent.info_hash()[..]);
111    println!("info_hash: {:?}", &b[16..36]);
112    // TODO [36..56] peerId b[36..56].clone_from_slice("todo".as_bytes());
113    // TODO [56..64] downloaded
114    println!("torrent.size(): {:?}", torrent.size());
115    BigEndian::write_u64(&mut b[64..72], torrent.size()); // [64..72] left
116
117    // TODO [72..80] uploaded
118    // TODO [80..84] event (0:none, 1:completed, 2:started, 3:stopped)
119    // TODO [84..88] ip address
120
121    let random_bytes = rand::thread_rng().gen::<[u8; 4]>();
122    b[88..92].clone_from_slice(&random_bytes[..]); // [88..92] key (random)
123    BigEndian::write_i32(&mut b[92..96], -1); // [92..96] num_want (default: -1)
124    BigEndian::write_u16(&mut b[96..98], port); // [96..98] port
125    b.to_vec()
126}
127
128fn resp_type(b: &Vec<u8>) -> u32 {
129    let action = BigEndian::read_u32(&b[0..4]);
130    if action == 0 {
131        return CONNECT_MSG;
132    } else {
133        return ANNOUNCE_MSG;
134    }
135}
136
137#[derive(Debug)]
138struct ConnResp {
139    action: u32,
140    transaction_id: u32,
141    connection_id: u64,
142}
143fn parse_connect_resp(b: &Vec<u8>) -> ConnResp {
144    ConnResp {
145        action: BigEndian::read_u32(&b[0..4]),
146        transaction_id: BigEndian::read_u32(&b[4..8]),
147        connection_id: BigEndian::read_u64(&b[8..]),
148    }
149}
150
151#[derive(Debug)]
152struct Peer {
153    ip: u32,
154    port: u32,
155}
156
157#[derive(Debug)]
158struct AnnResp {
159    action: u32,
160    transaction_id: u32,
161    interval: u32,
162    leechers: u32,
163    seeders: u32,
164    peers: Vec<Peer>,
165}
166fn parse_announce_resp(b: &Vec<u8>) -> AnnResp {
167    let mut peers: Vec<Peer> = Vec::new();
168    let n_peers = (b.len() - 20) / 6;
169    for i in 0..n_peers {
170        let peer: Peer = Peer {
171            ip: BigEndian::read_u32(&b[20 + (6 * i)..24 + (6 * i)]),
172            port: BigEndian::read_u32(&b[24 + (6 * i)..26 + (6 * i)]),
173        };
174        peers.push(peer);
175    }
176
177    let ann_resp: AnnResp = AnnResp {
178        action: BigEndian::read_u32(&b[0..4]),
179        transaction_id: BigEndian::read_u32(&b[4..8]),
180        interval: BigEndian::read_u32(&b[8..12]),
181        leechers: BigEndian::read_u32(&b[12..16]),
182        seeders: BigEndian::read_u32(&b[16..20]),
183        peers: peers,
184    };
185
186    ann_resp
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192
193    #[test]
194    fn test_read() {
195        let t = Torrent::open("test.torrent");
196        println!("torrent: {:?}", t.info.name);
197        println!("announce: {:?}", t.announce.clone().unwrap());
198
199        println!("conn_req_msg: {:?}", conn_req_msg());
200    }
201}