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 // TODO [72..80] uploaded
117 // TODO [80..84] event (0:none, 1:completed, 2:started, 3:stopped)
118 // TODO [84..88] ip address
119 let random_bytes = rand::thread_rng().gen::<[u8; 4]>();
120 b[88..92].clone_from_slice(&random_bytes[..]); // [88..92] key (random)
121 BigEndian::write_i32(&mut b[92..96], -1); // [92..96] num_want (default: -1)
122 BigEndian::write_u16(&mut b[96..98], port); // [96..98] port
123 b.to_vec()
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_read() {
132 let t = Torrent::open("test.torrent");
133 println!("torrent: {:?}", t.info.name);
134 println!("announce: {:?}", t.announce.clone().unwrap());
135
136 println!("conn_req_msg: {:?}", conn_req_msg());
137 }
138}