arnaucube
arnaucube/torrent-rsPublic Self-hosted repository
torrent-rs/src/lib.rs
src/lib.rsRaw
  1use async_std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
  2use std::error::Error;
  3use std::net::Ipv4Addr;
  4use std::convert::TryInto;
  5
  6use byteorder::{BigEndian, ByteOrder};
  7use rand::Rng;
  8use sha1::{Sha1, Digest};
  9
 10extern crate serde;
 11extern crate serde_bencode;
 12#[macro_use]
 13extern crate serde_derive;
 14extern crate serde_bytes;
 15
 16use serde_bencode::{de, ser};
 17use serde_bytes::ByteBuf;
 18
 19// use rustc_hex::ToHex;
 20
 21const CONNECT_MSG: u32 = 0;
 22const ANNOUNCE_MSG: u32 = 1;
 23
 24#[derive(Debug, Deserialize)]
 25struct Node(String, i64);
 26
 27#[derive(Debug, Serialize, Deserialize)]
 28struct File {
 29    path: Vec<String>,
 30    length: i64,
 31    #[serde(default)]
 32    md5sum: Option<String>,
 33}
 34
 35#[derive(Debug, Serialize, Deserialize)]
 36struct Info {
 37    name: String,
 38    pieces: ByteBuf,
 39    #[serde(rename = "piece length")]
 40    piece_length: i64,
 41    #[serde(default)]
 42    md5sum: Option<String>,
 43    #[serde(default)]
 44    length: Option<i64>,
 45    #[serde(default)]
 46    files: Option<Vec<File>>,
 47    #[serde(default)]
 48    private: Option<u8>,
 49    #[serde(default)]
 50    path: Option<Vec<String>>,
 51    #[serde(default)]
 52    #[serde(rename = "root hash")]
 53    root_hash: Option<String>,
 54}
 55
 56#[derive(Debug, Deserialize)]
 57struct Torrent {
 58    info: Info,
 59    #[serde(default)]
 60    announce: Option<String>,
 61    #[serde(default)]
 62    nodes: Option<Vec<Node>>,
 63    #[serde(default)]
 64    encoding: Option<String>,
 65    #[serde(default)]
 66    httpseeds: Option<Vec<String>>,
 67    #[serde(default)]
 68    #[serde(rename = "announce-list")]
 69    announce_list: Option<Vec<Vec<String>>>,
 70    #[serde(default)]
 71    #[serde(rename = "creation date")]
 72    creation_date: Option<i64>,
 73    #[serde(rename = "comment")]
 74    comment: Option<String>,
 75    #[serde(default)]
 76    #[serde(rename = "created by")]
 77    created_by: Option<String>,
 78}
 79
 80impl Torrent {
 81    pub fn open(path: &str) -> Torrent {
 82        let b = std::fs::read(path).unwrap();
 83        de::from_bytes::<Torrent>(&b).unwrap()
 84    }
 85    pub fn size(&self) -> u64 {
 86        let files = self.info.files.as_ref().unwrap();
 87        let mut size: u64 = 0;
 88        for i in 0..files.len() {
 89            size = size + files[i].length as u64;
 90        }
 91        size
 92    }
 93    pub fn info_hash(&self) -> Vec<u8> {
 94        let info_bytes: Vec<u8> = ser::to_bytes(&self.info).unwrap();
 95        let mut hasher = Sha1::new();
 96        hasher.update(info_bytes);
 97        let result = hasher.finalize();
 98        result.to_vec()
 99    }
100    pub fn announce(&self) -> Option<String> {
101        let mut a: String = "".to_string();
102        match &self.announce {
103            Some(s) => {
104                a = s.to_string();
105            }
106            _ => (),
107        };
108        match &self.announce_list { // TODO refactor approach
109            Some(list) => {
110                a = list[0][0].clone();
111            }
112            _ => (),
113        };
114        Some(a)
115    }
116    pub fn protocol(&self) -> Option<String> {
117        let announce = &self.announce().unwrap();
118        let aux: Vec<&str> = announce.split(":").collect();
119        let protocol = aux[0];
120        Some(protocol.to_string())
121    }
122    pub async fn get_peers(&self) -> Result<Vec<Peer>, Box<dyn Error>> {
123        // TODO timming system to resend request if no answer in X seconds
124        if self.protocol().unwrap() != "udp" {
125            panic!("not udp: {:?}", self.protocol().unwrap());
126        }
127
128        let peer = self.announce.clone().unwrap().replace("udp://", "");
129        println!("peer {:?}", peer);
130        let socket = UdpSocket::bind("0.0.0.0:6681").await?;
131
132        let conn_req = conn_req_msg().to_vec();
133        println!("SENDING conn_req {:?}", conn_req);
134        socket.send_to(&conn_req, &peer).await?;
135
136        let mut buf = vec![0; 1024];
137
138        let peers = loop {
139            let (n, src) = socket.recv_from(&mut buf).await?;
140
141            let typ = resp_type(&buf);
142            if typ == CONNECT_MSG {
143                println!("TYPE: CONNECT: {:?}", CONNECT_MSG);
144                // println!("HEX {:?}", &buf[0..n].to_hex());
145                let conn_resp = parse_connect_resp(&buf[0..n].to_vec());
146                let announce_req = ann_req_msg(conn_resp.connection_id, self, 6681);
147                socket.send_to(&announce_req[..], &src).await?;
148            } else if typ==ANNOUNCE_MSG {
149                println!("TYPE: ANNOUNCE: {:?}", ANNOUNCE_MSG);
150                // println!("HEX {:?}", &buf[0..n].to_hex());
151                let ann_resp = parse_announce_resp(&buf[0..n].to_vec());
152                break ann_resp.peers;
153            }
154        };
155
156        Ok(peers)
157    }
158}
159
160fn conn_req_msg() -> [u8; 16] {
161    let mut b: [u8; 16] = [0; 16];
162    BigEndian::write_u64(&mut b[0..8], 0x41727101980); // connection_id
163    let random_bytes = rand::thread_rng().gen::<[u8; 4]>();
164    b[12..16].clone_from_slice(&random_bytes[..]);
165    b
166}
167
168fn ann_req_msg(connection_id: u64, torrent: &Torrent, port: u16) -> Vec<u8> {
169    let mut b: [u8; 98] = [0; 98];
170    BigEndian::write_u64(&mut b[0..8], connection_id);
171    BigEndian::write_u32(&mut b[8..12], ANNOUNCE_MSG); // action
172    let random_bytes = rand::thread_rng().gen::<[u8; 4]>();
173    b[12..16].clone_from_slice(&random_bytes[..]);
174    b[16..36].clone_from_slice(&torrent.info_hash()[..]);
175    println!("info_hash: {:?}", &b[16..36]);
176    // TODO [36..56] peerId b[36..56].clone_from_slice("todo".as_bytes());
177    // TODO [56..64] downloaded
178    println!("torrent.size(): {:?}", torrent.size());
179    BigEndian::write_u64(&mut b[64..72], torrent.size()); // [64..72] left
180    // TODO [72..80] uploaded
181    // TODO [80..84] event (0:none, 1:completed, 2:started, 3:stopped)
182    // TODO [84..88] ip address
183    let random_bytes = rand::thread_rng().gen::<[u8; 4]>();
184    b[88..92].clone_from_slice(&random_bytes[..]); // [88..92] key (random)
185    BigEndian::write_i32(&mut b[92..96], -1); // [92..96] num_want (default: -1)
186    BigEndian::write_u16(&mut b[96..98], port); // [96..98] port
187    b.to_vec()
188}
189
190fn resp_type(b: &Vec<u8>) -> u32 {
191    let action = BigEndian::read_u32(&b[0..4]);
192    if action == 0 {
193        return CONNECT_MSG;
194    } else {
195        return ANNOUNCE_MSG;
196    }
197}
198
199#[derive(Debug)]
200struct ConnResp {
201    action: u32,
202    transaction_id: u32,
203    connection_id: u64,
204}
205fn parse_connect_resp(b: &Vec<u8>) -> ConnResp {
206    ConnResp {
207        action: BigEndian::read_u32(&b[0..4]),
208        transaction_id: BigEndian::read_u32(&b[4..8]),
209        connection_id: BigEndian::read_u64(&b[8..]),
210    }
211}
212
213#[derive(Debug)]
214struct Peer {
215    ip: String,
216    port: u16
217}
218
219#[derive(Debug)]
220struct AnnResp {
221    action: u32,
222    transaction_id: u32,
223    interval: u32,
224    leechers: u32,
225    seeders: u32,
226    peers: Vec<Peer>,
227}
228fn parse_announce_resp(b: &Vec<u8>) -> AnnResp {
229    let mut peers: Vec<Peer> = Vec::new();
230    let n_peers = (b.len()-20)/6;
231    for i in 0..n_peers {
232        let peer: Peer = Peer {
233            // ip: BigEndian::read_u32(&b[20+(6*i)..24+(6*i)]),
234            ip: bytes_to_ip(&b[20+(6*i)..24+(6*i)].try_into().expect("err parsing peer ip")),
235            port: BigEndian::read_u16(&b[24+(6*i)..26+(6*i)]),
236        };
237        peers.push(peer);
238    }
239
240    let ann_resp: AnnResp = AnnResp {
241        action: BigEndian::read_u32(&b[0..4]),
242        transaction_id: BigEndian::read_u32(&b[4..8]),
243        interval: BigEndian::read_u32(&b[8..12]),
244        leechers: BigEndian::read_u32(&b[12..16]),
245        seeders: BigEndian::read_u32(&b[16..20]),
246        peers: peers
247    };
248
249    ann_resp
250}
251fn bytes_to_ip(b: &[u8; 4]) -> String {
252    Ipv4Addr::new(b[0], b[1], b[2], b[3]).to_string()
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use rustc_hex::ToHex;
259
260    #[test]
261    fn torrent_structs() {
262        let t = Torrent::open("test.torrent");
263        assert_eq!(t.info.name, "Big Buck Bunny");
264        assert_eq!(t.announce().unwrap(), "udp://tracker.leechers-paradise.org:6969");
265        assert_eq!(t.info_hash().to_hex(), "dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c");
266        assert_eq!(t.size(), 276445467);
267    }
268
269    #[test]
270    fn discovery_msgs() {
271        let t = Torrent::open("test.torrent");
272
273        let conn_req = conn_req_msg().to_vec();
274        assert_eq!(conn_req[0..12].to_hex(), "000004172710198000000000"); // [12..16] is random
275        let conn_resp_buf = b"00000000d4c575c2c078f9a83418bebc";
276        let conn_resp = parse_connect_resp(&conn_resp_buf.to_vec());
277        println!("conn_resp {:?}", conn_resp);
278        assert_eq!(conn_resp.action, 808464432);
279        assert_eq!(conn_resp.transaction_id, 808464432);
280        assert_eq!(conn_resp.connection_id, 7220505182792409906);
281
282        let announce_req = ann_req_msg(conn_resp.connection_id, &t, 6681);
283        assert_eq!(announce_req[0..12].to_hex(), "643463353735633200000001");
284        assert_eq!(announce_req[16..72].to_hex(), "dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c0000000000000000000000000000000000000000000000000000000000000000107a391b");
285        assert_eq!(announce_req[92..98].to_hex(), "ffffffff1a19");
286
287        let announce_resp_buf = b"000000017a638a60000006e8000000030000002429d412a11ae12d53dcbd1a19d048c0e51ae1d8241960c8d5d5c36e71c8d5c5b974a31ae2c11ef348d4aabc5f370ec8d5bc4e16321ae1b9cee13b2327b94187b66f7cb94186b1c8d5b75308331ae1b068c0e0c263ac6f857911389d27ac821ae19088c2c01ae172fe2468c00a699aebe6720f68a2f99a1ae167fce2141ae163582ee41ae1634921a5d2725b72aaf5c8d559482e8fc9d257fb394cc8d55740e9ebd166524044e11aff51b6dcb6c8d54e2ebe61c3574b87860fc8d54b6e11026dff473afc7a1ec944cd47cd1ae131249b051ae124ff6b521ae105b75c13c8d50551153a1ae105024e09cd52";
288        let ann_resp = parse_announce_resp(&announce_resp_buf.to_vec());
289        assert_eq!(ann_resp.peers.len(), 81);
290        assert_eq!(ann_resp.peers[0].ip, "48.54.101.56");
291        assert_eq!(ann_resp.peers[0].port, 12336);
292        assert_eq!(ann_resp.peers[80].ip, "52.101.48.57");
293        assert_eq!(ann_resp.peers[80].port, 25444);
294    }
295    
296    #[async_std::test]
297    async fn get_peers() {
298        let t = Torrent::open("test.torrent");
299        // println!("{:?}", t);
300        println!("torrent: {:?}", t.info.name);
301        println!("protocol: {:?}", t.protocol().unwrap());
302    
303        println!("conn_req_msg: {:?}", conn_req_msg());
304    
305        let r = t.get_peers().await;
306        println!("get_peers r: {:?}", r);
307    }
308}