From 319d8e6d294d5317dd04240179d3875826d9e14e Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Fri, 21 Mar 2025 12:21:19 -0500 Subject: [PATCH] cargo fmt --- src/app_common.rs | 109 +++++---- src/avalanche.rs | 44 ++-- src/client.rs | 550 +++++++++++++++++++++--------------------- src/ip.rs | 184 +++++++------- src/main.rs | 84 ++++--- src/message.rs | 593 +++++++++++++++++++++------------------------- src/prelude.rs | 56 ++--- src/server.rs | 277 +++++++++++----------- src/tlv.rs | 207 ++++++++-------- 9 files changed, 1009 insertions(+), 1095 deletions(-) diff --git a/src/app_common.rs b/src/app_common.rs index 8bccdd6..14ebd17 100644 --- a/src/app_common.rs +++ b/src/app_common.rs @@ -1,73 +1,72 @@ use crate::prelude::*; -pub const LOOKAROUND_VERSION: &str = env! ("CARGO_PKG_VERSION"); +pub const LOOKAROUND_VERSION: &str = env!("CARGO_PKG_VERSION"); -pub fn find_project_dirs () -> Option { - ProjectDirs::from ("", "ReactorScram", "LookAround") +pub fn find_project_dirs() -> Option { + ProjectDirs::from("", "ReactorScram", "LookAround") } -#[derive (Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error)] pub enum AppError { - #[error (transparent)] - AddrParse (#[from] std::net::AddrParseError), - #[error (transparent)] - CliArgs (#[from] CliArgError), - #[error ("Operation timed out")] - Elapsed (#[from] tokio::time::error::Elapsed), - #[error (transparent)] - Io (#[from] std::io::Error), - #[error (transparent)] - Ip (#[from] crate::ip::IpError), - #[error (transparent)] - Join (#[from] tokio::task::JoinError), - #[error (transparent)] - MacAddr (#[from] mac_address::MacAddressError), - #[error (transparent)] - Message (#[from] crate::message::MessageError), - #[error (transparent)] - ParseInt (#[from] std::num::ParseIntError), - #[error (transparent)] - Tlv (#[from] crate::tlv::TlvError), + #[error(transparent)] + AddrParse(#[from] std::net::AddrParseError), + #[error(transparent)] + CliArgs(#[from] CliArgError), + #[error("Operation timed out")] + Elapsed(#[from] tokio::time::error::Elapsed), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Ip(#[from] crate::ip::IpError), + #[error(transparent)] + Join(#[from] tokio::task::JoinError), + #[error(transparent)] + MacAddr(#[from] mac_address::MacAddressError), + #[error(transparent)] + Message(#[from] crate::message::MessageError), + #[error(transparent)] + ParseInt(#[from] std::num::ParseIntError), + #[error(transparent)] + Tlv(#[from] crate::tlv::TlvError), } -#[derive (Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error)] pub enum CliArgError { - #[error ("Missing value for argument `{0}`")] - MissingArgumentValue (String), - #[error ("Missing required argument <{0}>")] - MissingRequiredArg (String), - #[error ("First argument should be a subcommand")] - MissingSubcommand, - #[error ("Unknown subcommand `{0}`")] - UnknownSubcommand (String), - #[error ("Unrecognized argument `{0}`")] - UnrecognizedArgument (String), + #[error("Missing value for argument `{0}`")] + MissingArgumentValue(String), + #[error("Missing required argument <{0}>")] + MissingRequiredArg(String), + #[error("First argument should be a subcommand")] + MissingSubcommand, + #[error("Unknown subcommand `{0}`")] + UnknownSubcommand(String), + #[error("Unrecognized argument `{0}`")] + UnrecognizedArgument(String), } -pub async fn recv_msg_from (socket: &UdpSocket) -> Result <(Vec , SocketAddr), AppError> -{ - let mut buf = vec! [0u8; PACKET_SIZE]; - let (bytes_recved, remote_addr) = socket.recv_from (&mut buf).await?; - buf.truncate (bytes_recved); - let msgs = Message::from_slice2 (&buf)?; - - Ok ((msgs, remote_addr)) +pub async fn recv_msg_from(socket: &UdpSocket) -> Result<(Vec, SocketAddr), AppError> { + let mut buf = vec![0u8; PACKET_SIZE]; + let (bytes_recved, remote_addr) = socket.recv_from(&mut buf).await?; + buf.truncate(bytes_recved); + let msgs = Message::from_slice2(&buf)?; + + Ok((msgs, remote_addr)) } -#[derive (Clone)] +#[derive(Clone)] pub struct Params { - // Servers bind on this port, clients must send to the port - pub server_port: u16, - - // Clients and servers will all join the same multicast addr - pub multicast_addr: Ipv4Addr, + // Servers bind on this port, clients must send to the port + pub server_port: u16, + + // Clients and servers will all join the same multicast addr + pub multicast_addr: Ipv4Addr, } impl Default for Params { - fn default () -> Self { - Self { - server_port: 9040, - multicast_addr: Ipv4Addr::new (225, 100, 99, 98), - } - } + fn default() -> Self { + Self { + server_port: 9040, + multicast_addr: Ipv4Addr::new(225, 100, 99, 98), + } + } } diff --git a/src/avalanche.rs b/src/avalanche.rs index 939bfb0..bba3541 100644 --- a/src/avalanche.rs +++ b/src/avalanche.rs @@ -1,40 +1,26 @@ type Mac = [u8; 6]; -pub fn debug () { - for input in [ - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1], - [1, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 1], - ] { - assert_eq! (unmix (mix (input)), input); - } - - println! ("Passed"); +pub fn debug() { + for input in [ + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1], + ] { + assert_eq!(unmix(mix(input)), input); + } + + println!("Passed"); } // NOT intended for any cryptography or security. This is TRIVIALLY reversible. // It's just to make it easier for humans to tell apart MACs where only a couple // numbers differ. -fn mix (i: Mac) -> Mac { - [ - i [0] ^ i [5], - i [1] ^ i [4], - i [2] ^ i [3], - i [3], - i [4], - i [5], - ] +fn mix(i: Mac) -> Mac { + [i[0] ^ i[5], i[1] ^ i[4], i[2] ^ i[3], i[3], i[4], i[5]] } -fn unmix (i: Mac) -> Mac { - [ - i [0] ^ i [5], - i [1] ^ i [4], - i [2] ^ i [3], - i [3], - i [4], - i [5], - ] +fn unmix(i: Mac) -> Mac { + [i[0] ^ i[5], i[1] ^ i[4], i[2] ^ i[3], i[3], i[4], i[5]] } diff --git a/src/client.rs b/src/client.rs index bb314f6..f04120f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,300 +1,310 @@ use crate::prelude::*; struct ServerResponse { - mac: Option <[u8; 6]>, - nickname: Option , + mac: Option<[u8; 6]>, + nickname: Option, } struct ConfigFile { - nicknames: HashMap , + nicknames: HashMap, } struct ClientParams { - common: app_common::Params, - bind_addrs: Vec , - nicknames: HashMap , - timeout_ms: u64, + common: app_common::Params, + bind_addrs: Vec, + nicknames: HashMap, + timeout_ms: u64, } -pub async fn client > (args: I) -> Result <(), AppError> { - match get_mac_address() { - Ok(Some(ma)) => { - println!("Our MAC addr = {}", ma); - } - Ok(None) => println!("No MAC address found."), - Err(e) => println!("{:?}", e), - } - - let params = configure_client (args)?; - let socket = make_socket (¶ms.common, params.bind_addrs).await?; - let msg = Message::new_request1 ().to_vec ()?; - tokio::spawn (send_requests (Arc::clone (&socket), params.common, msg)); - - let mut peers = HashMap::with_capacity (10); - - timeout (Duration::from_millis (params.timeout_ms), listen_for_responses (&socket, params.nicknames, &mut peers)).await.ok (); - - let mut peers: Vec <_> = peers.into_iter ().collect (); - peers.sort_by_key (|(_, v)| v.mac); - - println! ("Found {} peers:", peers.len ()); - for (ip, resp) in peers.into_iter () { - let mac = match resp.mac { - None => { - println! (" = {}", ip); - continue; - }, - Some (x) => x, - }; - - let nickname = match resp.nickname { - None => { - println! ("{} = {}", MacAddress::new (mac), ip.ip ()); - continue; - }, - Some (x) => x, - }; - - println! ("{} = {} `{}`", MacAddress::new (mac), ip.ip (), nickname); - } - - Ok (()) +pub async fn client>(args: I) -> Result<(), AppError> { + match get_mac_address() { + Ok(Some(ma)) => { + println!("Our MAC addr = {}", ma); + } + Ok(None) => println!("No MAC address found."), + Err(e) => println!("{:?}", e), + } + + let params = configure_client(args)?; + let socket = make_socket(¶ms.common, params.bind_addrs).await?; + let msg = Message::new_request1().to_vec()?; + tokio::spawn(send_requests(Arc::clone(&socket), params.common, msg)); + + let mut peers = HashMap::with_capacity(10); + + timeout( + Duration::from_millis(params.timeout_ms), + listen_for_responses(&socket, params.nicknames, &mut peers), + ) + .await + .ok(); + + let mut peers: Vec<_> = peers.into_iter().collect(); + peers.sort_by_key(|(_, v)| v.mac); + + println!("Found {} peers:", peers.len()); + for (ip, resp) in peers.into_iter() { + let mac = match resp.mac { + None => { + println!(" = {}", ip); + continue; + } + Some(x) => x, + }; + + let nickname = match resp.nickname { + None => { + println!("{} = {}", MacAddress::new(mac), ip.ip()); + continue; + } + Some(x) => x, + }; + + println!("{} = {} `{}`", MacAddress::new(mac), ip.ip(), nickname); + } + + Ok(()) } -pub async fn find_nick > (mut args: I) -> Result <(), AppError> -{ - let mut nick = None; - let mut timeout_ms = 500; - let ConfigFile { - nicknames, - } = load_config_file (); - - while let Some (arg) = args.next () { - match arg.as_str () { - "--timeout-ms" => { - timeout_ms = match args.next () { - None => return Err (CliArgError::MissingArgumentValue (arg).into ()), - Some (x) => u64::from_str (&x)?, - }; - }, - _ => nick = Some (arg), - } - } - - let needle_nick = nick.ok_or_else (|| CliArgError::MissingRequiredArg ("nickname".to_string ()))?; - let needle_nick = Some (needle_nick); - - let common_params = Default::default (); - - let socket = make_socket (&common_params, get_ips ()?).await?; - let msg = Message::new_request1 ().to_vec ()?; - tokio::spawn (send_requests (Arc::clone (&socket), common_params, msg)); - - timeout (Duration::from_millis (timeout_ms), async move { loop { - let (msgs, remote_addr) = match recv_msg_from (&socket).await { - Err (_) => continue, - Ok (x) => x, - }; - - let mut resp = ServerResponse { - mac: None, - nickname: None, - }; - - for msg in msgs.into_iter () { - match msg { - Message::Response1 (x) => resp.mac = x, - Message::Response2 (x) => resp.nickname = Some (x.nickname), - _ => (), - } - } - - resp.nickname = get_peer_nickname (&nicknames, resp.mac, resp.nickname); - - if resp.nickname == needle_nick { - println! ("{}", remote_addr.ip ()); - return; - } - }}).await?; - - Ok (()) +pub async fn find_nick>(mut args: I) -> Result<(), AppError> { + let mut nick = None; + let mut timeout_ms = 500; + let ConfigFile { nicknames } = load_config_file(); + + while let Some(arg) = args.next() { + match arg.as_str() { + "--timeout-ms" => { + timeout_ms = match args.next() { + None => return Err(CliArgError::MissingArgumentValue(arg).into()), + Some(x) => u64::from_str(&x)?, + }; + } + _ => nick = Some(arg), + } + } + + let needle_nick = + nick.ok_or_else(|| CliArgError::MissingRequiredArg("nickname".to_string()))?; + let needle_nick = Some(needle_nick); + + let common_params = Default::default(); + + let socket = make_socket(&common_params, get_ips()?).await?; + let msg = Message::new_request1().to_vec()?; + tokio::spawn(send_requests(Arc::clone(&socket), common_params, msg)); + + timeout(Duration::from_millis(timeout_ms), async move { + loop { + let (msgs, remote_addr) = match recv_msg_from(&socket).await { + Err(_) => continue, + Ok(x) => x, + }; + + let mut resp = ServerResponse { + mac: None, + nickname: None, + }; + + for msg in msgs.into_iter() { + match msg { + Message::Response1(x) => resp.mac = x, + Message::Response2(x) => resp.nickname = Some(x.nickname), + _ => (), + } + } + + resp.nickname = get_peer_nickname(&nicknames, resp.mac, resp.nickname); + + if resp.nickname == needle_nick { + println!("{}", remote_addr.ip()); + return; + } + } + }) + .await?; + + Ok(()) } -fn configure_client > (mut args: I) --> Result -{ - let mut bind_addrs = vec! []; - let mut timeout_ms = 500; - - let ConfigFile { - nicknames, - } = load_config_file (); - - while let Some (arg) = args.next () { - match arg.as_str () { - "--bind-addr" => { - bind_addrs.push (match args.next () { - None => return Err (CliArgError::MissingArgumentValue (arg).into ()), - Some (x) => Ipv4Addr::from_str (&x)?, - }); - }, - "--timeout-ms" => { - timeout_ms = match args.next () { - None => return Err (CliArgError::MissingArgumentValue (arg).into ()), - Some (x) => u64::from_str (&x)?, - }; - }, - _ => return Err (CliArgError::UnrecognizedArgument (arg).into ()), - } - } - - if bind_addrs.is_empty () { - bind_addrs = get_ips ()?; - } - - Ok (ClientParams { - common: Default::default (), - bind_addrs, - nicknames, - timeout_ms, - }) +fn configure_client>(mut args: I) -> Result { + let mut bind_addrs = vec![]; + let mut timeout_ms = 500; + + let ConfigFile { nicknames } = load_config_file(); + + while let Some(arg) = args.next() { + match arg.as_str() { + "--bind-addr" => { + bind_addrs.push(match args.next() { + None => return Err(CliArgError::MissingArgumentValue(arg).into()), + Some(x) => Ipv4Addr::from_str(&x)?, + }); + } + "--timeout-ms" => { + timeout_ms = match args.next() { + None => return Err(CliArgError::MissingArgumentValue(arg).into()), + Some(x) => u64::from_str(&x)?, + }; + } + _ => return Err(CliArgError::UnrecognizedArgument(arg).into()), + } + } + + if bind_addrs.is_empty() { + bind_addrs = get_ips()?; + } + + Ok(ClientParams { + common: Default::default(), + bind_addrs, + nicknames, + timeout_ms, + }) } -fn load_config_file () -> ConfigFile { - let mut nicknames: HashMap = Default::default (); - - if let Some (proj_dirs) = find_project_dirs () { - let mut ini = Ini::new_cs (); - let path = proj_dirs.config_local_dir ().join ("client.ini"); - if ini.load (&path).is_ok () { - let map_ref = ini.get_map_ref (); - if let Some (x) = map_ref.get ("nicknames") { - for (k, v) in x { - if let Some (v) = v { - let k = k.replace ('-', ":"); - nicknames.insert (k, v.to_string ()); - } - } - } - } - } - - ConfigFile { - nicknames, - } +fn load_config_file() -> ConfigFile { + let mut nicknames: HashMap = Default::default(); + + if let Some(proj_dirs) = find_project_dirs() { + let mut ini = Ini::new_cs(); + let path = proj_dirs.config_local_dir().join("client.ini"); + if ini.load(&path).is_ok() { + let map_ref = ini.get_map_ref(); + if let Some(x) = map_ref.get("nicknames") { + for (k, v) in x { + if let Some(v) = v { + let k = k.replace('-', ":"); + nicknames.insert(k, v.to_string()); + } + } + } + } + } + + ConfigFile { nicknames } } -async fn make_socket ( - common_params: &app_common::Params, - bind_addrs: Vec , -) -> Result , AppError> { - let socket = UdpSocket::bind (SocketAddrV4::new (Ipv4Addr::UNSPECIFIED, 0)).await?; - - for bind_addr in &bind_addrs { - if let Err (e) = socket.join_multicast_v4 (common_params.multicast_addr, *bind_addr) { - println! ("Error joining multicast group with iface {}: {:?}", bind_addr, e); - } - } - - Ok (Arc::new (socket)) +async fn make_socket( + common_params: &app_common::Params, + bind_addrs: Vec, +) -> Result, AppError> { + let socket = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)).await?; + + for bind_addr in &bind_addrs { + if let Err(e) = socket.join_multicast_v4(common_params.multicast_addr, *bind_addr) { + println!( + "Error joining multicast group with iface {}: {:?}", + bind_addr, e + ); + } + } + + Ok(Arc::new(socket)) } -async fn send_requests ( - socket: Arc , - params: app_common::Params, - msg: Vec , -) --> Result <(), AppError> -{ - for _ in 0..10 { - socket.send_to (&msg, (params.multicast_addr, params.server_port)).await?; - sleep (Duration::from_millis (100)).await; - } - - Ok::<_, AppError> (()) +async fn send_requests( + socket: Arc, + params: app_common::Params, + msg: Vec, +) -> Result<(), AppError> { + for _ in 0..10 { + socket + .send_to(&msg, (params.multicast_addr, params.server_port)) + .await?; + sleep(Duration::from_millis(100)).await; + } + + Ok::<_, AppError>(()) } -async fn listen_for_responses ( - socket: &UdpSocket, - nicknames: HashMap , - peers: &mut HashMap +async fn listen_for_responses( + socket: &UdpSocket, + nicknames: HashMap, + peers: &mut HashMap, ) { - loop { - let (msgs, remote_addr) = match recv_msg_from (socket).await { - Err (_) => continue, - Ok (x) => x, - }; - - let mut resp = ServerResponse { - mac: None, - nickname: None, - }; - - for msg in msgs.into_iter () { - match msg { - Message::Response1 (x) => resp.mac = x, - Message::Response2 (x) => resp.nickname = Some (x.nickname), - _ => (), - } - } - - resp.nickname = get_peer_nickname (&nicknames, resp.mac, resp.nickname); - - peers.insert (remote_addr, resp); - } + loop { + let (msgs, remote_addr) = match recv_msg_from(socket).await { + Err(_) => continue, + Ok(x) => x, + }; + + let mut resp = ServerResponse { + mac: None, + nickname: None, + }; + + for msg in msgs.into_iter() { + match msg { + Message::Response1(x) => resp.mac = x, + Message::Response2(x) => resp.nickname = Some(x.nickname), + _ => (), + } + } + + resp.nickname = get_peer_nickname(&nicknames, resp.mac, resp.nickname); + + peers.insert(remote_addr, resp); + } } -fn get_peer_nickname ( - nicknames: &HashMap , - mac: Option <[u8; 6]>, - peer_nickname: Option -) -> Option -{ - match peer_nickname.as_deref () { - None => (), - Some ("") => (), - _ => return peer_nickname, - } - - if let Some (mac) = &mac { - return nicknames.get (&format! ("{}", MacAddress::new (*mac))).cloned () - } - - None +fn get_peer_nickname( + nicknames: &HashMap, + mac: Option<[u8; 6]>, + peer_nickname: Option, +) -> Option { + match peer_nickname.as_deref() { + None => (), + Some("") => (), + _ => return peer_nickname, + } + + if let Some(mac) = &mac { + return nicknames + .get(&format!("{}", MacAddress::new(*mac))) + .cloned(); + } + + None } -#[cfg (test)] +#[cfg(test)] mod test { - use super::*; - - #[test] - fn test_nicknames () { - let mut nicks = HashMap::new (); - - for (k, v) in [ - ("01:01:01:01:01:01", "phoenix") - ] { - nicks.insert (k.to_string (), v.to_string ()); - } - - for (num, (mac, peer_nickname), expected) in [ - // Somehow the server returns no MAC nor nick. In this case we are helpless - ( 1, (None, None), None), - // If the server tells us its MAC, we can look up our nickname for it - ( 2, (Some ([1, 1, 1, 1, 1, 1]), None), Some ("phoenix")), - // Unless it's not in our nick list. - ( 3, (Some ([1, 1, 1, 1, 1, 2]), None), None), - // If the server tells us its nickname, that always takes priority - ( 4, (None, Some ("snowflake")), Some ("snowflake")), - ( 5, (Some ([1, 1, 1, 1, 1, 1]), Some ("snowflake")), Some ("snowflake")), - ( 6, (Some ([1, 1, 1, 1, 1, 2]), Some ("snowflake")), Some ("snowflake")), - // But blank nicknames are treated like None - ( 7, (None, Some ("")), None), - ( 8, (Some ([1, 1, 1, 1, 1, 1]), Some ("")), Some ("phoenix")), - ( 9, (Some ([1, 1, 1, 1, 1, 2]), Some ("")), None), - ] { - let actual = get_peer_nickname (&nicks, mac, peer_nickname.map (str::to_string)); - assert_eq! (actual.as_ref ().map (String::as_str), expected, "{}", num); - } - } + use super::*; + + #[test] + fn test_nicknames() { + let mut nicks = HashMap::new(); + + for (k, v) in [("01:01:01:01:01:01", "phoenix")] { + nicks.insert(k.to_string(), v.to_string()); + } + + for (num, (mac, peer_nickname), expected) in [ + // Somehow the server returns no MAC nor nick. In this case we are helpless + (1, (None, None), None), + // If the server tells us its MAC, we can look up our nickname for it + (2, (Some([1, 1, 1, 1, 1, 1]), None), Some("phoenix")), + // Unless it's not in our nick list. + (3, (Some([1, 1, 1, 1, 1, 2]), None), None), + // If the server tells us its nickname, that always takes priority + (4, (None, Some("snowflake")), Some("snowflake")), + ( + 5, + (Some([1, 1, 1, 1, 1, 1]), Some("snowflake")), + Some("snowflake"), + ), + ( + 6, + (Some([1, 1, 1, 1, 1, 2]), Some("snowflake")), + Some("snowflake"), + ), + // But blank nicknames are treated like None + (7, (None, Some("")), None), + (8, (Some([1, 1, 1, 1, 1, 1]), Some("")), Some("phoenix")), + (9, (Some([1, 1, 1, 1, 1, 2]), Some("")), None), + ] { + let actual = get_peer_nickname(&nicks, mac, peer_nickname.map(str::to_string)); + assert_eq!(actual.as_ref().map(String::as_str), expected, "{}", num); + } + } } diff --git a/src/ip.rs b/src/ip.rs index bbcac88..0fc9e7f 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -1,122 +1,112 @@ -use std::{ - net::Ipv4Addr, - process::Command, - str::FromStr, -}; +use std::{net::Ipv4Addr, process::Command, str::FromStr}; -#[derive (Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error)] pub enum IpError { - #[error (transparent)] - Io (#[from] std::io::Error), - #[error (transparent)] - FromUtf8 (#[from] std::string::FromUtf8Error), - #[error ("Self-IP detection is not implemented on Mac OS")] - NotImplementedOnMac, + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + FromUtf8(#[from] std::string::FromUtf8Error), + #[error("Self-IP detection is not implemented on Mac OS")] + NotImplementedOnMac, } #[cfg(target_os = "linux")] -pub fn get_ips () -> Result , IpError> { - let output = linux::get_ip_addr_output ()?; - - Ok (linux::parse_ip_addr_output (&output)) +pub fn get_ips() -> Result, IpError> { + let output = linux::get_ip_addr_output()?; + + Ok(linux::parse_ip_addr_output(&output)) } #[cfg(target_os = "macos")] -pub fn get_ips () -> Result , IpError> { - Err (IpError::NotImplementedOnMac) +pub fn get_ips() -> Result, IpError> { + Err(IpError::NotImplementedOnMac) } #[cfg(target_os = "windows")] -pub fn get_ips () -> Result , IpError> { - let output = windows::get_ip_config_output ()?; - - Ok (windows::parse_ip_config_output (&output)) +pub fn get_ips() -> Result, IpError> { + let output = windows::get_ip_config_output()?; + + Ok(windows::parse_ip_config_output(&output)) } #[cfg(target_os = "linux")] pub mod linux { - use super::*; - - pub fn get_ip_addr_output () -> Result { - let output = Command::new ("ip") - .arg ("addr") - .output ()?; - let output = output.stdout.as_slice (); - let output = String::from_utf8 (output.to_vec ())?; - Ok (output) - } + use super::*; - pub fn parse_ip_addr_output (output: &str) -> Vec { - // I wrote this in FP style because I was bored. - - output.lines () - .map (|l| l.trim_start ()) - .filter_map (|l| l.strip_prefix ("inet ")) - .filter_map (|l| l.find ('/').map (|x| &l [0..x])) - .filter_map (|l| Ipv4Addr::from_str (l).ok ()) - .filter (|a| ! a.is_loopback ()) - .collect () - } + pub fn get_ip_addr_output() -> Result { + let output = Command::new("ip").arg("addr").output()?; + let output = output.stdout.as_slice(); + let output = String::from_utf8(output.to_vec())?; + Ok(output) + } + + pub fn parse_ip_addr_output(output: &str) -> Vec { + // I wrote this in FP style because I was bored. + + output + .lines() + .map(|l| l.trim_start()) + .filter_map(|l| l.strip_prefix("inet ")) + .filter_map(|l| l.find('/').map(|x| &l[0..x])) + .filter_map(|l| Ipv4Addr::from_str(l).ok()) + .filter(|a| !a.is_loopback()) + .collect() + } } #[cfg(target_os = "windows")] pub mod windows { - use super::*; - - pub fn get_ip_config_output () -> Result { - let output = Command::new ("ipconfig") - .output ()?; - let output = output.stdout.as_slice (); - let output = String::from_utf8 (output.to_vec ())?; - Ok (output) - } + use super::*; - pub fn parse_ip_config_output (output: &str) -> Vec { - let mut addrs = vec! []; - - for line in output.lines () { - let line = line.trim_start (); - - // Maybe only works on English locales? - if ! line.starts_with ("IPv4 Address") { - continue; - } - let colon_pos = match line.find (':') { - None => continue, - Some (x) => x, - }; - let line = &line [colon_pos + 2..]; - - let addr = match Ipv4Addr::from_str (line) { - Err (_) => continue, - Ok (x) => x, - }; - - addrs.push (addr); - } - - addrs - } + pub fn get_ip_config_output() -> Result { + let output = Command::new("ipconfig").output()?; + let output = output.stdout.as_slice(); + let output = String::from_utf8(output.to_vec())?; + Ok(output) + } - #[cfg (test)] - mod test { - use super::*; - - #[test] - fn test () { - for (input, expected) in [ - ( - r" + pub fn parse_ip_config_output(output: &str) -> Vec { + let mut addrs = vec![]; + + for line in output.lines() { + let line = line.trim_start(); + + // Maybe only works on English locales? + if !line.starts_with("IPv4 Address") { + continue; + } + let colon_pos = match line.find(':') { + None => continue, + Some(x) => x, + }; + let line = &line[colon_pos + 2..]; + + let addr = match Ipv4Addr::from_str(line) { + Err(_) => continue, + Ok(x) => x, + }; + + addrs.push(addr); + } + + addrs + } + + #[cfg(test)] + mod test { + use super::*; + + #[test] + fn test() { + for (input, expected) in [( + r" IPv4 Address . . .. . . . : 192.168.1.1 ", - vec! [ - Ipv4Addr::new (192, 168, 1, 1), - ] - ), - ] { - let actual = parse_ip_config_output (input); - assert_eq! (actual, expected); - } - } - } + vec![Ipv4Addr::new(192, 168, 1, 1)], + )] { + let actual = parse_ip_config_output(input); + assert_eq!(actual, expected); + } + } + } } diff --git a/src/main.rs b/src/main.rs index 2fc482b..c771536 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,53 +9,51 @@ mod prelude; mod server; pub mod tlv; -fn main () -> Result <(), AppError> { - let rt = tokio::runtime::Builder::new_current_thread () - .enable_io () - .enable_time () - .build ()?; - - rt.block_on (async_main ())?; - - Ok (()) +fn main() -> Result<(), AppError> { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build()?; + + rt.block_on(async_main())?; + + Ok(()) } -async fn async_main () -> Result <(), AppError> { - let mut args = env::args (); - - let _exe_name = args.next (); - - let subcommand: Option = args.next (); - - match subcommand.as_ref ().map (|x| &x[..]) { - None => return Err (CliArgError::MissingSubcommand.into ()), - Some ("--version") => println! ("lookaround v{}", LOOKAROUND_VERSION), - Some ("client") => client::client (args).await?, - Some ("config") => config (), - Some ("debug-avalanche") => avalanche::debug (), - Some ("find-nick") => client::find_nick (args).await?, - Some ("my-ips") => my_ips ()?, - Some ("server") => server::server (args).await?, - Some (x) => return Err (CliArgError::UnknownSubcommand (x.to_string ()).into ()), - } - - Ok (()) +async fn async_main() -> Result<(), AppError> { + let mut args = env::args(); + + let _exe_name = args.next(); + + let subcommand: Option = args.next(); + + match subcommand.as_ref().map(|x| &x[..]) { + None => return Err(CliArgError::MissingSubcommand.into()), + Some("--version") => println!("lookaround v{}", LOOKAROUND_VERSION), + Some("client") => client::client(args).await?, + Some("config") => config(), + Some("debug-avalanche") => avalanche::debug(), + Some("find-nick") => client::find_nick(args).await?, + Some("my-ips") => my_ips()?, + Some("server") => server::server(args).await?, + Some(x) => return Err(CliArgError::UnknownSubcommand(x.to_string()).into()), + } + + Ok(()) } -fn config () { - if let Some (proj_dirs) = ProjectDirs::from ("", "ReactorScram", "LookAround") { - println! ("Using config dir {:?}", proj_dirs.config_local_dir ()); - } - else { - println! ("Can't detect config dir."); - } +fn config() { + if let Some(proj_dirs) = ProjectDirs::from("", "ReactorScram", "LookAround") { + println!("Using config dir {:?}", proj_dirs.config_local_dir()); + } else { + println!("Can't detect config dir."); + } } -fn my_ips () -> Result <(), AppError> { - for addr in ip::get_ips ()? - { - println! ("{:?}", addr); - } - - Ok (()) +fn my_ips() -> Result<(), AppError> { + for addr in ip::get_ips()? { + println!("{:?}", addr); + } + + Ok(()) } diff --git a/src/message.rs b/src/message.rs index 83ecfdb..cfc6064 100644 --- a/src/message.rs +++ b/src/message.rs @@ -5,343 +5,302 @@ pub const PACKET_SIZE: usize = 1024; type Mac = [u8; 6]; -#[derive (Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub enum Message { - // 1 - Request1 { - idem_id: [u8; 8], - mac: Option - }, - // 2 - Response1 (Option ), - // 3 - Response2 (Response2), + // 1 + Request1 { idem_id: [u8; 8], mac: Option }, + // 2 + Response1(Option), + // 3 + Response2(Response2), } impl Message { - pub fn new_request1 () -> Message { - let mut idem_id = [0u8; 8]; - rand::thread_rng ().fill_bytes (&mut idem_id); - - Message::Request1 { - idem_id, - mac: None, - } - } + pub fn new_request1() -> Message { + let mut idem_id = [0u8; 8]; + rand::thread_rng().fill_bytes(&mut idem_id); + + Message::Request1 { idem_id, mac: None } + } } -#[derive (Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Response2 { - pub idem_id: [u8; 8], - pub nickname: String, + pub idem_id: [u8; 8], + pub nickname: String, } -#[derive (Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error)] pub enum MessageError { - #[error (transparent)] - Io (#[from] std::io::Error), - #[error ("Length prefix too long")] - LengthPrefixTooLong ((usize, usize)), - #[error (transparent)] - Tlv (#[from] tlv::TlvError), - #[error (transparent)] - TryFromInt (#[from] std::num::TryFromIntError), - #[error ("Unknown type")] - UnknownType, - #[error (transparent)] - FromUtf8 (#[from] std::string::FromUtf8Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("Length prefix too long")] + LengthPrefixTooLong((usize, usize)), + #[error(transparent)] + Tlv(#[from] tlv::TlvError), + #[error(transparent)] + TryFromInt(#[from] std::num::TryFromIntError), + #[error("Unknown type")] + UnknownType, + #[error(transparent)] + FromUtf8(#[from] std::string::FromUtf8Error), } -#[derive (Default)] +#[derive(Default)] struct DummyWriter { - position: usize, + position: usize, } impl Write for DummyWriter { - fn flush (&mut self) -> std::io::Result <()> { - Ok (()) - } - - fn write (&mut self, buf: &[u8]) -> std::io::Result { - self.position += buf.len (); - Ok (buf.len ()) - } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.position += buf.len(); + Ok(buf.len()) + } } impl Message { - pub fn write (&self, w: &mut Cursor ) -> Result <(), MessageError> - where Cursor : Write - { - match self { - Self::Request1 { - idem_id, - mac, - }=> { - w.write_all (&[1])?; - w.write_all (&idem_id[..])?; - Self::write_mac_opt (w, *mac)?; - }, - Self::Response1 (mac) => { - w.write_all (&[2])?; - Self::write_mac_opt (w, *mac)?; - }, - Self::Response2 (x) => { - w.write_all (&[3])?; - // Measure length with dummy writes - // This is dumb, I'm just messing around to see if I can do - // this without allocating. - let mut dummy_writer = DummyWriter::default (); - - Self::write_response_2 (&mut dummy_writer, x)?; - - // Write length and real params to real output - let len = u32::try_from (dummy_writer.position)?; - w.write_all (&len.to_le_bytes ())?; - Self::write_response_2 (w, x)?; - }, - } - - Ok (()) - } - - fn write_response_2 (w: &mut W, params: &Response2) - -> Result <(), MessageError> - { - w.write_all (¶ms.idem_id)?; - let nickname = params.nickname.as_bytes (); - tlv::Writer::<_>::lv_bytes (w, nickname)?; - Ok (()) - } - - fn write_mac_opt (w: &mut W, mac: Option <[u8; 6]>) -> Result <(), std::io::Error> - { - match mac { - Some (mac) => { - w.write_all (&[1])?; - w.write_all (&mac[..])?; - }, - None => w.write_all (&[0])?, - } - Ok (()) - } - - pub fn to_vec (&self) -> Result , MessageError> { - let mut cursor = Cursor::new (Vec::with_capacity (PACKET_SIZE)); - cursor.write_all (&MAGIC_NUMBER)?; - self.write (&mut cursor)?; - Ok (cursor.into_inner ()) - } - - pub fn many_to_vec (msgs: &[Self]) -> Result , MessageError> { - let mut cursor = Cursor::new (Vec::with_capacity (PACKET_SIZE)); - cursor.write_all (&MAGIC_NUMBER)?; - for msg in msgs { - msg.write (&mut cursor)?; - } - Ok (cursor.into_inner ()) - } - - fn read2 (r: &mut R) -> Result { - let t = tlv::Reader::u8 (r)?; - - Ok (match t { - 1 => { - let mut idem_id = [0u8; 8]; - r.read_exact (&mut idem_id)?; - - let mac = Self::read_mac_opt (r)?; - Self::Request1 { - idem_id, - mac, - } - }, - 2 => { - let mac = Self::read_mac_opt (r)?; - Self::Response1 (mac) - }, - 3 => { - tlv::Reader::<_>::length (r)?; - - let mut idem_id = [0; 8]; - r.read_exact (&mut idem_id)?; - - let nickname = tlv::Reader::<_>::lv_bytes_to_vec (r, 64)?; - let nickname = String::from_utf8 (nickname)?; - - Self::Response2 (Response2 { - idem_id, - nickname, - }) - }, - _ => return Err (MessageError::UnknownType), - }) - } - - fn read_mac_opt (r: &mut R) - -> Result