use std::{ collections::HashMap, env, net::{ Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket, }, time::{Duration, Instant}, }; use mac_address::{ MacAddress, get_mac_address, }; use thiserror::Error; mod message; mod tlv; use message::{ PACKET_SIZE, Message, }; #[derive (Debug, Error)] enum AppError { #[error (transparent)] CliArgs (#[from] CliArgError), #[error (transparent)] Io (#[from] std::io::Error), #[error (transparent)] MacAddr (#[from] mac_address::MacAddressError), #[error (transparent)] Message (#[from] message::MessageError), #[error (transparent)] Tlv (#[from] tlv::TlvError), } #[derive (Debug, Error)] enum CliArgError { #[error ("First argument should be a subcommand")] MissingSubcommand, #[error ("Unknown subcommand `{0}`")] UnknownSubcommand (String), } struct CommonParams { // Servers bind on this port, clients must send to the port server_port: u16, // Clients and servers will all join the same multicast addr multicast_addr: Ipv4Addr, } impl Default for CommonParams { fn default () -> Self { Self { server_port: 9040, multicast_addr: Ipv4Addr::new (225, 100, 99, 98), } } } fn main () -> Result <(), AppError> { let mut args = env::args (); let _exe_name = args.next (); match get_mac_address() { Ok(Some(ma)) => { println!("Our MAC addr = {}", ma); } Ok(None) => println!("No MAC address found."), Err(e) => println!("{:?}", e), } match args.next ().as_ref ().map (|s| &s[..]) { None => return Err (CliArgError::MissingSubcommand.into ()), Some ("client") => client ()?, Some ("server") => server ()?, Some (x) => return Err (CliArgError::UnknownSubcommand (x.to_string ()).into ()), } Ok (()) } fn client () -> Result <(), AppError> { let params = CommonParams::default (); let socket = UdpSocket::bind ("0.0.0.0:0")?; socket.join_multicast_v4 (¶ms.multicast_addr, &([0u8, 0, 0, 0].into ()))?; socket.set_read_timeout (Some (Duration::from_millis (1_000)))?; let msg = Message::Request (None).to_vec ()?; socket.send_to (&msg, (params.multicast_addr, params.server_port))?; let start_time = Instant::now (); let mut peers = HashMap::with_capacity (10); while Instant::now () < start_time + Duration::from_secs (2) { let (resp, remote_addr) = match recv_msg_from (&socket) { Err (_) => continue, Ok (x) => x, }; let peer_mac_addr = match resp { Message::Response (mac) => mac, _ => continue, }; peers.insert (peer_mac_addr, remote_addr); } let mut peers: Vec <_> = peers.into_iter ().collect (); peers.sort (); println! ("Found {} peers:", peers.len ()); for (mac, ip) in &peers { match mac { Some (mac) => println! ("{} = {}", MacAddress::new (*mac), ip), None => println! (" = {}", ip), } } Ok (()) } fn server () -> Result <(), AppError> { let our_mac = get_mac_address ()?.map (|x| x.bytes ()); if our_mac.is_none () { println! ("Warning: Can't find our own MAC address. We won't be able to respond to MAC-specific lookaround requests"); } let params = CommonParams::default (); let socket = UdpSocket::bind (SocketAddrV4::new (Ipv4Addr::UNSPECIFIED, params.server_port)).unwrap (); socket.join_multicast_v4 (¶ms.multicast_addr, &([0u8, 0, 0, 0].into ())).unwrap (); loop { let (req, remote_addr) = recv_msg_from (&socket)?; let resp = match req { Message::Request (None) => { Some (Message::Response (our_mac)) }, _ => continue, }; if let Some (resp) = resp { socket.send_to (&resp.to_vec ()?, remote_addr).unwrap (); } } } fn recv_msg_from (socket: &UdpSocket) -> Result <(Message, SocketAddr), AppError> { let mut buf = vec! [0u8; PACKET_SIZE]; let (bytes_recved, remote_addr) = socket.recv_from (&mut buf)?; buf.truncate (bytes_recved); let msg = Message::from_slice (&buf)?; Ok ((msg, remote_addr)) }