diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..10efcb2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 352a626..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rust-analyzer.linkedProjects": [ - "./Cargo.toml" - ] -} \ No newline at end of file diff --git a/downloads/README b/downloads/README new file mode 100644 index 0000000..9f4b6d8 --- /dev/null +++ b/downloads/README @@ -0,0 +1 @@ +This is a test file diff --git a/downloads/images/LOC_Main_Reading_Room_Highsmith.jpg b/downloads/images/LOC_Main_Reading_Room_Highsmith.jpg new file mode 100644 index 0000000..2fee947 Binary files /dev/null and b/downloads/images/LOC_Main_Reading_Room_Highsmith.jpg differ diff --git a/downloads/images/melk-abbey-library.jpg b/downloads/images/melk-abbey-library.jpg new file mode 100644 index 0000000..1094bf3 Binary files /dev/null and b/downloads/images/melk-abbey-library.jpg differ diff --git a/src/main.rs b/src/main.rs index 5d75294..cb9ae5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,107 +17,111 @@ mod message; mod torrent; mod tracker; +use core::panic; +use std::net::SocketAddr; + // Crate Imports use crate::{ - files::Files, - peer::Peer, - torrent::Torrent, - tracker::{ - AnnounceMessage, AnnounceMessageResponse, - ConnectionMessage, - FromBuffer, - Tracker - } + files::Files, + peer::Peer, + torrent::Torrent, + tracker::tracker::Tracker }; +use tokio::sync::mpsc; // External Ipmorts use clap::Parser; -use log::{ debug, info, LevelFilter }; +use log::{ debug, info, LevelFilter, error }; +use tokio::spawn; /// Struct Respresenting needed arguments #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { - #[arg(short, long)] - log_file_path: Option, - - #[arg(short, long)] - torrent_file_path: String, - - #[arg(short, long)] - download_path: String, + #[arg(short, long)] + log_file_path: Option, + + #[arg(short, long)] + torrent_file_path: String, + + #[arg(short, long)] + download_path: String, + + #[arg(short, long)] + peer_id: String, } /// The root function #[tokio::main] async fn main() { - let args = Args::parse(); - - // Creates a log file to handle large amounts of data - let log_path = args.log_file_path.unwrap_or(String::from("./log/rustytorrent.log")); - simple_logging::log_to_file(&log_path, LevelFilter::Info).unwrap(); - - // Read the Torrent File - let torrent = Torrent::from_torrent_file(&args.torrent_file_path).await; - info!("Sucessfully read torrent file"); - torrent.log_useful_information(); - - // Create the files that will be written to - let mut files = Files::new(); - files.create_files(&torrent, &args.download_path).await; - - // Gets peers from the given tracker - let (_remote_hostname, _remote_port) = torrent.get_tracker(); - let (remote_hostname, remote_port) = ("tracker.opentrackr.org", 1337); - debug!("{}:{}", remote_hostname, remote_port); - - let mut tracker = Tracker::new("0.0.0.0:61389", remote_hostname, remote_port).await; - info!("Successfully connected to tracker {}:{}", remote_hostname, remote_port); - let connection_message = ConnectionMessage::from_buffer( - &tracker.send_message(&ConnectionMessage::create_basic_connection()).await - ); - - debug!("{:?}", connection_message); - - let announce_message_response = AnnounceMessageResponse::from_buffer( - &tracker.send_message(&AnnounceMessage::new( - connection_message.connection_id, - &torrent.get_info_hash(), - "-MY0001-123456654321", - torrent.get_total_length() as i64 - )).await - ); - - debug!("{:?}", announce_message_response); - info!("Found Peers"); - - // Creates an assumed peer connection to the `SocketAddr` given - let mut peer = match Peer::create_connection(&format!("{}:{}", announce_message_response.ips[0], announce_message_response.ports[0])).await { - None => { return }, - Some(peer) => peer - }; - - let num_pieces = torrent.info.pieces.len() / 20; - peer.handshake(&torrent).await; - peer.keep_alive_until_unchoke().await; - - info!("Successfully Created Connection with peer: {}", peer.peer_id); - - let mut len = 0; - - for index in 0..num_pieces { - let piece= peer.request_piece( - index as u32, torrent.info.piece_length as u32, - &mut len, torrent.get_total_length() as u32 - ).await; + let args = Args::parse(); - if torrent.check_piece(&piece, index as u32) { - files.write_piece(piece).await; - } else { - break + // Creates a log file to handle large amounts of data + let log_path = args.log_file_path.unwrap_or(String::from("./log/rustytorrent.log")); + //simple_logging::log_to_file(&log_path, LevelFilter::Debug).unwrap(); + simple_logging::log_to_stderr(LevelFilter::Debug); + + info!("==> WELCOME TO RUSTY-TORRENT <=="); + + // Read the Torrent File + let torrent = Torrent::from_torrent_file(&args.torrent_file_path).await; + torrent.log_useful_information(); + + // Create the files that will be written to + let mut files = Files::new(); + files.create_files(&torrent, &args.download_path).await; + + // Gets peers from the given tracker + + let Some(socketaddrs) = torrent.get_trackers() else { + error!("couldn't find trackers"); + panic!("couldn't find trackers") + }; + let (remote_hostname, remote_port) = ("tracker.opentrackr.org", 1337); + debug!("{}:{}", remote_hostname, remote_port); + + info!(""); + info!("--> Finding Peers <--"); + let listen_address = "0.0.0.0:61389".parse::().unwrap(); + let Ok(mut tracker) = Tracker::new(listen_address, std::net::SocketAddr::V4(socketaddrs[0])).await else { + panic!("tracker couldn't be created") + }; + info!("Successfully connected to tracker {}:{}", remote_hostname, remote_port); + + let peers = tracker.find_peers(&torrent, &args.peer_id).await; + + info!("Found Peers"); + + let num_pieces = torrent.info.pieces.len() / 20; + + let mut peer = match Peer::create_connection(peers[0]).await { + None => { return }, + Some(peer) => peer + }; + + peer.handshake(&torrent).await; + peer.keep_alive_until_unchoke().await; + info!("Successfully Created Connection with peer: {}", peer.peer_id); + + println!("{}", peers.len()); + + let mut len = 0; + + for index in 0..num_pieces { + let piece= peer.request_piece( + index as u32, torrent.info.piece_length as u32, + &mut len, torrent.get_total_length() as u32 + ).await; + + if torrent.check_piece(&piece, index as u32) { + files.write_piece(piece).await; + } else { + break + } } - } - - peer.disconnect().await; - info!("Successfully completed download"); + + peer.disconnect().await; + + + info!("Successfully completed download"); } diff --git a/src/message.rs b/src/message.rs index b1ca935..a36b5d1 100644 --- a/src/message.rs +++ b/src/message.rs @@ -49,7 +49,7 @@ impl FromBuffer for Message { let message_length = u32::from_be_bytes(message_length); - let payload: Option>; + let mut payload: Option> = None; let message_type: MessageType; if message_length == 0 { @@ -66,9 +66,9 @@ impl FromBuffer for Message { if end_of_message > buf.len() { error!("index too long"); debug!("{buf:?}"); - } - - payload = Some(buf[5..end_of_message].to_vec()); + } else { + payload = Some(buf[5..end_of_message].to_vec()); + } } Self { diff --git a/src/peer.rs b/src/peer.rs index b51104b..f8fc2f0 100644 --- a/src/peer.rs +++ b/src/peer.rs @@ -9,7 +9,7 @@ use crate::{ // External imports use log::{ debug, error }; -use std::net::SocketAddr; +use std::net::{SocketAddr, SocketAddrV4}; use tokio::{ io::{ AsyncReadExt, AsyncWriteExt }, net::TcpStream @@ -20,7 +20,7 @@ pub struct Peer { /// The `TcpStream` that is used to communicate with the peeer connection_stream: TcpStream, /// The `SocketAddr` of the peer - pub socket_addr: SocketAddr, + pub socket_addr: SocketAddrV4, /// The id of the peer pub peer_id: String, /// Whether the peer is choking the client @@ -33,29 +33,21 @@ impl Peer { /// # Arguments /// /// * `socket_address` - The socket address of the peer. - pub async fn create_connection(socket_address: &str) -> Option { - let socket_addr = match socket_address.parse::() { - Err(err) => { - error!("error parsing address {}, err: {}", socket_address, err); - return None; - } - Ok(addr) => { addr } - }; - - let connection_stream = match TcpStream::connect(socket_addr).await { + pub async fn create_connection(socket_address: SocketAddrV4) -> Option { + let connection_stream = match TcpStream::connect(socket_address).await { Err(err) => { error!("unable to connect to {}, err: {}", socket_address, err); return None }, Ok(stream) => { - debug!("created tcpstream successfully to: {socket_addr}"); + debug!("created tcpstream successfully to: {socket_address}"); stream } }; Some(Self { connection_stream, - socket_addr, + socket_addr: socket_address, peer_id: String::new(), choking: true, }) diff --git a/src/torrent.rs b/src/torrent.rs index 9ac2c71..a4a03e3 100644 --- a/src/torrent.rs +++ b/src/torrent.rs @@ -1,8 +1,9 @@ -use log::{debug, info, error}; +use log::{debug, info, error, warn, trace}; use regex::Regex; use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; use tokio::{fs::File as TokioFile, io::AsyncReadExt}; +use std::net::{IpAddr, SocketAddrV4}; /// Represents a node in a DHT network. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -11,203 +12,238 @@ struct Node(String, i64); /// Represents a file described in a torrent. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct File { - pub path: Vec, - pub length: u64, - #[serde(default)] - md5sum: Option, + pub path: Vec, + pub length: u64, + #[serde(default)] + md5sum: Option, } /// Represents the metadata of a torrent. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Info { - pub name: String, - #[serde(with = "serde_bytes")] - pub pieces: Vec, - #[serde(rename = "piece length")] - pub piece_length: u64, - #[serde(default)] - md5sum: Option, - #[serde(default)] - pub length: Option, - #[serde(default)] - pub files: Option>, - #[serde(default)] - private: Option, - #[serde(default)] - path: Option>, - #[serde(default)] - #[serde(rename = "root hash")] - root_hash: Option, + pub name: String, + #[serde(with = "serde_bytes")] + pub pieces: Vec, + #[serde(rename = "piece length")] + pub piece_length: u64, + #[serde(default)] + md5sum: Option, + #[serde(default)] + pub length: Option, + #[serde(default)] + pub files: Option>, + #[serde(default)] + private: Option, + #[serde(default)] + path: Option>, + #[serde(default)] + #[serde(rename = "root hash")] + root_hash: Option, } /// Represents a torrent. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Torrent { - pub info: Info, - #[serde(default)] - pub announce: Option, - #[serde(default)] - nodes: Option>, - #[serde(default)] - encoding: Option, - #[serde(default)] - httpseeds: Option>, - #[serde(default)] - #[serde(rename = "announce-list")] - announce_list: Option>>, - #[serde(default)] - #[serde(rename = "creation date")] - creation_date: Option, - #[serde(rename = "comment")] - comment: Option, - #[serde(default)] - #[serde(rename = "created by")] - created_by: Option + pub info: Info, + #[serde(default)] + pub announce: Option, + #[serde(default)] + nodes: Option>, + #[serde(default)] + encoding: Option, + #[serde(default)] + httpseeds: Option>, + #[serde(default)] + #[serde(rename = "announce-list")] + announce_list: Option>>, + #[serde(default)] + #[serde(rename = "creation date")] + creation_date: Option, + #[serde(rename = "comment")] + comment: Option, + #[serde(default)] + #[serde(rename = "created by")] + created_by: Option } impl Torrent { - /// Reads a `.torrent` file and converts it into a `Torrent` struct. - /// - /// # Arguments - /// - /// * `path` - The path to the `.torrent` file. - pub async fn from_torrent_file(path: &str) -> Self { - // Read .torrent File - let mut file = TokioFile::open(path).await.unwrap(); - let mut buf: Vec = Vec::new(); - - match file.read_to_end(&mut buf).await { - Err(err) => { - error!("Error reading file till end: {err}"); - panic!("Error reading file till end: {err}"); - } - Ok(_) => { info!("Succesfully read {path}") } - } - - match serde_bencode::from_bytes(&buf) { - Err(err) => { - error!("Error deserializing file: {err}"); - panic!("Error deserializing file: {err}"); - } - Ok(torrent) => { torrent } - } - } - - /// Logs info about the *.torrent file - pub fn log_useful_information(&self) { - info!(" -> Torrent Information <- "); - info!("Name: {}", self.info.name); - info!("Tracker: {:?}", self.announce); - info!("Tracker List: {:?}", self.announce_list); - info!("Info Hash: {:X?}", self.get_info_hash()); - info!("Length: {:?}", self.info.length); - - info!("Files:"); - - match &self.info.files { - None => { - info!("> {}", self.info.name); - } - Some(files) => { - for file in files { - let mut path = String::new(); - for section in &file.path { - path.push_str(&format!("{section}/")); - } - path.pop(); - info!("> {}: {}B", path, file.length) - } - } - } - } - - /// Calculates the info hash of the torrent. - pub fn get_info_hash(&self) -> Vec { - let buf = serde_bencode::to_bytes(&self.info).unwrap(); - - let mut hasher = Sha1::new(); - hasher.update(buf); - let res = hasher.finalize(); - res[..].to_vec() - } - - /// Checks if a downloaded piece matches its hash. - /// - /// # Arguments - /// - /// * `piece` - The downloaded piece. - /// * `index` - The index of the piece. - /// - /// # Returns - /// - /// * `true` if the piece is correct, `false` otherwise. - pub fn check_piece(&self, piece: &[u8], index: u32) -> bool { - let mut hasher = Sha1::new(); - hasher.update(piece); - let result = hasher.finalize(); - - let piece_hash = &self.info.pieces[(index * 20) as usize..(index * 20 + 20) as usize]; - - if &result[..] == piece_hash { - info!("Downloaded Piece {}/{} Correct!, Piece Was: {}B long", index + 1, self.info.pieces.len() / 20, piece.len(),); - true - } else { - debug!("{:?}", &result[..]); - debug!("{:?}", piece_hash); - debug!("{:?}", &result[..].len()); - debug!("{:?}", piece_hash.len()); - debug!("{}", piece.len()); - error!("Piece downloaded incorrectly"); - false - } - } - - pub fn get_total_length(&self) -> u64 { - match self.info.length { - None => {}, - Some(n) => { return n as u64 } - }; - - match &self.info.files { - None => { 0 }, - Some(files) => { - let mut n = 0; + /// Reads a `.torrent` file and converts it into a `Torrent` struct. + /// + /// # Arguments + /// + /// * `path` - The path to the `.torrent` file. + pub async fn from_torrent_file(path: &str) -> Self { + info!(""); + info!("--> Reading File <--"); + + let Ok(mut file) = TokioFile::open(path).await else { + error!("Unable to read file at {path}"); + panic!("Unable to read file at {path}"); + }; + info!("Found\t\t > {path}"); + + let mut buf: Vec = Vec::new(); + let Ok(_) = file.read_to_end(&mut buf).await else { + error!("Error reading file > {path}"); + panic!("Error reading file > {path}"); + }; + info!("Read\t\t > {path}"); + let Ok(torrent) = serde_bencode::from_bytes(&buf) else { + error!("Error deserializing file > {path}"); + panic!("Error deserializing file > {path}"); + }; + info!("Parsed\t > {path}"); + + torrent + } +} + +impl Torrent { + /// Logs info about the *.torrent file + pub fn log_useful_information(&self) { + info!(""); + info!("--> Torrent Information <--"); + info!("Name:\t\t{}", self.info.name); + info!("Trackers"); + if let Some(trackers) = &self.announce_list { + for tracker in trackers { + info!(" |> {}", tracker[0]) + } + } + info!("InfoHash:\t{:X?}", self.get_info_hash()); + info!("Length:\t{:?}", self.info.length); + + info!("Files:"); + let Some(mut files) = self.info.files.clone() else { + info!("./{}", self.info.name); + return + }; + + files.sort_by(|a, b| a.path.len().cmp(&b.path.len()) ); + info!("./"); for file in files { - n += file.length; + if file.path.len() == 1 { + info!(" |--> {:?}", file.path); + } else { + let mut path = String::new(); + file.path.iter().for_each(|s| { path.push_str(s); path.push('/') } ); + path.pop(); + + info!(" |--> {}: {}B", path, file.length) + } + } + } + + /// Calculates the info hash of the torrent. + pub fn get_info_hash(&self) -> Vec { + let buf = serde_bencode::to_bytes(&self.info).unwrap(); + + let mut hasher = Sha1::new(); + hasher.update(buf); + let res = hasher.finalize(); + res[..].to_vec() + } + + /// Checks if a downloaded piece matches its hash. + /// + /// # Arguments + /// + /// * `piece` - The downloaded piece. + /// * `index` - The index of the piece. + /// + /// # Returns + /// + /// * `true` if the piece is correct, `false` otherwise. + pub fn check_piece(&self, piece: &[u8], index: u32) -> bool { + let mut hasher = Sha1::new(); + hasher.update(piece); + let result = hasher.finalize(); + + let piece_hash = &self.info.pieces[(index * 20) as usize..(index * 20 + 20) as usize]; + + if &result[..] == piece_hash { + info!("Piece {}/{} Correct!", index + 1, self.info.pieces.len() / 20); + true + } else { + error!("Piece {}/{} incorrect :(", index + 1, self.info.pieces.len() / 20); + debug!("{:?}", &result[..]); + debug!("{:?}", piece_hash); + debug!("{:?}", &result[..].len()); + debug!("{:?}", piece_hash.len()); + debug!("{}", piece.len()); + false + } + } + + pub fn get_total_length(&self) -> u64 { + if let Some(n) = self.info.length { + return n as u64 }; - n - } + if let Some(files) = &self.info.files { + let mut n = 0; + + for file in files { + n += file.length; + }; + + return n + }; + + 0 } - } - - pub fn get_tracker(&self) -> (&str, u16) { - let re = Regex::new(r"^udp://([^:/]+):(\d+)/announce$").unwrap(); - if let Some(url) = &self.announce { - if let Some(captures) = re.captures(url) { - let hostname = captures.get(1).unwrap().as_str(); - let port = captures.get(2).unwrap().as_str(); + pub fn get_trackers(&self) -> Option> { + info!(""); + info!("--> Locating Trackers <--"); + + let mut addresses = vec![]; + + // This is the current regex as I haven't implemented support for http trackers yet + let re = Regex::new(r"^udp://([^:/]+):(\d+)/announce$").unwrap(); - return (hostname, port.parse().unwrap()); - } else { - println!("URL does not match the expected pattern | {}", url); - } - } - - for (i, url) in self.announce_list.as_ref().unwrap().iter().enumerate() { - debug!("{:?}", url); - if let Some(captures) = re.captures(&url[i]) { - let hostname = captures.get(1).unwrap().as_str(); - let port = captures.get(2).unwrap().as_str(); + if let Some(url) = &self.announce { + if let Some(captures) = re.captures(url) { + let hostname = captures.get(1).unwrap().as_str(); + let port = captures.get(2).unwrap().as_str(); + + if let Ok(ip) = dns_lookup::lookup_host(hostname) { + for i in ip { + if let IpAddr::V4(j) = i { + addresses.push(SocketAddrV4::new(j, port.parse().unwrap())) + } + } + } + } else { + warn!("{url} does not match the expected url pattern"); + } + } - return (hostname, port.parse().unwrap()); - } else { - println!("URL does not match the expected pattern | {}", url[i]); - } + if let Some(urls) = &self.announce_list { + for url in urls.iter() { + if let Some(captures) = re.captures(&url[0]) { + let hostname = captures.get(1).unwrap().as_str(); + let port = captures.get(2).unwrap().as_str(); + + if let Ok(ip) = dns_lookup::lookup_host(hostname) { + for i in ip { + if let IpAddr::V4(j) = i { + addresses.push(SocketAddrV4::new(j, port.parse().unwrap())); + } + } + info!("Sucessfully found tracker {}", url[0]); + } + } else { + warn!("{} does not match the expected url pattern", url[0]); + } + } + } + + if addresses.len() > 0 { + Some(addresses) + } else { + None + } } - - ("", 0) - } } \ No newline at end of file diff --git a/src/tracker/mod.rs b/src/tracker/mod.rs new file mode 100644 index 0000000..d23c619 --- /dev/null +++ b/src/tracker/mod.rs @@ -0,0 +1 @@ +pub mod tracker; \ No newline at end of file diff --git a/src/tracker.rs b/src/tracker/tracker.rs similarity index 82% rename from src/tracker.rs rename to src/tracker/tracker.rs index 5630162..90e7c0c 100644 --- a/src/tracker.rs +++ b/src/tracker/tracker.rs @@ -1,15 +1,17 @@ -use std::{net::{SocketAddr, Ipv4Addr}, vec}; +use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4}; use tokio::net::UdpSocket; use log::{debug, error}; +use crate::torrent::Torrent; + pub struct Tracker { /// A UdpSocket used for communication. connection_stream: UdpSocket, /// The local socket address requests are made from - pub socket_addr: SocketAddr, + listen_address: SocketAddr, /// The remote socket address of the tracker. - pub remote_addr: SocketAddr + remote_address: SocketAddr } impl Tracker { @@ -24,46 +26,30 @@ impl Tracker { /// # Panics /// /// Panics if there is an error parsing the given address or creating the UDP socket. - pub async fn new(socket_address: &str, remote_hostname: &str, remote_port: u16) -> Self { - let socket_addr = match socket_address.parse::() { - Err(err) => { - error!("error parsing address {}, err: {}", socket_address, err); - panic!("error parsing given address, {}", err); - } - Ok(addr) => { addr } + pub async fn new(listen_address: SocketAddr, remote_address: SocketAddr) -> Result { + let Ok(connection_stream) = UdpSocket::bind(listen_address).await else { + error!("error binding to udpsocket {listen_address}"); + return Err(()) }; + + debug!("bound udpsocket successfully to: {listen_address}"); - let remote_address = dns_lookup::lookup_host(remote_hostname).unwrap()[0]; - - let remote_addr = SocketAddr::new(remote_address, remote_port); - - let connection_stream = match UdpSocket::bind(socket_addr).await { + match connection_stream.connect(remote_address).await { Err(err) => { - error!("unable to bind to {}, err: {}", socket_address, err); - panic!("error creating udpsocket, {}", err); - }, - Ok(stream) => { - debug!("bound udpsocket successfully to: {socket_addr}"); - stream - } - }; - - match connection_stream.connect(remote_addr).await { - Err(err) => { - error!("unable to connect to {}, err: {}", remote_addr, err); + error!("unable to connect to {}, err: {}", remote_address, err); panic!("error creating udpsocket, {}", err); }, Ok(()) => { - debug!("successfully connected to: {remote_addr}"); + debug!("successfully connected to: {remote_address}"); } }; - Self { + Ok(Self { connection_stream, - socket_addr, - remote_addr - } + listen_address, + remote_address + }) } /// Sends a message to the tracker and receives a response asynchronously. @@ -75,7 +61,7 @@ impl Tracker { /// # Returns /// /// A byte vector containing the received response. - pub async fn send_message(&mut self, message: &T) -> Vec { + async fn send_message(&mut self, message: &T) -> Vec { let mut buf: Vec = vec![ 0; 16_384 ]; self.connection_stream.send(&message.to_buffer()).await.unwrap(); @@ -83,6 +69,33 @@ impl Tracker { buf } + + async fn send_handshake(&mut self) -> i64 { + ConnectionMessage::from_buffer( + &self.send_message(&ConnectionMessage::create_basic_connection()).await + ).connection_id + } + + pub async fn find_peers(&mut self, torrent: &Torrent, peer_id: &str) -> Vec { + let id = self.send_handshake().await; + + let message = AnnounceMessage::new( + id, + &torrent.get_info_hash(), + peer_id, + torrent.get_total_length() as i64 + ); + + let announce_message_response = AnnounceMessageResponse::from_buffer(&self.send_message(&message).await); + + let mut peer_addresses = vec![]; + + for i in 0..announce_message_response.ips.len() { + peer_addresses.push(SocketAddrV4::new(announce_message_response.ips[i], announce_message_response.ports[i])) + } + + peer_addresses + } } /// A trait for converting a type into a byte buffer.