use byteorder::{ReadBytesExt, WriteBytesExt, NetworkEndian}; use std::convert::TryInto; use std::io; use std::io::{Read, Write}; use std::net::Ipv4Addr; use crate::types::*; use super::common::*; const MAGIC_NUMBER: i64 = 4_497_486_125_440; #[inline] pub fn request_to_bytes( bytes: &mut impl Write, request: Request ){ match request { Request::Connect(r) => { bytes.write_i64::(MAGIC_NUMBER).unwrap(); bytes.write_i32::(0).unwrap(); bytes.write_i32::(r.transaction_id.0).unwrap(); }, Request::Announce(r) => { bytes.write_i64::(r.connection_id.0).unwrap(); bytes.write_i32::(1).unwrap(); bytes.write_i32::(r.transaction_id.0).unwrap(); bytes.write_all(&r.info_hash.0).unwrap(); bytes.write_all(&r.peer_id.0).unwrap(); bytes.write_i64::(r.bytes_downloaded.0).unwrap(); bytes.write_i64::(r.bytes_left.0).unwrap(); bytes.write_i64::(r.bytes_uploaded.0).unwrap(); bytes.write_i32::(event_to_i32(r.event)).unwrap(); bytes.write_all(&r.ip_address.map_or([0; 4], |ip| ip.octets())).unwrap(); bytes.write_u32::(0).unwrap(); // IP bytes.write_u32::(r.key.0).unwrap(); bytes.write_i32::(r.peers_wanted.0).unwrap(); bytes.write_u16::(r.port.0).unwrap(); }, Request::Scrape(r) => { bytes.write_i64::(r.connection_id.0).unwrap(); bytes.write_i32::(2).unwrap(); bytes.write_i32::(r.transaction_id.0).unwrap(); for info_hash in &r.info_hashes { bytes.write_all(&info_hash.0).unwrap(); } } _ => () // Invalid requests should never happen } } #[inline] pub fn request_from_bytes( bytes: &[u8], max_scrape_torrents: u8, ) -> Result { let mut bytes = io::Cursor::new(bytes); let connection_id = bytes.read_i64::()?; let action = bytes.read_i32::()?; let transaction_id = bytes.read_i32::()?; match action { // Connect 0 => { if connection_id == MAGIC_NUMBER { Ok(Request::Connect(ConnectRequest { transaction_id:TransactionId(transaction_id) })) } else { Ok(Request::Invalid(InvalidRequest { transaction_id:TransactionId(transaction_id), message: "Please send protocol identifier in connect request" .to_string() })) } }, // Announce 1 => { let mut info_hash = [0; 20]; let mut peer_id = [0; 20]; let mut ip = [0; 4]; bytes.read_exact(&mut info_hash)?; bytes.read_exact(&mut peer_id)?; let bytes_downloaded = bytes.read_i64::()?; let bytes_left = bytes.read_i64::()?; let bytes_uploaded = bytes.read_i64::()?; let event = bytes.read_i32::()?; bytes.read_exact(&mut ip)?; let key = bytes.read_u32::()?; let peers_wanted = bytes.read_i32::()?; let port = bytes.read_u16::()?; let opt_ip = if ip == [0; 4] { None } else { Some(Ipv4Addr::from(ip)) }; Ok(Request::Announce(AnnounceRequest { connection_id: ConnectionId(connection_id), transaction_id: TransactionId(transaction_id), info_hash: InfoHash(info_hash), peer_id: PeerId(peer_id), bytes_downloaded: NumberOfBytes(bytes_downloaded), bytes_uploaded: NumberOfBytes(bytes_uploaded), bytes_left: NumberOfBytes(bytes_left), event: event_from_i32(event), ip_address: opt_ip, key: PeerKey(key), peers_wanted: NumberOfPeers(peers_wanted), port: Port(port) })) }, // Scrape 2 => { let position = bytes.position() as usize; let inner = bytes.into_inner(); let info_hashes = (&inner[position..]).chunks_exact(20) .take(max_scrape_torrents as usize) .map(|chunk| InfoHash(chunk.try_into().unwrap())) .collect(); Ok(Request::Scrape(ScrapeRequest { connection_id: ConnectionId(connection_id), transaction_id: TransactionId(transaction_id), info_hashes })) } _ => Ok(Request::Invalid(InvalidRequest { transaction_id: TransactionId(transaction_id), message: "Invalid action".to_string() })) } }