mod download_peer; pub mod listen; pub mod upload_peer; // Export actor types pub use crate::actor::peer_actor::PeerMessage; pub use download_peer::part_path_of; pub use download_peer::{DownloadError, DownloadPeer}; use crate::message::Message; use core::fmt; use std::str::FromStr; #[derive(Debug, Clone)] pub enum ConnectionType { P, F, D, } #[derive(Debug, Clone)] pub struct ParseConnectionTypeError; impl fmt::Display for ParseConnectionTypeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "invalid connection type") } } impl std::error::Error for ParseConnectionTypeError {} impl FromStr for ConnectionType { type Err = ParseConnectionTypeError; fn from_str(s: &str) -> Result { match s { "P" => Ok(Self::P), "F" => Ok(Self::F), "D" => Ok(Self::D), _ => Err(ParseConnectionTypeError), } } } impl fmt::Display for ConnectionType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { Self::P => "P", Self::F => "F", Self::D => "D", }; write!(f, "{s}") } } #[derive(Debug, Clone)] pub struct Peer { pub username: String, pub connection_type: ConnectionType, pub host: String, pub port: u32, pub token: Option, pub privileged: Option, pub unknown: Option, pub obfuscated_port: Option, } impl Peer { #[allow(clippy::too_many_arguments)] #[must_use] pub const fn new( username: String, connection_type: ConnectionType, host: String, port: u32, token: Option, privileged: u8, unknown: u8, obfuscated_port: u16, ) -> Self { Self { username, connection_type, host, port, token, privileged: Some(privileged), unknown: Some(unknown), obfuscated_port: Some(obfuscated_port), } } pub fn new_from_message(message: &mut Message) -> Option { let username = message.read_string(); // The connection type is an untrusted string; an unknown value must not // panic the actor that parses it. let connection_type = message.read_string().parse().ok()?; let ip = [ message.read_int8(), message.read_int8(), message.read_int8(), message.read_int8(), ]; let host = format!("{}.{}.{}.{}", ip[3], ip[2], ip[1], ip[0]); let (port, token, privileged, unknown, obfuscated_port) = ( message.read_int32(), message.read_int32(), message.read_int8(), message.read_int8(), message.read_int8(), ); Some(Self { username, connection_type, host, port, token: Some(token), privileged: Some(privileged), unknown: Some(unknown), obfuscated_port: Some(u16::from(obfuscated_port)), }) } } #[test] fn new_accepts_a_full_range_obfuscated_port() { // A real obfuscated port is a u16; it must be stored without truncation. // (The GetPeerAddressResponse path previously narrowed it to u8 and // panicked for any value > 255, crashing the client's ops thread.) let peer = Peer::new( "u".to_string(), ConnectionType::P, "1.2.3.4".to_string(), 1, None, 0, 0, 51770, ); assert_eq!(peer.obfuscated_port, Some(51770)); } #[test] fn new_from_message_returns_none_on_invalid_connection_type() { // username "ab", connection_type "X" (not P/F/D) from an untrusted server. let mut data: Vec = vec![0, 0, 0, 0, 0, 0, 0, 0]; data.extend([2, 0, 0, 0, 97, 98]); // username = "ab" data.extend([1, 0, 0, 0, 88]); // connection_type = "X" data.extend([1, 2, 3, 4]); // ip data.extend([0, 0, 0, 0]); // port data.extend([0, 0, 0, 0]); // token data.extend([0, 0, 0]); // privileged, unknown, obfuscated_port let mut message = Message::new_with_data(data); message.set_pointer(8); assert!(Peer::new_from_message(&mut message).is_none()); } #[test] fn test_new_from_message() { let data: Vec = [ 36, 0, 0, 0, 18, 0, 0, 0, 2, 0, 0, 0, 100, 112, 1, 0, 0, 0, 80, 27, 231, 37, 45, 186, 8, 0, 0, 178, 78, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ] .to_vec(); let mut message = Message::new_with_data(data); message.set_pointer(8); let peer = Peer::new_from_message(&mut message).unwrap(); assert_eq!(peer.username, "dp"); assert!(matches!(peer.connection_type, ConnectionType::P)); assert_eq!(peer.host, "45.37.231.27"); assert_eq!(peer.port, 2234); assert_eq!(peer.token, Some(1658546)); assert_eq!(peer.privileged, Some(0)); assert_eq!(peer.unknown, Some(0)); assert_eq!(peer.obfuscated_port, Some(0)); } #[test] fn test_new_from_message2() { let data: Vec = [ 42, 0, 0, 0, 18, 0, 0, 0, 8, 0, 0, 0, 103, 114, 97, 110, 100, 112, 97, 103, 1, 0, 0, 0, 80, 137, 128, 193, 68, 187, 8, 0, 0, 58, 16, 0, 0, 0, 1, 0, 0, 0, 188, 8, 0, 0, ] .to_vec(); let mut message = Message::new_with_data(data); message.set_pointer(8); println!("code: {}", message.get_message_code()); let peer = Peer::new_from_message(&mut message).unwrap(); assert_eq!(peer.username, "grandpag"); assert!(matches!(peer.connection_type, ConnectionType::P)); assert_eq!(peer.host, "68.193.128.137"); assert_eq!(peer.port, 2235); assert_eq!(peer.token, Some(4154)); assert_eq!(peer.privileged, Some(0)); assert_eq!(peer.unknown, Some(1)); assert_eq!(peer.obfuscated_port, Some(0)); }