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}