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.
276 lines
8.2 KiB
Rust
276 lines
8.2 KiB
Rust
use crate::tracker_manager::{TrackerId, TrackerManager};
|
|
use bytes::Bytes;
|
|
use ring::digest::{digest, SHA1_FOR_LEGACY_USE_ONLY};
|
|
use std::collections::{HashMap, HashSet, VecDeque};
|
|
use std::net::SocketAddr;
|
|
use std::ops::Index;
|
|
use std::option::Option::Some;
|
|
use std::path::PathBuf;
|
|
use torment_core::infohash::v1::U160;
|
|
use torment_core::metainfo::{MetaInfo, Torrent};
|
|
use torment_core::Bitfield;
|
|
use torment_peer::message::{Handshake, Message, PieceMessage, SelectionMessage};
|
|
use torment_peer::{Peer, PeerProtocol};
|
|
use torment_storage::{StorageMap, ToStorageMap};
|
|
|
|
#[derive(Debug)]
|
|
pub enum Source {
|
|
Torrent(TorrentSource),
|
|
MetaInfo(MetaInfoSource),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MetaInfoSource {
|
|
info_hash: U160,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TorrentSource {
|
|
file: Option<PathBuf>,
|
|
torrent: Option<Torrent>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TorrentTarget {
|
|
pub path: PathBuf,
|
|
pub is_base_path: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TorrentManager {
|
|
info_hash: U160,
|
|
bitfield: Bitfield,
|
|
trackers: Vec<Vec<TrackerId>>,
|
|
source: Source,
|
|
target: TorrentTarget,
|
|
peers: HashMap<U160, Peer>,
|
|
queue: VecDeque<(U160, Message)>,
|
|
storage_map: StorageMap,
|
|
}
|
|
|
|
pub const REQUEST_SIZE: usize = 16384;
|
|
|
|
impl TorrentManager {
|
|
pub fn info_hash(&self) -> U160 {
|
|
self.info_hash
|
|
}
|
|
|
|
pub fn from_torrent(
|
|
torrent: Torrent,
|
|
target: TorrentTarget,
|
|
tracker_manager: Option<&mut TrackerManager>,
|
|
) -> TorrentManager {
|
|
let trackers = tracker_manager
|
|
.map(|manager| {
|
|
torrent
|
|
.announce_list()
|
|
.iter()
|
|
.map(|tier_list| {
|
|
tier_list
|
|
.iter()
|
|
.filter_map(|tracker| manager.get_tracker_id(tracker))
|
|
.collect::<Vec<_>>()
|
|
})
|
|
.collect::<Vec<_>>()
|
|
})
|
|
.unwrap_or(vec![]);
|
|
|
|
TorrentManager {
|
|
info_hash: torrent.info_hash(),
|
|
bitfield: Bitfield::with_size(torrent.meta_info().pieces()),
|
|
trackers,
|
|
source: Source::Torrent(TorrentSource {
|
|
file: None,
|
|
torrent: Some(torrent.clone()),
|
|
}),
|
|
storage_map: torrent.to_storage_map(&target.path, target.is_base_path),
|
|
target,
|
|
peers: HashMap::new(),
|
|
queue: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn handshake(
|
|
&mut self,
|
|
handshake: Handshake,
|
|
addr: SocketAddr,
|
|
protocol: PeerProtocol,
|
|
) -> bool {
|
|
if handshake.info_hash() != self.info_hash {
|
|
return false;
|
|
}
|
|
|
|
if let Some(peer) = self.peers.get(&handshake.peer_id()) {
|
|
if peer.addr() != addr {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
let meta_info = self.meta_info();
|
|
let peer = Peer::new(addr, protocol, handshake, meta_info);
|
|
self.peers.insert(peer.id(), peer);
|
|
|
|
true
|
|
}
|
|
|
|
fn meta_info(&mut self) -> &MetaInfo {
|
|
if let Source::Torrent(torrent) = &self.source {
|
|
return torrent
|
|
.torrent
|
|
.as_ref()
|
|
.expect("No torrent loaded uh oh missing functionality")
|
|
.meta_info();
|
|
}
|
|
|
|
panic!("Can't resolve MetaInfo for torrent")
|
|
}
|
|
|
|
pub fn process(&mut self, peer_id: U160, message: Message) -> bool {
|
|
let mut queue = vec![];
|
|
let ok = if let Some(peer) = self.peers.get_mut(&peer_id) {
|
|
if peer.process(message) {
|
|
while let Some(message) = peer.next() {
|
|
self.queue.push_back((peer_id, message));
|
|
}
|
|
|
|
while let Some(piece) = peer.next_piece() {
|
|
if self.storage_map.has_piece(piece.index() as usize) {
|
|
continue;
|
|
}
|
|
|
|
if self
|
|
.storage_map
|
|
.write(
|
|
piece.index() as usize,
|
|
piece.offset() as usize,
|
|
piece.piece(),
|
|
)
|
|
.unwrap()
|
|
{
|
|
queue.push(piece.index() as usize);
|
|
}
|
|
}
|
|
|
|
while let Some(piece_request) = peer.next_request() {
|
|
if !self.bitfield.get(piece_request.index()) {
|
|
continue;
|
|
}
|
|
|
|
let mut buffer = vec![0u8; piece_request.length() as usize];
|
|
self.storage_map
|
|
.read(
|
|
piece_request.index() as usize,
|
|
piece_request.offset() as usize,
|
|
&mut buffer,
|
|
)
|
|
.unwrap();
|
|
|
|
self.queue.push_back((
|
|
peer_id,
|
|
Message::Piece(PieceMessage {
|
|
index: piece_request.index(),
|
|
offset: piece_request.offset(),
|
|
piece: Bytes::from(buffer),
|
|
}),
|
|
))
|
|
}
|
|
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
};
|
|
|
|
for have in queue {
|
|
let piece_hash = self.meta_info().hash(have);
|
|
let piece_data = self.storage_map.read_piece(have).unwrap();
|
|
let res = digest(&SHA1_FOR_LEGACY_USE_ONLY, &piece_data);
|
|
if piece_hash != res.as_ref() {
|
|
println!("=> Piece#{} failed verification", have);
|
|
self.storage_map.wipe_piece(have);
|
|
continue;
|
|
} else {
|
|
println!("=> Piece#{} verified", have);
|
|
}
|
|
|
|
self.bitfield.set(have as u32);
|
|
|
|
let keys = self.peers.keys().copied().collect::<Vec<_>>();
|
|
for key in keys {
|
|
self.queue.push_back((key, Message::Have(have as u32)));
|
|
|
|
if !self.peers[&key].has_piece(have as u32) && self.peers[&key].we_choked() {
|
|
self.peers.get_mut(&key).unwrap().set_we_choked(false);
|
|
self.queue.push_back((key, Message::Unchoke));
|
|
}
|
|
}
|
|
}
|
|
|
|
ok
|
|
}
|
|
|
|
pub fn next(&mut self) -> Option<(U160, Message)> {
|
|
self.queue.pop_front()
|
|
}
|
|
|
|
pub fn house_keeping(&mut self) {
|
|
let mut peers = self
|
|
.peers
|
|
.iter()
|
|
.filter_map(|(_, peer)| {
|
|
if peer.has()1
|
|
|
|
if peer.is_choked() {
|
|
None
|
|
} else {
|
|
Some(peer.id())
|
|
}
|
|
})
|
|
.collect::<HashSet<_>>();
|
|
|
|
let pieces = self.meta_info().pieces() as u32;
|
|
for i in 0u32..pieces {
|
|
if self.bitfield.get(i) {
|
|
continue;
|
|
}
|
|
|
|
let length = self.storage_map.get_piece_length(i as usize);
|
|
let mut offset = 0;
|
|
|
|
for peer in &peers.clone() {
|
|
if !self.peers[peer].has_piece(i) {
|
|
continue;
|
|
}
|
|
|
|
while offset < length && self.peers[peer].count_open_requests() < 25 {
|
|
if !self.storage_map.has_piece_bit(i as usize, offset) {
|
|
let request_length = if (offset + REQUEST_SIZE) > length {
|
|
length - offset
|
|
} else {
|
|
REQUEST_SIZE
|
|
};
|
|
|
|
let msg = SelectionMessage::new(i, offset as u32, request_length as u32);
|
|
|
|
if self.peers.get_mut(peer).unwrap().requested(msg) {
|
|
self.queue.push_back((*peer, Message::Request(msg)));
|
|
}
|
|
}
|
|
|
|
offset += REQUEST_SIZE;
|
|
}
|
|
|
|
if self.peers[peer].count_open_requests() >= 25 {
|
|
peers.remove(peer);
|
|
}
|
|
|
|
if offset >= length {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|