├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── TODO.md ├── downloads └── .notempty ├── src ├── decoder.rs ├── download.rs ├── hash.rs ├── ipc.rs ├── listener.rs ├── main.rs ├── metainfo.rs ├── peer_connection.rs ├── request_metadata.rs ├── request_queue.rs ├── tracker.rs └── tracker_response.rs ├── test_data ├── flagfromserver.torrent └── ubuntu-15.04-server-amd64.iso.torrent └── watch /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /downloads 3 | /target 4 | /tmp 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "rusty_torrent" 3 | version = "0.0.1" 4 | dependencies = [ 5 | "bencode 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "getopts 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "hyper 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 8 | "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 9 | "rust-crypto 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", 10 | "url 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)", 11 | ] 12 | 13 | [[package]] 14 | name = "bencode" 15 | version = "0.1.12" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | dependencies = [ 18 | "byteorder 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", 19 | "num 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 20 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 21 | ] 22 | 23 | [[package]] 24 | name = "bitflags" 25 | version = "0.1.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | 28 | [[package]] 29 | name = "byteorder" 30 | version = "0.3.10" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | 33 | [[package]] 34 | name = "cookie" 35 | version = "0.1.21" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | dependencies = [ 38 | "openssl 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 39 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "time 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "url 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)", 42 | ] 43 | 44 | [[package]] 45 | name = "gcc" 46 | version = "0.3.8" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | 49 | [[package]] 50 | name = "getopts" 51 | version = "0.2.11" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | dependencies = [ 54 | "log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 55 | ] 56 | 57 | [[package]] 58 | name = "httparse" 59 | version = "0.1.4" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | 62 | [[package]] 63 | name = "hyper" 64 | version = "0.5.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | dependencies = [ 67 | "cookie 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "httparse 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 69 | "log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "mime 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "num_cpus 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "openssl 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "time 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 77 | "unicase 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "url 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)", 79 | ] 80 | 81 | [[package]] 82 | name = "lazy_static" 83 | version = "0.1.11" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | 86 | [[package]] 87 | name = "libc" 88 | version = "0.1.8" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | 91 | [[package]] 92 | name = "libressl-pnacl-sys" 93 | version = "2.1.5" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | dependencies = [ 96 | "pnacl-build-helper 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 97 | ] 98 | 99 | [[package]] 100 | name = "log" 101 | version = "0.3.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | dependencies = [ 104 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 105 | ] 106 | 107 | [[package]] 108 | name = "matches" 109 | version = "0.1.2" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | 112 | [[package]] 113 | name = "mime" 114 | version = "0.0.11" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | dependencies = [ 117 | "log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 118 | ] 119 | 120 | [[package]] 121 | name = "num" 122 | version = "0.1.25" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | dependencies = [ 125 | "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 126 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 127 | ] 128 | 129 | [[package]] 130 | name = "num_cpus" 131 | version = "0.2.6" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | dependencies = [ 134 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "openssl" 139 | version = "0.6.2" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 143 | "lazy_static 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "openssl-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 146 | ] 147 | 148 | [[package]] 149 | name = "openssl-sys" 150 | version = "0.6.2" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | dependencies = [ 153 | "gcc 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 154 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "libressl-pnacl-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 156 | "pkg-config 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 157 | ] 158 | 159 | [[package]] 160 | name = "pkg-config" 161 | version = "0.3.4" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | 164 | [[package]] 165 | name = "pnacl-build-helper" 166 | version = "1.4.0" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | dependencies = [ 169 | "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 170 | ] 171 | 172 | [[package]] 173 | name = "rand" 174 | version = "0.3.8" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | dependencies = [ 177 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 178 | ] 179 | 180 | [[package]] 181 | name = "rust-crypto" 182 | version = "0.2.31" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | dependencies = [ 185 | "gcc 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 186 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 187 | "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 189 | "time 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 190 | ] 191 | 192 | [[package]] 193 | name = "rustc-serialize" 194 | version = "0.3.15" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | 197 | [[package]] 198 | name = "tempdir" 199 | version = "0.3.4" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | dependencies = [ 202 | "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 203 | ] 204 | 205 | [[package]] 206 | name = "time" 207 | version = "0.1.26" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | dependencies = [ 210 | "gcc 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 212 | ] 213 | 214 | [[package]] 215 | name = "traitobject" 216 | version = "0.0.1" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | 219 | [[package]] 220 | name = "typeable" 221 | version = "0.1.2" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | 224 | [[package]] 225 | name = "unicase" 226 | version = "0.1.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | 229 | [[package]] 230 | name = "url" 231 | version = "0.2.35" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | dependencies = [ 234 | "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 235 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 236 | ] 237 | 238 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusty_torrent" 3 | description = "A BitTorrent client, written in Rust." 4 | repository = "https://github.com/kenpratt/rusty_torrent" 5 | version = "0.0.1" 6 | readme = "README.md" 7 | keywords = ["bittorrent"] 8 | authors = ["ken@kenpratt.net", "pietromenna@yahoo.com"] 9 | 10 | 11 | [dependencies] 12 | bencode = "0.1" 13 | getopts = "0.2" 14 | hyper = "0.5" 15 | rand = "0.3" 16 | rust-crypto = "0.2" 17 | url = "0.2" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ken Pratt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RustyTorrent 2 | ============ 3 | 4 | A BitTorrent client, written in Rust. 5 | 6 | It supports: 7 | 8 | * Reading `.torrent` files (single-file torrents only) 9 | * Connecting to a tracker to discover peers 10 | * Downloading a file from multiple peers in parallel 11 | * Queueing multiple requests with each peer for faster downloading (aka pipelining) 12 | * Uploading files to peers, and seeding existing files from disk 13 | * Resuming partial downloads 14 | * Verification of correctness of downloaded chunks 15 | 16 | Not yet: 17 | 18 | * Multi-file torrents 19 | * Connecting to multiple trackers 20 | * Upload throttling/congestion control 21 | * NAT traversal 22 | 23 | Requirements 24 | ------------ 25 | 26 | * Rust 1.0.0 or later 27 | 28 | Usage 29 | ----- 30 | 31 | Download and install Rust 1.0 from http://www.rust-lang.org/install.html. 32 | 33 | Clone the repository: 34 | 35 | git clone git@github.com:kenpratt/rusty_torrent.git 36 | cd rusty_torrent 37 | 38 | To run: 39 | 40 | cargo run path/to/myfile.torrent 41 | 42 | To run specifying a port to listen on: 43 | 44 | cargo run -- -p 3333 path/to/myfile.torrent 45 | 46 | Your file will be saved in the `downloads/` directory. 47 | 48 | To build and run an optimized version (will enable significantly faster downloads): 49 | 50 | cargo run --release -- path/to/myfile.torrent 51 | cargo run --release -- -p 3333 path/to/myfile.torrent 52 | 53 | To watch for changes and auto-rebuild (on OS X): 54 | 55 | gem install kicker -s http://gemcutter.org 56 | ./watch 57 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * Benchmark CPU usage to try to figure out why we use ~100% while writing files. 2 | * Implement "rarest-first" strategy where peers will prioritize files that they have that not many other peers do. 3 | * Support torrents with multiple files. 4 | * Support torrents with multiple trackers. 5 | * Announce each X minutes to Tracker that you have a file. 6 | * Announce to tracker when file completes. 7 | * Instead of closing peer when Download completes, close it when neither peer is interested anymore? 8 | * Only verify the file if it already existed on boot. 9 | * Put file writing in a thread? (Measure time taken waiting for locks to see if this is delaying the PeerConnections.) 10 | * Parallelize hash verification? 11 | * Prioritize completing partially-completed pieces, to spread out hash calculation. 12 | * With to_request changes, corrupted blocks won't get re-queued. 13 | -------------------------------------------------------------------------------- /downloads/.notempty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenpratt/rusty_torrent/c17a3e2e258d360159c9a9c01a35dba609064990/downloads/.notempty -------------------------------------------------------------------------------- /src/decoder.rs: -------------------------------------------------------------------------------- 1 | #![macro_use] 2 | 3 | use bencode; 4 | use std::{convert, io}; 5 | 6 | #[macro_export] 7 | macro_rules! get_field_with_default { 8 | ($m:expr, $field:expr, $default:expr) => ( 9 | match $m.get(&ByteString::from_str($field)) { 10 | Some(a) => try!(FromBencode::from_bencode(a)), 11 | None => $default 12 | } 13 | ) 14 | } 15 | 16 | #[macro_export] 17 | macro_rules! get_field { 18 | ($m:expr, $field:expr) => ( 19 | get_field_with_default!($m, $field, return Err(decoder::Error::DoesntContain($field))) 20 | ) 21 | } 22 | 23 | #[macro_export] 24 | macro_rules! get_optional_field { 25 | ($m:expr, $field:expr) => ( 26 | get_field_with_default!($m, $field, None) 27 | ) 28 | } 29 | 30 | #[macro_export] 31 | macro_rules! get_raw_field { 32 | ($m:expr, $field:expr) => ( 33 | match $m.get(&ByteString::from_str($field)) { 34 | Some(a) => a, 35 | None => return Err(decoder::Error::DoesntContain($field)) 36 | } 37 | ) 38 | } 39 | 40 | #[macro_export] 41 | macro_rules! get_field_as_bencoded_bytes { 42 | ($m:expr, $field:expr) => ( 43 | try!(get_raw_field!($m, $field).to_bytes()) 44 | ) 45 | } 46 | 47 | #[macro_export] 48 | macro_rules! get_field_as_bytes { 49 | ($m:expr, $field:expr) => ( 50 | match get_raw_field!($m, $field) { 51 | &Bencode::ByteString(ref v) => v.clone(), 52 | _ => return Err(decoder::Error::NotAByteString) 53 | } 54 | ) 55 | } 56 | 57 | #[derive(Debug)] 58 | pub enum Error { 59 | IoError(io::Error), 60 | DecodingError(bencode::streaming::Error), 61 | NotADict, 62 | NotAByteString, 63 | DoesntContain(&'static str), 64 | NotANumber(bencode::NumFromBencodeError), 65 | NotAString(bencode::StringFromBencodeError), 66 | } 67 | 68 | impl convert::From for Error { 69 | fn from(err: io::Error) -> Error { 70 | Error::IoError(err) 71 | } 72 | } 73 | 74 | impl convert::From for Error { 75 | fn from(err: bencode::streaming::Error) -> Error { 76 | Error::DecodingError(err) 77 | } 78 | } 79 | 80 | impl convert::From for Error { 81 | fn from(err: bencode::NumFromBencodeError) -> Error { 82 | Error::NotANumber(err) 83 | } 84 | } 85 | 86 | impl convert::From for Error { 87 | fn from(err: bencode::StringFromBencodeError) -> Error { 88 | Error::NotAString(err) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/download.rs: -------------------------------------------------------------------------------- 1 | use hash::{calculate_sha1, Sha1}; 2 | use std::{convert, io}; 3 | use std::fs::{File, OpenOptions}; 4 | use std::io::{Read, Seek, Write}; 5 | use std::path::Path; 6 | use std::sync::mpsc::{Sender, SendError}; 7 | 8 | use ipc::IPC; 9 | use metainfo::Metainfo; 10 | use request_metadata::RequestMetadata; 11 | 12 | pub const BLOCK_SIZE: u32 = 16384; 13 | 14 | pub struct Download { 15 | pub our_peer_id: String, 16 | pub metainfo: Metainfo, 17 | pieces: Vec, 18 | file: File, 19 | peer_channels: Vec>, 20 | } 21 | 22 | impl Download { 23 | pub fn new(our_peer_id: String, metainfo: Metainfo) -> Result { 24 | let file_length = metainfo.info.length; 25 | let piece_length = metainfo.info.piece_length; 26 | let num_pieces = metainfo.info.num_pieces; 27 | 28 | // create/open file 29 | let path = Path::new("downloads").join(&metainfo.info.name); 30 | let mut file = try!(OpenOptions::new().create(true).read(true).write(true).open(path)); 31 | 32 | // create pieces 33 | let mut pieces = vec![]; 34 | for i in 0..num_pieces { 35 | let offset = i as u64 * piece_length as u64; 36 | let length = if i < (num_pieces - 1) { 37 | piece_length 38 | } else { 39 | (file_length - offset) as u32 40 | }; 41 | let mut piece = Piece::new(length, offset, metainfo.info.pieces[i as usize].clone()); 42 | try!(piece.verify(&mut file)); 43 | pieces.push(piece); 44 | } 45 | 46 | Ok(Download { 47 | our_peer_id: our_peer_id, 48 | metainfo: metainfo, 49 | pieces: pieces, 50 | file: file, 51 | peer_channels: vec![], 52 | }) 53 | } 54 | 55 | pub fn register_peer(&mut self, channel: Sender) { 56 | self.peer_channels.push(channel); 57 | } 58 | 59 | pub fn store(&mut self, piece_index: u32, block_index: u32, data: Vec) -> Result<(), Error> { 60 | { 61 | let piece = &mut self.pieces[piece_index as usize]; 62 | if piece.is_complete || piece.has_block(block_index) { 63 | // if we already have this block, do an early return to avoid re-writing the piece, sending complete messages, etc 64 | return Ok(()) 65 | } 66 | try!(piece.store(&mut self.file, block_index, data)); 67 | } 68 | 69 | // notify peers that this block is complete 70 | self.broadcast(IPC::BlockComplete(piece_index, block_index)); 71 | 72 | // notify peers if piece is complete 73 | if self.pieces[piece_index as usize].is_complete { 74 | self.broadcast(IPC::PieceComplete(piece_index)); 75 | } 76 | 77 | // notify peers if download is complete 78 | if self.is_complete() { 79 | println!("Download complete"); 80 | self.broadcast(IPC::DownloadComplete); 81 | } 82 | 83 | Ok(()) 84 | } 85 | 86 | pub fn retrive_data(&mut self, request: &RequestMetadata) -> Result, Error> { 87 | let ref piece = self.pieces[request.piece_index as usize]; 88 | if piece.is_complete { 89 | let offset = piece.offset + request.offset as u64; 90 | let file = &mut self.file; 91 | try!(file.seek(io::SeekFrom::Start(offset))); 92 | let mut buf = vec![]; 93 | try!(file.take(request.block_length as u64).read_to_end(&mut buf)); 94 | Ok(buf) 95 | } else { 96 | Err(Error::MissingPieceData) 97 | } 98 | 99 | } 100 | 101 | pub fn have_pieces(&self) -> Vec { 102 | self.pieces.iter().map(|p| p.is_complete).collect() 103 | } 104 | 105 | pub fn incomplete_blocks_for_piece(&self, piece_index: u32) -> Vec<(u32,u32)> { 106 | let ref piece = self.pieces[piece_index as usize]; 107 | if !piece.is_complete { 108 | piece.blocks.iter().filter(|b| !b.is_complete).map(|b| (b.index, b.length)).collect() 109 | } else { 110 | vec![] 111 | } 112 | } 113 | 114 | fn is_complete(&self) -> bool { 115 | for piece in self.pieces.iter() { 116 | if !piece.is_complete { 117 | return false 118 | } 119 | } 120 | true 121 | } 122 | 123 | fn broadcast(&mut self, ipc: IPC) { 124 | self.peer_channels.retain(|channel| { 125 | match channel.send(ipc.clone()) { 126 | Ok(_) => true, 127 | Err(SendError(_)) => false // presumably channel has disconnected 128 | } 129 | }); 130 | } 131 | } 132 | 133 | struct Piece { 134 | length: u32, 135 | offset: u64, 136 | hash: Sha1, 137 | blocks: Vec, 138 | is_complete: bool, 139 | } 140 | 141 | impl Piece { 142 | fn new(length: u32, offset: u64, hash: Sha1) -> Piece { 143 | // create blocks 144 | let mut blocks = vec![]; 145 | let num_blocks = (length as f64 / BLOCK_SIZE as f64).ceil() as u32; 146 | for i in 0..num_blocks { 147 | let len = if i < (num_blocks - 1) { 148 | BLOCK_SIZE 149 | } else { 150 | length - (BLOCK_SIZE * (num_blocks - 1)) 151 | }; 152 | blocks.push(Block::new(i, len)); 153 | } 154 | 155 | Piece { 156 | length: length, 157 | offset: offset, 158 | hash: hash, 159 | blocks: blocks, 160 | is_complete: false, 161 | } 162 | } 163 | 164 | fn store(&mut self, file: &mut File, block_index: u32, data: Vec) -> Result<(), Error> { 165 | { 166 | // store data in the appropriate point in the file 167 | let offset = self.offset + (block_index * BLOCK_SIZE) as u64; 168 | try!(file.seek(io::SeekFrom::Start(offset))); 169 | try!(file.write_all(&data)); 170 | self.blocks[block_index as usize].is_complete = true; 171 | } 172 | 173 | if self.has_all_blocks() { 174 | let valid = try!(self.verify(file)); 175 | if !valid { 176 | self.reset_blocks(); 177 | } 178 | } 179 | 180 | Ok(()) 181 | } 182 | 183 | fn verify(&mut self, file: &mut File) -> Result { 184 | // read in the part of the file corresponding to the piece 185 | try!(file.seek(io::SeekFrom::Start(self.offset))); 186 | let mut data = vec![]; 187 | try!(file.take(self.length as u64).read_to_end(&mut data)); 188 | 189 | // calculate the hash, verify it, and update is_complete 190 | self.is_complete = self.hash == calculate_sha1(&data); 191 | Ok(self.is_complete) 192 | } 193 | 194 | fn has_block(&self, block_index: u32) -> bool { 195 | self.blocks[block_index as usize].is_complete 196 | } 197 | 198 | fn has_all_blocks(&self) -> bool { 199 | for block in self.blocks.iter() { 200 | if !block.is_complete { 201 | return false; 202 | } 203 | } 204 | true 205 | } 206 | 207 | fn reset_blocks(&mut self) { 208 | for block in self.blocks.iter_mut() { 209 | block.is_complete = false; 210 | } 211 | } 212 | } 213 | 214 | struct Block { 215 | index: u32, 216 | length: u32, 217 | is_complete: bool, 218 | } 219 | 220 | impl Block { 221 | fn new(index: u32, length: u32) -> Block { 222 | Block { 223 | index: index, 224 | length: length, 225 | is_complete: false, 226 | } 227 | } 228 | } 229 | 230 | #[derive(Debug)] 231 | pub enum Error { 232 | MissingPieceData, 233 | IoError(io::Error), 234 | } 235 | 236 | impl convert::From for Error { 237 | fn from(err: io::Error) -> Error { 238 | Error::IoError(err) 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/hash.rs: -------------------------------------------------------------------------------- 1 | extern crate crypto; 2 | 3 | use self::crypto::digest::Digest; 4 | 5 | pub type Sha1 = Vec; 6 | 7 | pub fn calculate_sha1(input: &[u8]) -> Sha1 { 8 | let mut hasher = crypto::sha1::Sha1::new(); 9 | hasher.input(input); 10 | 11 | let mut buf: Vec = vec![0; hasher.output_bytes()]; 12 | hasher.result(&mut buf); 13 | buf 14 | } 15 | -------------------------------------------------------------------------------- /src/ipc.rs: -------------------------------------------------------------------------------- 1 | use peer_connection::Message; 2 | 3 | #[derive(Clone)] 4 | pub enum IPC { 5 | BlockComplete(u32, u32), 6 | PieceComplete(u32), 7 | DownloadComplete, 8 | Message(Message), 9 | BlockUploaded, 10 | } 11 | -------------------------------------------------------------------------------- /src/listener.rs: -------------------------------------------------------------------------------- 1 | use std::net::{TcpListener, TcpStream}; 2 | use std::sync::{Arc, Mutex}; 3 | use std::thread; 4 | use std::thread::JoinHandle; 5 | 6 | use download::Download; 7 | use peer_connection; 8 | 9 | pub fn start(port: u16, download_mutex: Arc>) -> JoinHandle<()> { 10 | let tcp_listener = TcpListener::bind(("0.0.0.0", port)).unwrap(); 11 | thread::spawn(move || { 12 | for stream in tcp_listener.incoming() { 13 | match stream { 14 | Ok(s) => handle_connection(s, download_mutex.clone()), 15 | Err(e) => println!("Error: {:?}", e) 16 | } 17 | } 18 | }) 19 | } 20 | 21 | fn handle_connection(stream: TcpStream, download_mutex: Arc>) { 22 | thread::spawn(move || { 23 | match peer_connection::accept(stream, download_mutex) { 24 | Ok(_) => println!("Peer done"), 25 | Err(e) => println!("Error: {:?}", e) 26 | } 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate bencode; 2 | extern crate getopts; 3 | extern crate rand; 4 | 5 | mod decoder; 6 | mod download; 7 | mod hash; 8 | mod ipc; 9 | mod listener; 10 | mod metainfo; 11 | mod peer_connection; 12 | mod request_metadata; 13 | mod request_queue; 14 | mod tracker; 15 | mod tracker_response; 16 | 17 | use getopts::Options; 18 | use rand::Rng; 19 | use std::{any, convert, env, process, thread}; 20 | use std::sync::{Arc, Mutex}; 21 | use std::thread::JoinHandle; 22 | 23 | use download::Download; 24 | 25 | const PEER_ID_PREFIX: &'static str = "-RC0001-"; 26 | 27 | fn main() { 28 | // parse command-line arguments & options 29 | let args: Vec = env::args().collect(); 30 | let program = &args[0]; 31 | let mut opts = Options::new(); 32 | opts.optopt("p", "port", "set listen port to", "6881"); 33 | opts.optflag("h", "help", "print this help menu"); 34 | let matches = match opts.parse(&args[1..]) { 35 | Ok(m) => { m } 36 | Err(f) => { panic!(f.to_string()) } 37 | }; 38 | 39 | if matches.opt_present("h") { 40 | print_usage(&program, opts); 41 | return; 42 | } 43 | 44 | let port = match matches.opt_str("p") { 45 | Some(port_string) => { 46 | let port: Result = port_string.parse(); 47 | match port { 48 | Ok(p) => p, 49 | Err(_) => return abort(&program, opts, format!("Bad port number: {}", port_string)) 50 | } 51 | }, 52 | None => 6881 53 | }; 54 | 55 | let rest = matches.free; 56 | if rest.len() != 1 { 57 | abort(&program, opts, format!("You must provide exactly 1 argument to rusty_torrent: {:?}", rest)) 58 | } 59 | 60 | let filename = &rest[0]; 61 | match run(filename, port) { 62 | Ok(_) => {}, 63 | Err(e) => println!("Error: {:?}", e) 64 | } 65 | } 66 | 67 | fn print_usage(program: &str, opts: Options) { 68 | let brief = format!("Usage: {} [options] path/to/myfile.torrent", program); 69 | print!("{}", opts.usage(&brief)); 70 | } 71 | 72 | fn abort(program: &str, opts: Options, err: String) { 73 | println!("{}", err); 74 | print_usage(program, opts); 75 | process::exit(1); 76 | } 77 | 78 | fn run(filename: &str, listener_port: u16) -> Result<(), Error> { 79 | let our_peer_id = generate_peer_id(); 80 | println!("Using peer id: {}", our_peer_id); 81 | 82 | // parse .torrent file 83 | let metainfo = try!(metainfo::parse(filename)); 84 | 85 | // connect to tracker and download list of peers 86 | let peers = try!(tracker::get_peers(&our_peer_id, &metainfo, listener_port)); 87 | println!("Found {} peers", peers.len()); 88 | 89 | // create the download metadata object and stuff it inside a reference-counted mutex 90 | let download = try!(Download::new(our_peer_id, metainfo)); 91 | let download_mutex = Arc::new(Mutex::new(download)); 92 | 93 | // spawn thread to listen for incoming request 94 | listener::start(listener_port, download_mutex.clone()); 95 | 96 | // spawn threads to connect to peers and start the download 97 | let peer_threads: Vec> = peers.into_iter().map(|peer| { 98 | let mutex = download_mutex.clone(); 99 | thread::spawn(move || { 100 | match peer_connection::connect(&peer, mutex) { 101 | Ok(_) => println!("Peer done"), 102 | Err(e) => println!("Error: {:?}", e) 103 | } 104 | }) 105 | }).collect(); 106 | 107 | // wait for peers to complete 108 | for thr in peer_threads { 109 | try!(thr.join()); 110 | } 111 | 112 | Ok(()) 113 | } 114 | 115 | fn generate_peer_id() -> String { 116 | let mut rng = rand::thread_rng(); 117 | let rand_chars: String = rng.gen_ascii_chars().take(20 - PEER_ID_PREFIX.len()).collect(); 118 | format!("{}{}", PEER_ID_PREFIX, rand_chars) 119 | } 120 | 121 | #[derive(Debug)] 122 | pub enum Error { 123 | DecoderError(decoder::Error), 124 | DownloadError(download::Error), 125 | TrackerError(tracker::Error), 126 | Any(Box), 127 | } 128 | 129 | impl convert::From for Error { 130 | fn from(err: decoder::Error) -> Error { 131 | Error::DecoderError(err) 132 | } 133 | } 134 | 135 | impl convert::From for Error { 136 | fn from(err: download::Error) -> Error { 137 | Error::DownloadError(err) 138 | } 139 | } 140 | 141 | impl convert::From for Error { 142 | fn from(err: tracker::Error) -> Error { 143 | Error::TrackerError(err) 144 | } 145 | } 146 | 147 | impl convert::From> for Error { 148 | fn from(err: Box) -> Error { 149 | Error::Any(err) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/metainfo.rs: -------------------------------------------------------------------------------- 1 | use bencode; 2 | use bencode::{Bencode, FromBencode}; 3 | use bencode::util::ByteString; 4 | use std::fs::File; 5 | use std::io::Read; 6 | 7 | use decoder; 8 | use hash::{calculate_sha1, Sha1}; 9 | 10 | #[derive(PartialEq, Debug)] 11 | pub struct Metainfo { 12 | pub announce: String, 13 | pub info: Info, 14 | pub info_hash: Vec, 15 | pub created_by: String, 16 | } 17 | 18 | impl FromBencode for Metainfo { 19 | type Err = decoder::Error; 20 | 21 | fn from_bencode(bencode: &bencode::Bencode) -> Result { 22 | match bencode { 23 | &Bencode::Dict(ref m) => { 24 | let info_bytes = get_field_as_bencoded_bytes!(m, "info"); 25 | let info_hash = calculate_sha1(&info_bytes); 26 | 27 | let metainfo = Metainfo{ 28 | announce: get_field!(m, "announce"), 29 | info: get_field!(m, "info"), 30 | info_hash: info_hash, 31 | created_by: get_field_with_default!(m, "created by", "".to_string()), 32 | }; 33 | Ok(metainfo) 34 | } 35 | _ => Err(decoder::Error::NotADict) 36 | } 37 | } 38 | } 39 | 40 | #[derive(PartialEq, Debug)] 41 | pub struct Info { 42 | pub piece_length: u32, 43 | pub pieces: Vec, 44 | pub num_pieces: u32, 45 | pub name: String, 46 | pub length: u64, 47 | } 48 | 49 | impl FromBencode for Info { 50 | type Err = decoder::Error; 51 | 52 | fn from_bencode(bencode: &bencode::Bencode) -> Result { 53 | match bencode { 54 | &Bencode::Dict(ref m) => { 55 | let pieces_bytes = get_field_as_bytes!(m, "pieces"); 56 | let pieces: Vec = pieces_bytes.chunks(20).map(|v| v.to_owned()).collect(); 57 | let num_pieces = pieces.len() as u32; 58 | 59 | let info = Info { 60 | piece_length: get_field!(m, "piece length"), 61 | pieces: pieces, 62 | num_pieces: num_pieces, 63 | name: get_field!(m, "name"), 64 | length: get_field!(m, "length"), 65 | }; 66 | Ok(info) 67 | } 68 | _ => Err(decoder::Error::NotADict) 69 | } 70 | } 71 | } 72 | 73 | pub fn parse(filename: &str) -> Result { 74 | println!("Loading {}", filename); 75 | 76 | // read the torrent file into a byte vector 77 | let mut f = try!(File::open(filename)); 78 | let mut v = Vec::new(); 79 | try!(f.read_to_end(&mut v)); 80 | 81 | // decode the byte vector into a struct 82 | let bencode = try!(bencode::from_vec(v)); 83 | let result = try!(FromBencode::from_bencode(&bencode)); 84 | 85 | Ok(result) 86 | } 87 | -------------------------------------------------------------------------------- /src/peer_connection.rs: -------------------------------------------------------------------------------- 1 | use rand; 2 | use rand::Rng; 3 | use std::{any, convert, fmt, io, thread}; 4 | use std::collections::HashMap; 5 | use std::io::{Read, Write}; 6 | use std::net::{Shutdown, TcpStream}; 7 | use std::sync::{Arc, Mutex}; 8 | use std::sync::mpsc::{channel, Receiver, RecvError, Sender, SendError}; 9 | 10 | use download; 11 | use download::{BLOCK_SIZE, Download}; 12 | use ipc::IPC; 13 | use tracker_response::Peer; 14 | use request_queue::RequestQueue; 15 | 16 | const PROTOCOL: &'static str = "BitTorrent protocol"; 17 | const MAX_CONCURRENT_REQUESTS: u32 = 10; 18 | 19 | pub fn connect(peer: &Peer, download_mutex: Arc>) -> Result<(), Error> { 20 | PeerConnection::connect(peer, download_mutex) 21 | } 22 | 23 | pub fn accept(stream: TcpStream, download_mutex: Arc>) -> Result<(), Error> { 24 | PeerConnection::accept(stream, download_mutex) 25 | } 26 | 27 | pub struct PeerConnection { 28 | halt: bool, 29 | download_mutex: Arc>, 30 | stream: TcpStream, 31 | me: PeerMetadata, 32 | them: PeerMetadata, 33 | incoming_tx: Sender, 34 | outgoing_tx: Sender, 35 | upload_in_progress: bool, 36 | to_request: HashMap<(u32, u32), (u32, u32, u32)>, 37 | } 38 | 39 | impl PeerConnection { 40 | fn connect(peer: &Peer, download_mutex: Arc>) -> Result<(), Error> { 41 | println!("Connecting to {}:{}", peer.ip, peer.port); 42 | let stream = try!(TcpStream::connect((peer.ip, peer.port))); 43 | PeerConnection::new(stream, download_mutex, true) 44 | } 45 | 46 | fn accept(stream: TcpStream, download_mutex: Arc>) -> Result<(), Error> { 47 | println!("Received connection from a peer!"); 48 | PeerConnection::new(stream, download_mutex, false) 49 | } 50 | 51 | fn new(stream: TcpStream, download_mutex: Arc>, send_handshake_first: bool) -> Result<(), Error> { 52 | let have_pieces = { 53 | let download = download_mutex.lock().unwrap(); 54 | download.have_pieces() 55 | }; 56 | let num_pieces = have_pieces.len(); 57 | 58 | // create & register incoming IPC channel with Download 59 | let (incoming_tx, incoming_rx) = channel::(); 60 | { 61 | let mut download = download_mutex.lock().unwrap(); 62 | download.register_peer(incoming_tx.clone()); 63 | } 64 | 65 | // create outgoing Message channel 66 | let (outgoing_tx, outgoing_rx) = channel::(); 67 | 68 | let conn = PeerConnection { 69 | halt: false, 70 | download_mutex: download_mutex, 71 | stream: stream, 72 | me: PeerMetadata::new(have_pieces), 73 | them: PeerMetadata::new(vec![false; num_pieces]), 74 | incoming_tx: incoming_tx, 75 | outgoing_tx: outgoing_tx, 76 | upload_in_progress: false, 77 | to_request: HashMap::new(), 78 | }; 79 | 80 | try!(conn.run(send_handshake_first, incoming_rx, outgoing_rx)); 81 | 82 | println!("Disconnected"); 83 | Ok(()) 84 | } 85 | 86 | fn run(mut self, send_handshake_first: bool, incoming_rx: Receiver, outgoing_rx: Receiver) -> Result<(), Error> { 87 | if send_handshake_first { 88 | try!(self.send_handshake()); 89 | try!(self.receive_handshake()); 90 | } else { 91 | try!(self.receive_handshake()); 92 | try!(self.send_handshake()); 93 | } 94 | 95 | println!("Handshake complete"); 96 | 97 | // spawn a thread to funnel incoming messages from the socket into the incoming message channel 98 | let downstream_funnel_thread = { 99 | let stream = self.stream.try_clone().unwrap(); 100 | let tx = self.incoming_tx.clone(); 101 | thread::spawn(move || DownstreamMessageFunnel::start(stream, tx)) 102 | }; 103 | 104 | // spawn a thread to funnel outgoing messages from the outgoing message channel into the socket 105 | let upstream_funnel_thread = { 106 | let stream = self.stream.try_clone().unwrap(); 107 | let tx = self.incoming_tx.clone(); 108 | thread::spawn(move || UpstreamMessageFunnel::start(stream, outgoing_rx, tx)) 109 | }; 110 | 111 | // send a bitfield message letting peer know what we have 112 | try!(self.send_bitfield()); 113 | 114 | // process messages received on the channel (both from the remote peer, and from Downlad) 115 | while !self.halt { 116 | let message = try!(incoming_rx.recv()); 117 | try!(self.process(message)); 118 | } 119 | 120 | println!("Disconnecting"); 121 | try!(self.stream.shutdown(Shutdown::Both)); 122 | try!(downstream_funnel_thread.join()); 123 | try!(upstream_funnel_thread.join()); 124 | Ok(()) 125 | } 126 | 127 | fn send_handshake(&mut self) -> Result<(), Error> { 128 | let message = { 129 | let download = self.download_mutex.lock().unwrap(); 130 | let mut message = vec![]; 131 | message.push(PROTOCOL.len() as u8); 132 | message.extend(PROTOCOL.bytes()); 133 | message.extend(vec![0; 8].into_iter()); 134 | message.extend(download.metainfo.info_hash.iter().cloned()); 135 | message.extend(download.our_peer_id.bytes()); 136 | message 137 | }; 138 | try!(self.stream.write_all(&message)); 139 | Ok(()) 140 | } 141 | 142 | fn receive_handshake(&mut self) -> Result<(), Error> { 143 | let pstrlen = try!(read_n(&mut self.stream, 1)); 144 | try!(read_n(&mut self.stream, pstrlen[0] as u32)); // ignore pstr 145 | try!(read_n(&mut self.stream, 8)); // ignore reserved 146 | let info_hash = try!(read_n(&mut self.stream, 20)); 147 | let peer_id = try!(read_n(&mut self.stream, 20)); 148 | 149 | { 150 | let download = self.download_mutex.lock().unwrap(); 151 | 152 | // validate info hash 153 | if info_hash != download.metainfo.info_hash { 154 | return Err(Error::InvalidInfoHash); 155 | } 156 | 157 | // validate peer id 158 | let our_peer_id: Vec = download.our_peer_id.bytes().collect(); 159 | if peer_id == our_peer_id { 160 | return Err(Error::ConnectingToSelf); 161 | } 162 | } 163 | 164 | Ok(()) 165 | } 166 | 167 | fn send_message(&mut self, message: Message) -> Result<(), Error> { 168 | // println!("Sending: {:?}", message); 169 | try!(self.outgoing_tx.send(message)); 170 | Ok(()) 171 | } 172 | 173 | fn process(&mut self, ipc: IPC) -> Result<(), Error> { 174 | match ipc { 175 | IPC::Message(message) => self.process_message(message), 176 | IPC::BlockComplete(piece_index, block_index) => { 177 | self.to_request.remove(&(piece_index, block_index)); 178 | match self.me.requests.remove(piece_index, block_index) { 179 | Some(r) => self.send_message(Message::Cancel(r.piece_index, r.offset, r.block_length)), 180 | None => Ok(()) 181 | } 182 | }, 183 | IPC::PieceComplete(piece_index) => { 184 | self.me.has_pieces[piece_index as usize] = true; 185 | try!(self.update_my_interested_status()); 186 | try!(self.send_message(Message::Have(piece_index))); 187 | Ok(()) 188 | }, 189 | IPC::DownloadComplete => { 190 | self.halt = true; 191 | try!(self.update_my_interested_status()); 192 | Ok(()) 193 | }, 194 | IPC::BlockUploaded => { 195 | self.upload_in_progress = false; 196 | try!(self.upload_next_block()); 197 | Ok(()) 198 | }, 199 | } 200 | } 201 | 202 | fn process_message(&mut self, message: Message) -> Result<(), Error> { 203 | // println!("Received: {:?}", message); 204 | match message { 205 | Message::KeepAlive => {}, 206 | Message::Choke => { 207 | self.me.is_choked = true; 208 | }, 209 | Message::Unchoke => { 210 | if self.me.is_choked { 211 | self.me.is_choked = false; 212 | try!(self.request_more_blocks()); 213 | } 214 | }, 215 | Message::Interested => { 216 | self.them.is_interested = true; 217 | try!(self.unchoke_them()); 218 | }, 219 | Message::NotInterested => { 220 | self.them.is_interested = false; 221 | }, 222 | Message::Have(have_index) => { 223 | self.them.has_pieces[have_index as usize] = true; 224 | self.queue_blocks(have_index); 225 | try!(self.update_my_interested_status()); 226 | try!(self.request_more_blocks()); 227 | }, 228 | Message::Bitfield(bytes) => { 229 | for have_index in 0..self.them.has_pieces.len() { 230 | let bytes_index = have_index / 8; 231 | let index_into_byte = have_index % 8; 232 | let byte = bytes[bytes_index]; 233 | let mask = 1 << (7 - index_into_byte); 234 | let value = (byte & mask) != 0; 235 | self.them.has_pieces[have_index] = value; 236 | if value { 237 | self.queue_blocks(have_index as u32); 238 | } 239 | }; 240 | try!(self.update_my_interested_status()); 241 | try!(self.request_more_blocks()); 242 | }, 243 | Message::Request(piece_index, offset, length) => { 244 | let block_index = offset / BLOCK_SIZE; 245 | self.them.requests.add(piece_index, block_index, offset, length); 246 | try!(self.upload_next_block()); 247 | }, 248 | Message::Piece(piece_index, offset, data) => { 249 | let block_index = offset / BLOCK_SIZE; 250 | self.me.requests.remove(piece_index, block_index); 251 | { 252 | let mut download = self.download_mutex.lock().unwrap(); 253 | try!(download.store(piece_index, block_index, data)) 254 | } 255 | try!(self.update_my_interested_status()); 256 | try!(self.request_more_blocks()); 257 | }, 258 | Message::Cancel(piece_index, offset, _) => { 259 | let block_index = offset / BLOCK_SIZE; 260 | self.them.requests.remove(piece_index, block_index); 261 | }, 262 | _ => return Err(Error::UnknownRequestType(message)) 263 | }; 264 | Ok(()) 265 | } 266 | 267 | fn queue_blocks(&mut self, piece_index: u32) { 268 | let incomplete_blocks = { 269 | let download = self.download_mutex.lock().unwrap(); 270 | download.incomplete_blocks_for_piece(piece_index) 271 | }; 272 | 273 | for (block_index, block_length) in incomplete_blocks { 274 | if !self.me.requests.has(piece_index, block_index) { 275 | self.to_request.insert((piece_index, block_index), (piece_index, block_index, block_length)); 276 | } 277 | } 278 | } 279 | 280 | fn update_my_interested_status(&mut self) -> Result<(), Error> { 281 | let am_interested = self.me.requests.len() > 0 || self.to_request.len() > 0; 282 | 283 | if self.me.is_interested != am_interested { 284 | self.me.is_interested = am_interested; 285 | let message = if am_interested { Message::Interested } else { Message::NotInterested }; 286 | self.send_message(message) 287 | } else { 288 | Ok(()) 289 | } 290 | } 291 | 292 | fn send_bitfield(&mut self) -> Result<(), Error> { 293 | let mut bytes: Vec = vec![0; (self.me.has_pieces.len() as f64 / 8 as f64).ceil() as usize]; 294 | for have_index in 0..self.me.has_pieces.len() { 295 | let bytes_index = have_index / 8; 296 | let index_into_byte = have_index % 8; 297 | if self.me.has_pieces[have_index] { 298 | let mask = 1 << (7 - index_into_byte); 299 | bytes[bytes_index] |= mask; 300 | } 301 | }; 302 | self.send_message(Message::Bitfield(bytes)) 303 | } 304 | 305 | fn request_more_blocks(&mut self) -> Result<(), Error> { 306 | if self.me.is_choked || !self.me.is_interested || self.to_request.len() == 0 { 307 | return Ok(()) 308 | } 309 | 310 | while self.me.requests.len() < MAX_CONCURRENT_REQUESTS as usize { 311 | let len = self.to_request.len(); 312 | if len == 0 { 313 | return Ok(()); 314 | } 315 | 316 | // remove a block at random from to_request 317 | let (piece_index, block_index, block_length) = { 318 | let index = rand::thread_rng().gen_range(0, len); 319 | let target = self.to_request.keys().nth(index).unwrap().clone(); 320 | self.to_request.remove(&target).unwrap() 321 | }; 322 | 323 | // add a request 324 | let offset = block_index * BLOCK_SIZE; 325 | if self.me.requests.add(piece_index, block_index, offset, block_length) { 326 | try!(self.send_message(Message::Request(piece_index, offset, block_length))); 327 | } 328 | } 329 | 330 | Ok(()) 331 | } 332 | 333 | fn unchoke_them(&mut self) -> Result<(), Error> { 334 | if self.them.is_choked { 335 | self.them.is_choked = false; 336 | try!(self.send_message(Message::Unchoke)); 337 | try!(self.upload_next_block()); 338 | } 339 | Ok(()) 340 | } 341 | 342 | fn upload_next_block(&mut self) -> Result<(), Error> { 343 | if self.upload_in_progress || self.them.is_choked || !self.them.is_interested { 344 | return Ok(()); 345 | } 346 | 347 | match self.them.requests.pop() { 348 | Some(r) => { 349 | let data = { 350 | let mut download = self.download_mutex.lock().unwrap(); 351 | try!(download.retrive_data(&r)) 352 | }; 353 | self.upload_in_progress = true; 354 | self.send_message(Message::Piece(r.piece_index, r.offset, data)) 355 | }, 356 | None => Ok(()) 357 | } 358 | } 359 | } 360 | 361 | struct PeerMetadata { 362 | has_pieces: Vec, 363 | is_choked: bool, 364 | is_interested: bool, 365 | requests: RequestQueue, 366 | } 367 | 368 | impl PeerMetadata { 369 | fn new(has_pieces: Vec) -> PeerMetadata { 370 | PeerMetadata { 371 | has_pieces: has_pieces, 372 | is_choked: true, 373 | is_interested: false, 374 | requests: RequestQueue::new(), 375 | } 376 | } 377 | } 378 | 379 | struct DownstreamMessageFunnel { 380 | stream: TcpStream, 381 | tx: Sender, 382 | } 383 | 384 | impl DownstreamMessageFunnel { 385 | fn start(stream: TcpStream, tx: Sender) { 386 | let mut funnel = DownstreamMessageFunnel { 387 | stream: stream, 388 | tx: tx, 389 | }; 390 | match funnel.run() { 391 | Ok(_) => {}, 392 | Err(e) => println!("Error: {:?}", e) 393 | } 394 | } 395 | 396 | fn run(&mut self) -> Result<(), Error> { 397 | loop { 398 | let message = try!(self.receive_message()); 399 | try!(self.tx.send(IPC::Message(message))); 400 | } 401 | } 402 | 403 | fn receive_message(&mut self) -> Result { 404 | let message_size = bytes_to_u32(&try!(read_n(&mut self.stream, 4))); 405 | if message_size > 0 { 406 | let message = try!(read_n(&mut self.stream, message_size)); 407 | Ok(Message::new(&message[0], &message[1..])) 408 | } else { 409 | Ok(Message::KeepAlive) 410 | } 411 | } 412 | } 413 | 414 | struct UpstreamMessageFunnel { 415 | stream: TcpStream, 416 | rx: Receiver, 417 | tx: Sender, 418 | } 419 | 420 | impl UpstreamMessageFunnel { 421 | fn start(stream: TcpStream, rx: Receiver, tx: Sender) { 422 | let mut funnel = UpstreamMessageFunnel { 423 | stream: stream, 424 | rx: rx, 425 | tx: tx, 426 | }; 427 | match funnel.run() { 428 | Ok(_) => {}, 429 | Err(e) => println!("Error: {:?}", e) 430 | } 431 | } 432 | 433 | fn run(&mut self) -> Result<(), Error> { 434 | loop { 435 | let message = try!(self.rx.recv()); 436 | let is_block_upload = match message { 437 | Message::Piece(_, _, _) => true, 438 | _ => false 439 | }; 440 | 441 | // do a blocking write to the TCP stream 442 | try!(self.stream.write_all(&message.serialize())); 443 | 444 | // notify the main PeerConnection thread that this block is finished 445 | if is_block_upload { 446 | try!(self.tx.send(IPC::BlockUploaded)); 447 | } 448 | } 449 | } 450 | } 451 | 452 | fn read_n(stream: &mut TcpStream, bytes_to_read: u32) -> Result, Error> { 453 | let mut buf = vec![]; 454 | try!(read_n_to_buf(stream, &mut buf, bytes_to_read)); 455 | Ok(buf) 456 | } 457 | fn read_n_to_buf(stream: &mut TcpStream, buf: &mut Vec, bytes_to_read: u32) -> Result<(), Error> { 458 | if bytes_to_read == 0 { 459 | return Ok(()); 460 | } 461 | 462 | let bytes_read = stream.take(bytes_to_read as u64).read_to_end(buf); 463 | match bytes_read { 464 | Ok(0) => Err(Error::SocketClosed), 465 | Ok(n) if n == bytes_to_read as usize => Ok(()), 466 | Ok(n) => read_n_to_buf(stream, buf, bytes_to_read - n as u32), 467 | Err(e) => try!(Err(e)) 468 | } 469 | } 470 | 471 | #[derive(Clone)] 472 | pub enum Message { 473 | KeepAlive, 474 | Choke, 475 | Unchoke, 476 | Interested, 477 | NotInterested, 478 | Have(u32), 479 | Bitfield(Vec), 480 | Request(u32, u32, u32), 481 | Piece(u32, u32, Vec), 482 | Cancel(u32, u32, u32), 483 | Port, // TODO Add params 484 | } 485 | 486 | impl Message { 487 | fn new(id: &u8, body: &[u8]) -> Message { 488 | match *id { 489 | 0 => Message::Choke, 490 | 1 => Message::Unchoke, 491 | 2 => Message::Interested, 492 | 3 => Message::NotInterested, 493 | 4 => Message::Have(bytes_to_u32(body)), 494 | 5 => Message::Bitfield(body.to_owned()), 495 | 6 => { 496 | let index = bytes_to_u32(&body[0..4]); 497 | let offset = bytes_to_u32(&body[4..8]); 498 | let length = bytes_to_u32(&body[8..12]); 499 | Message::Request(index, offset, length) 500 | }, 501 | 7 => { 502 | let index = bytes_to_u32(&body[0..4]); 503 | let offset = bytes_to_u32(&body[4..8]); 504 | let data = body[8..].to_owned(); 505 | Message::Piece(index, offset, data) 506 | }, 507 | 8 => { 508 | let index = bytes_to_u32(&body[0..4]); 509 | let offset = bytes_to_u32(&body[4..8]); 510 | let length = bytes_to_u32(&body[8..12]); 511 | Message::Cancel(index, offset, length) 512 | }, 513 | 9 => Message::Port, 514 | _ => panic!("Bad message id: {}", id) 515 | } 516 | } 517 | 518 | fn serialize(self) -> Vec { 519 | let mut payload = vec![]; 520 | match self { 521 | Message::KeepAlive => {}, 522 | Message::Choke => payload.push(0), 523 | Message::Unchoke => payload.push(1), 524 | Message::Interested => payload.push(2), 525 | Message::NotInterested => payload.push(3), 526 | Message::Have(index) => { 527 | payload.push(4); 528 | payload.extend(u32_to_bytes(index).into_iter()); 529 | }, 530 | Message::Bitfield(bytes) => { 531 | payload.push(5); 532 | payload.extend(bytes); 533 | }, 534 | Message::Request(index, offset, length) => { 535 | payload.push(6); 536 | payload.extend(u32_to_bytes(index).into_iter()); 537 | payload.extend(u32_to_bytes(offset).into_iter()); 538 | payload.extend(u32_to_bytes(length).into_iter()); 539 | }, 540 | Message::Piece(index, offset, data) => { 541 | payload.push(6); 542 | payload.extend(u32_to_bytes(index).into_iter()); 543 | payload.extend(u32_to_bytes(offset).into_iter()); 544 | payload.extend(data); 545 | }, 546 | Message::Cancel(index, offset, length) => { 547 | payload.push(8); 548 | payload.extend(u32_to_bytes(index).into_iter()); 549 | payload.extend(u32_to_bytes(offset).into_iter()); 550 | payload.extend(u32_to_bytes(length).into_iter()); 551 | }, 552 | Message::Port => payload.push(9), 553 | }; 554 | 555 | // prepend size 556 | let mut size = u32_to_bytes(payload.len() as u32); 557 | size.extend(payload); 558 | size 559 | } 560 | } 561 | 562 | impl fmt::Debug for Message { 563 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 564 | match *self { 565 | Message::KeepAlive => write!(f, "KeepAlive"), 566 | Message::Choke => write!(f, "Choke"), 567 | Message::Unchoke => write!(f, "Unchoke"), 568 | Message::Interested => write!(f, "Interested"), 569 | Message::NotInterested => write!(f, "NotInterested"), 570 | Message::Have(ref index) => write!(f, "Have({})", index), 571 | Message::Bitfield(ref bytes) => write!(f, "Bitfield({:?})", bytes), 572 | Message::Request(ref index, ref offset, ref length) => write!(f, "Request({}, {}, {})", index, offset, length), 573 | Message::Piece(ref index, ref offset, ref data) => write!(f, "Piece({}, {}, size={})", index, offset, data.len()), 574 | Message::Cancel(ref index, ref offset, ref length) => write!(f, "Cancel({}, {}, {})", index, offset, length), 575 | Message::Port => write!(f, "Port"), 576 | } 577 | } 578 | } 579 | 580 | const BYTE_0: u32 = 256 * 256 * 256; 581 | const BYTE_1: u32 = 256 * 256; 582 | const BYTE_2: u32 = 256; 583 | const BYTE_3: u32 = 1; 584 | 585 | fn bytes_to_u32(bytes: &[u8]) -> u32 { 586 | bytes[0] as u32 * BYTE_0 + 587 | bytes[1] as u32 * BYTE_1 + 588 | bytes[2] as u32 * BYTE_2 + 589 | bytes[3] as u32 * BYTE_3 590 | } 591 | 592 | fn u32_to_bytes(integer: u32) -> Vec { 593 | let mut rest = integer; 594 | let first = rest / BYTE_0; 595 | rest -= first * BYTE_0; 596 | let second = rest / BYTE_1; 597 | rest -= second * BYTE_1; 598 | let third = rest / BYTE_2; 599 | rest -= third * BYTE_2; 600 | let fourth = rest; 601 | vec![first as u8, second as u8, third as u8, fourth as u8] 602 | } 603 | 604 | #[derive(Debug)] 605 | pub enum Error { 606 | InvalidInfoHash, 607 | ConnectingToSelf, 608 | DownloadError(download::Error), 609 | IoError(io::Error), 610 | SocketClosed, 611 | UnknownRequestType(Message), 612 | ReceiveError(RecvError), 613 | SendMessageError(SendError), 614 | SendIPCError(SendError), 615 | Any(Box), 616 | } 617 | 618 | impl convert::From for Error { 619 | fn from(err: download::Error) -> Error { 620 | Error::DownloadError(err) 621 | } 622 | } 623 | 624 | impl convert::From for Error { 625 | fn from(err: io::Error) -> Error { 626 | Error::IoError(err) 627 | } 628 | } 629 | 630 | impl convert::From for Error { 631 | fn from(err: RecvError) -> Error { 632 | Error::ReceiveError(err) 633 | } 634 | } 635 | 636 | impl convert::From> for Error { 637 | fn from(err: SendError) -> Error { 638 | Error::SendMessageError(err) 639 | } 640 | } 641 | 642 | impl convert::From> for Error { 643 | fn from(err: SendError) -> Error { 644 | Error::SendIPCError(err) 645 | } 646 | } 647 | 648 | impl convert::From> for Error { 649 | fn from(err: Box) -> Error { 650 | Error::Any(err) 651 | } 652 | } 653 | -------------------------------------------------------------------------------- /src/request_metadata.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct RequestMetadata { 3 | pub piece_index: u32, 4 | pub block_index: u32, 5 | pub offset: u32, 6 | pub block_length: u32, 7 | } 8 | 9 | impl RequestMetadata { 10 | pub fn matches(&self, piece_index: u32, block_index: u32) -> bool { 11 | self.piece_index == piece_index && self.block_index == block_index 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/request_queue.rs: -------------------------------------------------------------------------------- 1 | use request_metadata::RequestMetadata; 2 | 3 | #[derive(Debug)] 4 | pub struct RequestQueue { 5 | requests: Vec, 6 | } 7 | 8 | impl RequestQueue { 9 | pub fn new() -> RequestQueue { 10 | RequestQueue { requests: vec![] } 11 | } 12 | 13 | pub fn has(&self, piece_index: u32, block_index: u32) -> bool { 14 | self.position(piece_index, block_index).is_some() 15 | } 16 | 17 | pub fn add(&mut self, piece_index: u32, block_index: u32, offset: u32, block_length: u32) -> bool { 18 | if !self.has(piece_index, block_index) { 19 | let r = RequestMetadata { 20 | piece_index: piece_index, 21 | block_index: block_index, 22 | offset: offset, 23 | block_length: block_length, 24 | }; 25 | self.requests.push(r); 26 | true 27 | } else { 28 | false 29 | } 30 | } 31 | 32 | pub fn pop(&mut self) -> Option { 33 | if self.requests.len() > 0 { 34 | Some(self.requests.remove(0)) 35 | } else { 36 | None 37 | } 38 | } 39 | 40 | pub fn remove(&mut self, piece_index: u32, block_index: u32) -> Option { 41 | match self.position(piece_index, block_index) { 42 | Some(i) => { 43 | let r = self.requests.remove(i); 44 | Some(r) 45 | }, 46 | None => None 47 | } 48 | } 49 | 50 | fn position(&self, piece_index: u32, block_index: u32) -> Option { 51 | self.requests.iter().position(|r| r.matches(piece_index, block_index)) 52 | } 53 | 54 | pub fn len(&self) -> usize { 55 | self.requests.len() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/tracker.rs: -------------------------------------------------------------------------------- 1 | extern crate hyper; 2 | extern crate url; 3 | 4 | use std::{convert, io}; 5 | use std::io::Read; 6 | use self::hyper::Client; 7 | use self::hyper::header::Connection; 8 | use self::url::percent_encoding::{percent_encode, FORM_URLENCODED_ENCODE_SET}; 9 | 10 | use decoder; 11 | use metainfo::Metainfo; 12 | use tracker_response::{Peer, TrackerResponse}; 13 | 14 | pub fn get_peers(peer_id: &str, metainfo: &Metainfo, listener_port: u16) ->Result, Error> { 15 | let length_string = metainfo.info.length.to_string(); 16 | let encoded_info_hash = percent_encode(&metainfo.info_hash, FORM_URLENCODED_ENCODE_SET); 17 | let listener_port_string = listener_port.to_string(); 18 | let params = vec![("left", length_string.as_ref()), 19 | ("info_hash", encoded_info_hash.as_ref()), 20 | ("downloaded", "0"), 21 | ("uploaded", "0"), 22 | ("event", "started"), 23 | ("peer_id", peer_id), 24 | ("compact", "1"), 25 | ("port", listener_port_string.as_ref())]; 26 | let url = format!("{}?{}", metainfo.announce, encode_query_params(¶ms)); 27 | 28 | let mut client = Client::new(); 29 | let mut http_res = try!(client.get(&url).header(Connection::close()).send()); 30 | 31 | let mut body = Vec::new(); 32 | try!(http_res.read_to_end(&mut body)); 33 | 34 | let res = try!(TrackerResponse::parse(&body)); 35 | Ok(res.peers) 36 | } 37 | 38 | fn encode_query_params(params: &[(&str, &str)]) -> String { 39 | let param_strings: Vec = params.iter().map(|&(k, v)| format!("{}={}", k, v)).collect(); 40 | param_strings.join("&") 41 | } 42 | 43 | #[derive(Debug)] 44 | pub enum Error { 45 | DecoderError(decoder::Error), 46 | HyperError(hyper::Error), 47 | IoError(io::Error), 48 | } 49 | 50 | impl convert::From for Error { 51 | fn from(err: decoder::Error) -> Error { 52 | Error::DecoderError(err) 53 | } 54 | } 55 | 56 | impl convert::From for Error { 57 | fn from(err: hyper::Error) -> Error { 58 | Error::HyperError(err) 59 | } 60 | } 61 | 62 | impl convert::From for Error { 63 | fn from(err: io::Error) -> Error { 64 | Error::IoError(err) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/tracker_response.rs: -------------------------------------------------------------------------------- 1 | use bencode; 2 | use bencode::{Bencode, FromBencode}; 3 | use bencode::util::ByteString; 4 | use std::net::Ipv4Addr; 5 | 6 | use decoder; 7 | 8 | #[derive(PartialEq, Debug)] 9 | pub struct TrackerResponse { 10 | pub interval: u32, 11 | pub min_interval: Option, 12 | pub complete: u32, 13 | pub incomplete: u32, 14 | pub peers: Vec, 15 | } 16 | 17 | impl TrackerResponse { 18 | pub fn parse(bytes: &[u8]) -> Result { 19 | let bencode = try!(bencode::from_buffer(bytes)); 20 | FromBencode::from_bencode(&bencode) 21 | } 22 | } 23 | 24 | impl FromBencode for TrackerResponse { 25 | type Err = decoder::Error; 26 | 27 | fn from_bencode(bencode: &bencode::Bencode) -> Result { 28 | match bencode { 29 | &Bencode::Dict(ref m) => { 30 | let peers = get_field_as_bytes!(m, "peers").chunks(6).map(Peer::from_bytes).collect(); 31 | 32 | let response = TrackerResponse{ 33 | interval: get_field!(m, "interval"), 34 | min_interval: get_optional_field!(m, "min interval"), 35 | complete: get_field!(m, "complete"), 36 | incomplete: get_field!(m, "incomplete"), 37 | peers: peers, 38 | }; 39 | Ok(response) 40 | } 41 | _ => Err(decoder::Error::NotADict) 42 | } 43 | } 44 | } 45 | 46 | #[derive(PartialEq, Debug)] 47 | pub struct Peer { 48 | pub ip: Ipv4Addr, 49 | pub port: u16, 50 | } 51 | 52 | impl Peer { 53 | fn from_bytes(v: &[u8]) -> Peer { 54 | let ip = Ipv4Addr::new(v[0], v[1], v[2], v[3]); 55 | let port = (v[4] as u16) * 256 + (v[5] as u16); 56 | Peer{ ip: ip, port: port } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test_data/flagfromserver.torrent: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenpratt/rusty_torrent/c17a3e2e258d360159c9a9c01a35dba609064990/test_data/flagfromserver.torrent -------------------------------------------------------------------------------- /test_data/ubuntu-15.04-server-amd64.iso.torrent: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenpratt/rusty_torrent/c17a3e2e258d360159c9a9c01a35dba609064990/test_data/ubuntu-15.04-server-amd64.iso.torrent -------------------------------------------------------------------------------- /watch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | kicker -e "cargo build" -c -l 0.1 src/*.rs 3 | --------------------------------------------------------------------------------