You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
5.1 KiB
Rust
147 lines
5.1 KiB
Rust
use bytes::{Buf, Bytes, BytesMut};
|
|
use rand::random;
|
|
use std::cmp::min;
|
|
use std::convert::TryInto;
|
|
use std::io::{Read, Write};
|
|
use std::net::{SocketAddr, TcpStream};
|
|
use std::process::exit;
|
|
use torment_core::infohash::v1::U160;
|
|
use torment_core::infohash::InfoHashCapable;
|
|
use torment_core::metainfo::Torrent;
|
|
use torment_core::peer_id;
|
|
use torment_peer::message::{Handshake, Message, SelectionMessage};
|
|
use torment_peer::{Peer, PeerProtocol};
|
|
use torment_storage::ToStorageMap;
|
|
|
|
pub fn download(torrent: Torrent, peers: Vec<SocketAddr>) {
|
|
println!("Starting download of {}", torrent.name());
|
|
if peers.len() == 0 {
|
|
println!("No peers given, stopping");
|
|
exit(1);
|
|
}
|
|
let peer = peers.first().unwrap();
|
|
let mut stream = TcpStream::connect(peer).expect("Failed to open tcp connection to peer");
|
|
stream.set_nodelay(true).expect("Failed setting nodelay");
|
|
let id = U160::from(peer_id(random()));
|
|
|
|
let our_header = Handshake::new(id, torrent.info_hash());
|
|
stream.write(&our_header.to_bytes()).unwrap();
|
|
let mut buffer = [0u8; 4096];
|
|
let mut done_header = false;
|
|
#[allow(unused_assignments)]
|
|
let mut header = Handshake::new(U160::random(), U160::random());
|
|
let mut peer = None;
|
|
let mut message_buffer = BytesMut::with_capacity(4096 * 10);
|
|
let mut storage = torrent.to_storage_map("/tmp", false);
|
|
let mut asked = 0;
|
|
let request_length = 2u32.pow(14);
|
|
let piece_length = torrent.meta_info().piece_length() as u32;
|
|
let mut progress = 0;
|
|
let mut header_buffer = BytesMut::new();
|
|
loop {
|
|
let size = stream.read(&mut buffer).expect("Failed to receive data");
|
|
if done_header == false {
|
|
header_buffer.extend_from_slice(&buffer[..size]);
|
|
if header_buffer.len() < 68 {
|
|
continue;
|
|
}
|
|
|
|
header = Handshake::from_bytes(Bytes::from(buffer[..size].to_vec()))
|
|
.expect("Failed to parse header");
|
|
if header.info_hash() != torrent.info_hash() {
|
|
panic!(
|
|
"Peer wants to serve different torrent ({}) :(",
|
|
header.info_hash()
|
|
);
|
|
}
|
|
|
|
println!(
|
|
"Peer[{}] connected",
|
|
String::from_utf8_lossy(&header.peer_id().to_bytes())
|
|
);
|
|
peer = Some(Peer::new(
|
|
stream.peer_addr().unwrap(),
|
|
PeerProtocol::TCP,
|
|
header,
|
|
torrent.meta_info(),
|
|
));
|
|
done_header = true;
|
|
// stream.write_all(&*Message::Request(SelectionMessage::new(0, 0, 1024)).to_bytes());
|
|
|
|
continue;
|
|
}
|
|
|
|
let peer = peer.as_mut().unwrap();
|
|
message_buffer.extend_from_slice(&buffer[..size]);
|
|
while message_buffer.len() >= 4 {
|
|
let length = u32::from_be_bytes(message_buffer[..4].try_into().unwrap());
|
|
if length == 0 {
|
|
message_buffer.advance(4);
|
|
continue;
|
|
}
|
|
|
|
if message_buffer.len() < (length + 4) as usize {
|
|
break;
|
|
}
|
|
|
|
let message = message_buffer.split_to((4 + length) as usize).freeze();
|
|
|
|
let msg = Message::from_bytes(message.slice(4..)).expect("Failed parsing message");
|
|
// println!("=> {:?}", msg);
|
|
if msg == Message::Unchoke {
|
|
stream
|
|
.write_all(&Message::Interested.to_length_prefixed_bytes())
|
|
.unwrap();
|
|
}
|
|
peer.process(msg);
|
|
}
|
|
|
|
let mut messages = vec![];
|
|
let was_above_90 = peer.count_open_requests() > 90;
|
|
while !peer.is_choked()
|
|
&& peer.count_open_requests() < 500
|
|
&& !was_above_90
|
|
&& asked < torrent.meta_info().pieces()
|
|
{
|
|
let curr_piece_length = if asked + 1 == torrent.meta_info().pieces() {
|
|
piece_length - (storage.size() as u32 % piece_length)
|
|
} else {
|
|
piece_length
|
|
};
|
|
let size = min(request_length, curr_piece_length - progress);
|
|
let sel = SelectionMessage::new(asked as u32, progress, size);
|
|
peer.requested(sel);
|
|
let msg = Message::Request(sel);
|
|
// println!("<= {:?}", msg);
|
|
messages.extend(msg.to_length_prefixed_bytes());
|
|
progress += size;
|
|
if progress >= curr_piece_length {
|
|
asked += 1;
|
|
progress = 0;
|
|
}
|
|
}
|
|
|
|
if messages.len() > 0 {
|
|
stream
|
|
.write_all(&messages)
|
|
.expect("Failed writing to network socket");
|
|
}
|
|
|
|
while let Some(piece) = peer.next_piece() {
|
|
// println!("> {} {} {}", piece.index(), piece.offset(), piece.length());
|
|
storage
|
|
.write(
|
|
piece.index() as usize,
|
|
piece.offset() as usize,
|
|
piece.piece(),
|
|
)
|
|
.expect("Failed writing piece to storage");
|
|
}
|
|
|
|
if peer.count_open_requests() == 0 && asked > 1 {
|
|
println!("done");
|
|
break;
|
|
}
|
|
}
|
|
}
|