Compare commits

..

No commits in common. "319d8e6d294d5317dd04240179d3875826d9e14e" and "2cc1ed4a9282eef99c006e0a7fd306712d5d9cac" have entirely different histories.

11 changed files with 1136 additions and 1035 deletions

60
Cargo.lock generated
View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]] [[package]]
name = "autocfg" name = "autocfg"
@ -15,10 +15,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]] [[package]]
name = "bitflags" name = "cc"
version = "2.9.0" version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -26,12 +26,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "configparser" name = "configparser"
version = "3.0.0" version = "3.0.0"
@ -71,9 +65,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.170" version = "0.2.135"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[package]] [[package]]
name = "log" name = "log"
@ -91,7 +85,7 @@ dependencies = [
"configparser", "configparser",
"directories", "directories",
"mac_address", "mac_address",
"nix", "nix 0.25.0",
"rand", "rand",
"thiserror", "thiserror",
"tokio", "tokio",
@ -99,19 +93,19 @@ dependencies = [
[[package]] [[package]]
name = "mac_address" name = "mac_address"
version = "1.1.8" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" checksum = "89544d9544366f6cda81244514a80809b137b5a179947b73bfa9f2797480de69"
dependencies = [ dependencies = [
"nix", "nix 0.22.2",
"winapi", "winapi",
] ]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.9.1" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@ -140,17 +134,31 @@ dependencies = [
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.29.0" version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags",
"cc",
"cfg-if", "cfg-if",
"cfg_aliases",
"libc", "libc",
"memoffset", "memoffset",
] ]
[[package]]
name = "nix"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
dependencies = [
"autocfg",
"bitflags",
"cfg-if",
"libc",
"memoffset",
"pin-utils",
]
[[package]] [[package]]
name = "ntapi" name = "ntapi"
version = "0.3.6" version = "0.3.6"
@ -166,6 +174,12 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.15" version = "0.2.15"
@ -236,7 +250,7 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
] ]
[[package]] [[package]]

View File

@ -2,7 +2,7 @@
authors = ["Trish"] authors = ["Trish"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
description = "Report and find your MAC and IP addresses within LANs" description = "Report and find your MAC and IP addresses within LANs"
edition = "2024" edition = "2021"
homepage = "https://six-five-six-four.com/git/reactor/lookaround" homepage = "https://six-five-six-four.com/git/reactor/lookaround"
keywords = ["address", "discovery", "ip", "network"] keywords = ["address", "discovery", "ip", "network"]
license = "AGPL-3.0" license = "AGPL-3.0"
@ -14,8 +14,8 @@ version = "0.1.6"
[dependencies] [dependencies]
configparser = "3.0.0" configparser = "3.0.0"
directories = "5.0.0" directories = "5.0.0"
mac_address = "1.1.8" mac_address = "1.1.2"
nix = "0.29.0" nix = "0.25.0"
rand = "0.8.4" rand = "0.8.4"
thiserror = "1.0.30" thiserror = "1.0.30"
tokio = { version = "1.14.0", features = ["fs", "net", "rt", "time"] } tokio = { version = "1.14.0", features = ["fs", "net", "rt", "time"] }

View File

@ -44,7 +44,8 @@ pub enum CliArgError {
UnrecognizedArgument (String), UnrecognizedArgument (String),
} }
pub async fn recv_msg_from(socket: &UdpSocket) -> Result<(Vec<Message>, SocketAddr), AppError> { pub async fn recv_msg_from (socket: &UdpSocket) -> Result <(Vec <Message>, SocketAddr), AppError>
{
let mut buf = vec! [0u8; PACKET_SIZE]; let mut buf = vec! [0u8; PACKET_SIZE];
let (bytes_recved, remote_addr) = socket.recv_from (&mut buf).await?; let (bytes_recved, remote_addr) = socket.recv_from (&mut buf).await?;
buf.truncate (bytes_recved); buf.truncate (bytes_recved);

View File

@ -18,9 +18,23 @@ pub fn debug() {
// numbers differ. // numbers differ.
fn mix (i: Mac) -> Mac { fn mix (i: Mac) -> Mac {
[i[0] ^ i[5], i[1] ^ i[4], i[2] ^ i[3], i[3], i[4], i[5]] [
i [0] ^ i [5],
i [1] ^ i [4],
i [2] ^ i [3],
i [3],
i [4],
i [5],
]
} }
fn unmix (i: Mac) -> Mac { fn unmix (i: Mac) -> Mac {
[i[0] ^ i[5], i[1] ^ i[4], i[2] ^ i[3], i[3], i[4], i[5]] [
i [0] ^ i [5],
i [1] ^ i [4],
i [2] ^ i [3],
i [3],
i [4],
i [5],
]
} }

View File

@ -32,12 +32,7 @@ pub async fn client<I: Iterator<Item = String>>(args: I) -> Result<(), AppError>
let mut peers = HashMap::with_capacity (10); let mut peers = HashMap::with_capacity (10);
timeout( timeout (Duration::from_millis (params.timeout_ms), listen_for_responses (&*socket, params.nicknames, &mut peers)).await.ok ();
Duration::from_millis(params.timeout_ms),
listen_for_responses(&socket, params.nicknames, &mut peers),
)
.await
.ok();
let mut peers: Vec <_> = peers.into_iter ().collect (); let mut peers: Vec <_> = peers.into_iter ().collect ();
peers.sort_by_key (|(_, v)| v.mac); peers.sort_by_key (|(_, v)| v.mac);
@ -48,7 +43,7 @@ pub async fn client<I: Iterator<Item = String>>(args: I) -> Result<(), AppError>
None => { None => {
println! ("<Unknown> = {}", ip); println! ("<Unknown> = {}", ip);
continue; continue;
} },
Some (x) => x, Some (x) => x,
}; };
@ -56,7 +51,7 @@ pub async fn client<I: Iterator<Item = String>>(args: I) -> Result<(), AppError>
None => { None => {
println! ("{} = {}", MacAddress::new (mac), ip.ip ()); println! ("{} = {}", MacAddress::new (mac), ip.ip ());
continue; continue;
} },
Some (x) => x, Some (x) => x,
}; };
@ -66,10 +61,13 @@ pub async fn client<I: Iterator<Item = String>>(args: I) -> Result<(), AppError>
Ok (()) Ok (())
} }
pub async fn find_nick<I: Iterator<Item = String>>(mut args: I) -> Result<(), AppError> { pub async fn find_nick <I: Iterator <Item=String>> (mut args: I) -> Result <(), AppError>
{
let mut nick = None; let mut nick = None;
let mut timeout_ms = 500; let mut timeout_ms = 500;
let ConfigFile { nicknames } = load_config_file(); let ConfigFile {
nicknames,
} = load_config_file ();
while let Some (arg) = args.next () { while let Some (arg) = args.next () {
match arg.as_str () { match arg.as_str () {
@ -78,13 +76,12 @@ pub async fn find_nick<I: Iterator<Item = String>>(mut args: I) -> Result<(), Ap
None => return Err (CliArgError::MissingArgumentValue (arg).into ()), None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => u64::from_str (&x)?, Some (x) => u64::from_str (&x)?,
}; };
} },
_ => nick = Some (arg), _ => nick = Some (arg),
} }
} }
let needle_nick = let needle_nick = nick.ok_or_else (|| CliArgError::MissingRequiredArg ("nickname".to_string ()))?;
nick.ok_or_else(|| CliArgError::MissingRequiredArg("nickname".to_string()))?;
let needle_nick = Some (needle_nick); let needle_nick = Some (needle_nick);
let common_params = Default::default (); let common_params = Default::default ();
@ -93,8 +90,7 @@ pub async fn find_nick<I: Iterator<Item = String>>(mut args: I) -> Result<(), Ap
let msg = Message::new_request1 ().to_vec ()?; let msg = Message::new_request1 ().to_vec ()?;
tokio::spawn (send_requests (Arc::clone (&socket), common_params, msg)); tokio::spawn (send_requests (Arc::clone (&socket), common_params, msg));
timeout(Duration::from_millis(timeout_ms), async move { timeout (Duration::from_millis (timeout_ms), async move { loop {
loop {
let (msgs, remote_addr) = match recv_msg_from (&socket).await { let (msgs, remote_addr) = match recv_msg_from (&socket).await {
Err (_) => continue, Err (_) => continue,
Ok (x) => x, Ok (x) => x,
@ -119,18 +115,20 @@ pub async fn find_nick<I: Iterator<Item = String>>(mut args: I) -> Result<(), Ap
println! ("{}", remote_addr.ip ()); println! ("{}", remote_addr.ip ());
return; return;
} }
} }}).await?;
})
.await?;
Ok (()) Ok (())
} }
fn configure_client<I: Iterator<Item = String>>(mut args: I) -> Result<ClientParams, AppError> { fn configure_client <I: Iterator <Item=String>> (mut args: I)
-> Result <ClientParams, AppError>
{
let mut bind_addrs = vec! []; let mut bind_addrs = vec! [];
let mut timeout_ms = 500; let mut timeout_ms = 500;
let ConfigFile { nicknames } = load_config_file(); let ConfigFile {
nicknames,
} = load_config_file ();
while let Some (arg) = args.next () { while let Some (arg) = args.next () {
match arg.as_str () { match arg.as_str () {
@ -139,13 +137,13 @@ fn configure_client<I: Iterator<Item = String>>(mut args: I) -> Result<ClientPar
None => return Err (CliArgError::MissingArgumentValue (arg).into ()), None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => Ipv4Addr::from_str (&x)?, Some (x) => Ipv4Addr::from_str (&x)?,
}); });
} },
"--timeout-ms" => { "--timeout-ms" => {
timeout_ms = match args.next () { timeout_ms = match args.next () {
None => return Err (CliArgError::MissingArgumentValue (arg).into ()), None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => u64::from_str (&x)?, Some (x) => u64::from_str (&x)?,
}; };
} },
_ => return Err (CliArgError::UnrecognizedArgument (arg).into ()), _ => return Err (CliArgError::UnrecognizedArgument (arg).into ()),
} }
} }
@ -181,7 +179,9 @@ fn load_config_file() -> ConfigFile {
} }
} }
ConfigFile { nicknames } ConfigFile {
nicknames,
}
} }
async fn make_socket ( async fn make_socket (
@ -192,10 +192,7 @@ async fn make_socket(
for bind_addr in &bind_addrs { for bind_addr in &bind_addrs {
if let Err (e) = socket.join_multicast_v4 (common_params.multicast_addr, *bind_addr) { if let Err (e) = socket.join_multicast_v4 (common_params.multicast_addr, *bind_addr) {
println!( println! ("Error joining multicast group with iface {}: {:?}", bind_addr, e);
"Error joining multicast group with iface {}: {:?}",
bind_addr, e
);
} }
} }
@ -206,11 +203,11 @@ async fn send_requests(
socket: Arc <UdpSocket>, socket: Arc <UdpSocket>,
params: app_common::Params, params: app_common::Params,
msg: Vec <u8>, msg: Vec <u8>,
) -> Result<(), AppError> { )
-> Result <(), AppError>
{
for _ in 0..10 { for _ in 0..10 {
socket socket.send_to (&msg, (params.multicast_addr, params.server_port)).await?;
.send_to(&msg, (params.multicast_addr, params.server_port))
.await?;
sleep (Duration::from_millis (100)).await; sleep (Duration::from_millis (100)).await;
} }
@ -220,7 +217,7 @@ async fn send_requests(
async fn listen_for_responses ( async fn listen_for_responses (
socket: &UdpSocket, socket: &UdpSocket,
nicknames: HashMap <String, String>, nicknames: HashMap <String, String>,
peers: &mut HashMap<SocketAddr, ServerResponse>, peers: &mut HashMap <SocketAddr, ServerResponse>
) { ) {
loop { loop {
let (msgs, remote_addr) = match recv_msg_from (socket).await { let (msgs, remote_addr) = match recv_msg_from (socket).await {
@ -250,8 +247,9 @@ async fn listen_for_responses(
fn get_peer_nickname ( fn get_peer_nickname (
nicknames: &HashMap <String, String>, nicknames: &HashMap <String, String>,
mac: Option <[u8; 6]>, mac: Option <[u8; 6]>,
peer_nickname: Option<String>, peer_nickname: Option <String>
) -> Option<String> { ) -> Option <String>
{
match peer_nickname.as_deref () { match peer_nickname.as_deref () {
None => (), None => (),
Some ("") => (), Some ("") => (),
@ -259,9 +257,7 @@ fn get_peer_nickname(
} }
if let Some (mac) = &mac { if let Some (mac) = &mac {
return nicknames return nicknames.get (&format! ("{}", MacAddress::new (*mac))).cloned ()
.get(&format!("{}", MacAddress::new(*mac)))
.cloned();
} }
None None
@ -275,7 +271,9 @@ mod test {
fn test_nicknames () { fn test_nicknames () {
let mut nicks = HashMap::new (); let mut nicks = HashMap::new ();
for (k, v) in [("01:01:01:01:01:01", "phoenix")] { for (k, v) in [
("01:01:01:01:01:01", "phoenix")
] {
nicks.insert (k.to_string (), v.to_string ()); nicks.insert (k.to_string (), v.to_string ());
} }
@ -288,16 +286,8 @@ mod test {
( 3, (Some ([1, 1, 1, 1, 1, 2]), None), None), ( 3, (Some ([1, 1, 1, 1, 1, 2]), None), None),
// If the server tells us its nickname, that always takes priority // If the server tells us its nickname, that always takes priority
( 4, (None, Some ("snowflake")), Some ("snowflake")), ( 4, (None, Some ("snowflake")), Some ("snowflake")),
( ( 5, (Some ([1, 1, 1, 1, 1, 1]), Some ("snowflake")), Some ("snowflake")),
5, ( 6, (Some ([1, 1, 1, 1, 1, 2]), Some ("snowflake")), Some ("snowflake")),
(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 // But blank nicknames are treated like None
( 7, (None, Some ("")), None), ( 7, (None, Some ("")), None),
( 8, (Some ([1, 1, 1, 1, 1, 1]), Some ("")), Some ("phoenix")), ( 8, (Some ([1, 1, 1, 1, 1, 1]), Some ("")), Some ("phoenix")),

View File

@ -1,4 +1,8 @@
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 { pub enum IpError {
@ -34,7 +38,9 @@ pub mod linux {
use super::*; use super::*;
pub fn get_ip_addr_output () -> Result <String, IpError> { pub fn get_ip_addr_output () -> Result <String, IpError> {
let output = Command::new("ip").arg("addr").output()?; let output = Command::new ("ip")
.arg ("addr")
.output ()?;
let output = output.stdout.as_slice (); let output = output.stdout.as_slice ();
let output = String::from_utf8 (output.to_vec ())?; let output = String::from_utf8 (output.to_vec ())?;
Ok (output) Ok (output)
@ -43,8 +49,7 @@ pub mod linux {
pub fn parse_ip_addr_output (output: &str) -> Vec <Ipv4Addr> { pub fn parse_ip_addr_output (output: &str) -> Vec <Ipv4Addr> {
// I wrote this in FP style because I was bored. // I wrote this in FP style because I was bored.
output output.lines ()
.lines()
.map (|l| l.trim_start ()) .map (|l| l.trim_start ())
.filter_map (|l| l.strip_prefix ("inet ")) .filter_map (|l| l.strip_prefix ("inet "))
.filter_map (|l| l.find ('/').map (|x| &l [0..x])) .filter_map (|l| l.find ('/').map (|x| &l [0..x]))
@ -59,7 +64,8 @@ pub mod windows {
use super::*; use super::*;
pub fn get_ip_config_output () -> Result <String, IpError> { pub fn get_ip_config_output () -> Result <String, IpError> {
let output = Command::new("ipconfig").output()?; let output = Command::new ("ipconfig")
.output ()?;
let output = output.stdout.as_slice (); let output = output.stdout.as_slice ();
let output = String::from_utf8 (output.to_vec ())?; let output = String::from_utf8 (output.to_vec ())?;
Ok (output) Ok (output)
@ -98,12 +104,16 @@ pub mod windows {
#[test] #[test]
fn test () { fn test () {
for (input, expected) in [( for (input, expected) in [
(
r" r"
IPv4 Address . . .. . . . : 192.168.1.1 IPv4 Address . . .. . . . : 192.168.1.1
", ",
vec![Ipv4Addr::new(192, 168, 1, 1)], vec! [
)] { Ipv4Addr::new (192, 168, 1, 1),
]
),
] {
let actual = parse_ip_config_output (input); let actual = parse_ip_config_output (input);
assert_eq! (actual, expected); assert_eq! (actual, expected);
} }

View File

@ -45,13 +45,15 @@ async fn async_main() -> Result<(), AppError> {
fn config () { fn config () {
if let Some (proj_dirs) = ProjectDirs::from ("", "ReactorScram", "LookAround") { if let Some (proj_dirs) = ProjectDirs::from ("", "ReactorScram", "LookAround") {
println! ("Using config dir {:?}", proj_dirs.config_local_dir ()); println! ("Using config dir {:?}", proj_dirs.config_local_dir ());
} else { }
else {
println! ("Can't detect config dir."); println! ("Can't detect config dir.");
} }
} }
fn my_ips () -> Result <(), AppError> { fn my_ips () -> Result <(), AppError> {
for addr in ip::get_ips()? { for addr in ip::get_ips ()?
{
println! ("{:?}", addr); println! ("{:?}", addr);
} }

View File

@ -8,7 +8,10 @@ type Mac = [u8; 6];
#[derive (Debug, PartialEq)] #[derive (Debug, PartialEq)]
pub enum Message { pub enum Message {
// 1 // 1
Request1 { idem_id: [u8; 8], mac: Option<Mac> }, Request1 {
idem_id: [u8; 8],
mac: Option <Mac>
},
// 2 // 2
Response1 (Option <Mac>), Response1 (Option <Mac>),
// 3 // 3
@ -20,7 +23,10 @@ impl Message {
let mut idem_id = [0u8; 8]; let mut idem_id = [0u8; 8];
rand::thread_rng ().fill_bytes (&mut idem_id); rand::thread_rng ().fill_bytes (&mut idem_id);
Message::Request1 { idem_id, mac: None } Message::Request1 {
idem_id,
mac: None,
}
} }
} }
@ -64,19 +70,21 @@ impl Write for DummyWriter {
impl Message { impl Message {
pub fn write <T> (&self, w: &mut Cursor <T>) -> Result <(), MessageError> pub fn write <T> (&self, w: &mut Cursor <T>) -> Result <(), MessageError>
where where Cursor <T>: Write
Cursor<T>: Write,
{ {
match self { match self {
Self::Request1 { idem_id, mac } => { Self::Request1 {
idem_id,
mac,
}=> {
w.write_all (&[1])?; w.write_all (&[1])?;
w.write_all (&idem_id[..])?; w.write_all (&idem_id[..])?;
Self::write_mac_opt (w, *mac)?; Self::write_mac_opt (w, *mac)?;
} },
Self::Response1 (mac) => { Self::Response1 (mac) => {
w.write_all (&[2])?; w.write_all (&[2])?;
Self::write_mac_opt (w, *mac)?; Self::write_mac_opt (w, *mac)?;
} },
Self::Response2 (x) => { Self::Response2 (x) => {
w.write_all (&[3])?; w.write_all (&[3])?;
// Measure length with dummy writes // Measure length with dummy writes
@ -90,25 +98,28 @@ impl Message {
let len = u32::try_from (dummy_writer.position)?; let len = u32::try_from (dummy_writer.position)?;
w.write_all (&len.to_le_bytes ())?; w.write_all (&len.to_le_bytes ())?;
Self::write_response_2 (w, x)?; Self::write_response_2 (w, x)?;
} },
} }
Ok (()) Ok (())
} }
fn write_response_2<W: Write>(w: &mut W, params: &Response2) -> Result<(), MessageError> { fn write_response_2 <W: Write> (w: &mut W, params: &Response2)
-> Result <(), MessageError>
{
w.write_all (&params.idem_id)?; w.write_all (&params.idem_id)?;
let nickname = params.nickname.as_bytes (); let nickname = params.nickname.as_bytes ();
tlv::Writer::<_>::lv_bytes (w, nickname)?; tlv::Writer::<_>::lv_bytes (w, nickname)?;
Ok (()) Ok (())
} }
fn write_mac_opt<W: Write>(w: &mut W, mac: Option<[u8; 6]>) -> Result<(), std::io::Error> { fn write_mac_opt <W: Write> (w: &mut W, mac: Option <[u8; 6]>) -> Result <(), std::io::Error>
{
match mac { match mac {
Some (mac) => { Some (mac) => {
w.write_all (&[1])?; w.write_all (&[1])?;
w.write_all (&mac[..])?; w.write_all (&mac[..])?;
} },
None => w.write_all (&[0])?, None => w.write_all (&[0])?,
} }
Ok (()) Ok (())
@ -139,12 +150,15 @@ impl Message {
r.read_exact (&mut idem_id)?; r.read_exact (&mut idem_id)?;
let mac = Self::read_mac_opt (r)?; let mac = Self::read_mac_opt (r)?;
Self::Request1 { idem_id, mac } Self::Request1 {
idem_id,
mac,
} }
},
2 => { 2 => {
let mac = Self::read_mac_opt (r)?; let mac = Self::read_mac_opt (r)?;
Self::Response1 (mac) Self::Response1 (mac)
} },
3 => { 3 => {
tlv::Reader::<_>::length (r)?; tlv::Reader::<_>::length (r)?;
@ -154,18 +168,24 @@ impl Message {
let nickname = tlv::Reader::<_>::lv_bytes_to_vec (r, 64)?; let nickname = tlv::Reader::<_>::lv_bytes_to_vec (r, 64)?;
let nickname = String::from_utf8 (nickname)?; let nickname = String::from_utf8 (nickname)?;
Self::Response2(Response2 { idem_id, nickname }) Self::Response2 (Response2 {
} idem_id,
nickname,
})
},
_ => return Err (MessageError::UnknownType), _ => return Err (MessageError::UnknownType),
}) })
} }
fn read_mac_opt<R: std::io::Read>(r: &mut R) -> Result<Option<[u8; 6]>, std::io::Error> { fn read_mac_opt <R: std::io::Read> (r: &mut R)
-> Result <Option <[u8; 6]>, std::io::Error>
{
Ok (if tlv::Reader::u8 (r)? == 1 { Ok (if tlv::Reader::u8 (r)? == 1 {
let mut mac = [0u8; 6]; let mut mac = [0u8; 6];
r.read_exact (&mut mac)?; r.read_exact (&mut mac)?;
Some (mac) Some (mac)
} else { }
else {
None None
}) })
} }
@ -192,14 +212,19 @@ mod test {
fn test_write_2 () -> Result <(), MessageError> { fn test_write_2 () -> Result <(), MessageError> {
for (input, expected) in [ for (input, expected) in [
( (
vec![Message::Request1 {
idem_id: [1, 2, 3, 4, 5, 6, 7, 8],
mac: None,
}],
vec! [ vec! [
154, 74, 67, 129, // Request tag Message::Request1 {
1, // Idem ID idem_id: [1, 2, 3, 4, 5, 6, 7, 8,],
1, 2, 3, 4, 5, 6, 7, 8, // MAC is None mac: None,
},
],
vec! [
154, 74, 67, 129,
// Request tag
1,
// Idem ID
1, 2, 3, 4, 5, 6, 7, 8,
// MAC is None
0, 0,
], ],
), ),
@ -207,20 +232,28 @@ mod test {
vec! [ vec! [
Message::Response1 (Some ([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])), Message::Response1 (Some ([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])),
Message::Response2 (Response2 { Message::Response2 (Response2 {
idem_id: [1, 2, 3, 4, 5, 6, 7, 8], idem_id: [1, 2, 3, 4, 5, 6, 7, 8,],
nickname: ":V".to_string (), nickname: ":V".to_string (),
}), }),
], ],
vec! [ vec! [
// Magic number for LookAround packets // Magic number for LookAround packets
154, 74, 67, 129, // Response1 tag 154, 74, 67, 129,
2, // MAC is Some // Response1 tag
1, // MAC 2,
17, 34, 51, 68, 85, 102, // Response2 tag // MAC is Some
3, // Length prefix 1,
14, 0, 0, 0, // Idem ID // MAC
1, 2, 3, 4, 5, 6, 7, 8, // Length-prefixed string 17, 34, 51, 68, 85, 102,
2, 0, 0, 0, 58, 86, // Response2 tag
3,
// Length prefix
14, 0, 0, 0,
// Idem ID
1, 2, 3, 4, 5, 6, 7, 8,
// Length-prefixed string
2, 0, 0, 0,
58, 86,
], ],
), ),
] { ] {
@ -240,9 +273,12 @@ mod test {
mac: None, mac: None,
}, },
vec! [ vec! [
154, 74, 67, 129, // Request tag 154, 74, 67, 129,
1, // Idem ID // Request tag
1, 2, 3, 4, 5, 6, 7, 8, // MAC is None 1,
// Idem ID
1, 2, 3, 4, 5, 6, 7, 8,
// MAC is None
0, 0,
], ],
), ),
@ -250,9 +286,12 @@ mod test {
Message::Response1 (Some ([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])), Message::Response1 (Some ([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])),
vec! [ vec! [
// Magic number for LookAround packets // Magic number for LookAround packets
154, 74, 67, 129, // Response tag 154, 74, 67, 129,
2, // MAC is Some // Response tag
1, // MAC 2,
// MAC is Some
1,
// MAC
17, 34, 51, 68, 85, 102, 17, 34, 51, 68, 85, 102,
], ],
), ),
@ -260,14 +299,14 @@ mod test {
Message::Response1 (None), Message::Response1 (None),
vec! [ vec! [
// Magic number for LookAround packets // Magic number for LookAround packets
154, 74, 67, 129, // Response tag 154, 74, 67, 129,
2, // MAC is None // Response tag
2,
// MAC is None
0, 0,
], ],
), ),
] ].into_iter () {
.into_iter()
{
let actual = input.to_vec ()?; let actual = input.to_vec ()?;
assert_eq! (actual, expected, "{:?}", input); assert_eq! (actual, expected, "{:?}", input);
} }
@ -278,24 +317,26 @@ mod test {
#[test] #[test]
fn test_read_2 () -> Result <(), MessageError> { fn test_read_2 () -> Result <(), MessageError> {
for input in [ for input in [
vec![Message::Request1 { vec! [
Message::Request1 {
idem_id: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], idem_id: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
mac: None, mac: None,
}], },
vec![Message::Response1(Some([ ],
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, vec! [
]))], Message::Response1 (Some ([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])),
vec![Message::Response1(None)], ],
vec! [
Message::Response1 (None),
],
vec! [ vec! [
Message::Response1 (Some ([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])), Message::Response1 (Some ([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])),
Message::Response2 (Response2 { Message::Response2 (Response2 {
idem_id: [1, 2, 3, 4, 5, 6, 7, 8], idem_id: [1, 2, 3, 4, 5, 6, 7, 8,],
nickname: ":V".to_string (), nickname: ":V".to_string (),
}), }),
], ],
] ].into_iter () {
.into_iter()
{
let encoded = Message::many_to_vec (&input)?; let encoded = Message::many_to_vec (&input)?;
let decoded = Message::from_slice2 (&encoded)?; let decoded = Message::from_slice2 (&encoded)?;
assert_eq! (input, decoded); assert_eq! (input, decoded);

View File

@ -1,27 +1,52 @@
pub use std::{ pub use std::{
collections::HashMap, collections::HashMap,
env, env,
io::{Cursor, Write}, io::{
net::{Ipv4Addr, SocketAddr, SocketAddrV4}, Cursor,
Write,
},
net::{
Ipv4Addr,
SocketAddr,
SocketAddrV4,
},
str::FromStr, str::FromStr,
sync::Arc, sync::Arc,
time::Duration, time::{
Duration,
Instant,
},
}; };
pub use configparser::ini::Ini; pub use configparser::ini::Ini;
pub use directories::ProjectDirs; pub use directories::ProjectDirs;
pub use mac_address::{MacAddress, get_mac_address}; pub use mac_address::{
MacAddress,
get_mac_address,
};
pub use rand::RngCore; pub use rand::RngCore;
pub use tokio::{ pub use tokio::{
net::UdpSocket, net::UdpSocket,
time::{sleep, timeout}, time::{
sleep,
timeout,
},
}; };
pub use crate::{ pub use crate::{
app_common::{ app_common::{
self, AppError, CliArgError, LOOKAROUND_VERSION, find_project_dirs, recv_msg_from, self,
LOOKAROUND_VERSION,
AppError,
CliArgError,
find_project_dirs,
recv_msg_from,
}, },
ip::get_ips, ip::get_ips,
message::{self, Message, PACKET_SIZE}, message::{
self,
PACKET_SIZE,
Message,
},
tlv, tlv,
}; };

View File

@ -8,7 +8,8 @@ struct Params {
our_mac: Option <[u8; 6]>, our_mac: Option <[u8; 6]>,
} }
pub async fn server<I: Iterator<Item = String>>(args: I) -> Result<(), AppError> { pub async fn server <I: Iterator <Item=String>> (args: I) -> Result <(), AppError>
{
match get_mac_address() { match get_mac_address() {
Ok(Some(ma)) => { Ok(Some(ma)) => {
println!("Our MAC addr = {}", ma); println!("Our MAC addr = {}", ma);
@ -19,18 +20,11 @@ pub async fn server<I: Iterator<Item = String>>(args: I) -> Result<(), AppError>
let params = configure (args)?; let params = configure (args)?;
let socket = UdpSocket::bind(SocketAddrV4::new( let socket = UdpSocket::bind (SocketAddrV4::new (Ipv4Addr::UNSPECIFIED, params.common.server_port)).await?;
Ipv4Addr::UNSPECIFIED,
params.common.server_port,
))
.await?;
for bind_addr in &params.bind_addrs { for bind_addr in &params.bind_addrs {
if let Err (e) = socket.join_multicast_v4 (params.common.multicast_addr, *bind_addr) { if let Err (e) = socket.join_multicast_v4 (params.common.multicast_addr, *bind_addr) {
println!( println! ("Error joining multicast group with iface {}: {:?}", bind_addr, e);
"Error joining multicast group with iface {}: {:?}",
bind_addr, e
);
} }
} }
@ -39,7 +33,8 @@ pub async fn server<I: Iterator<Item = String>>(args: I) -> Result<(), AppError>
Ok (()) Ok (())
} }
fn configure<I: Iterator<Item = String>>(mut args: I) -> Result<Params, AppError> { fn configure <I: Iterator <Item=String>> (mut args: I) -> Result <Params, AppError>
{
let common = app_common::Params::default (); let common = app_common::Params::default ();
let mut bind_addrs = vec![]; let mut bind_addrs = vec![];
let mut nickname = String::new (); let mut nickname = String::new ();
@ -52,13 +47,12 @@ fn configure<I: Iterator<Item = String>>(mut args: I) -> Result<Params, AppError
nickname = x; nickname = x;
eprintln! ("Loaded nickname {:?}", nickname); eprintln! ("Loaded nickname {:?}", nickname);
} }
} else {
eprintln!(
"Can't load ini from {:?}, didn't load default configs",
path
);
} }
} else { else {
eprintln! ("Can't load ini from {:?}, didn't load default configs", path);
}
}
else {
eprintln! ("Can't find config dir, didn't load default configs"); eprintln! ("Can't find config dir, didn't load default configs");
} }
@ -69,22 +63,20 @@ fn configure<I: Iterator<Item = String>>(mut args: I) -> Result<Params, AppError
None => return Err (CliArgError::MissingArgumentValue (arg).into ()), None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some (x) => Ipv4Addr::from_str (&x)?, Some (x) => Ipv4Addr::from_str (&x)?,
}); });
} },
"--nickname" => { "--nickname" => {
nickname = match args.next () { nickname = match args.next () {
None => return Err (CliArgError::MissingArgumentValue (arg).into ()), None => return Err (CliArgError::MissingArgumentValue (arg).into ()),
Some(x) => x, Some (x) => x
}; };
} },
_ => return Err (CliArgError::UnrecognizedArgument (arg).into ()), _ => return Err (CliArgError::UnrecognizedArgument (arg).into ()),
} }
} }
let our_mac = get_mac_address ()?.map (|x| x.bytes ()); let our_mac = get_mac_address ()?.map (|x| x.bytes ());
if our_mac.is_none () { if our_mac.is_none () {
println!( println! ("Warning: Can't find our own MAC address. We won't be able to respond to MAC-specific lookaround requests");
"Warning: Can't find our own MAC address. We won't be able to respond to MAC-specific lookaround requests"
);
} }
if bind_addrs.is_empty () { if bind_addrs.is_empty () {
@ -100,7 +92,12 @@ fn configure<I: Iterator<Item = String>>(mut args: I) -> Result<Params, AppError
}) })
} }
async fn serve_interface(params: Params, socket: UdpSocket) -> Result<(), AppError> { async fn serve_interface (
params: Params,
socket: UdpSocket,
)
-> Result <(), AppError>
{
let mut recent_idem_ids = Vec::with_capacity (32); let mut recent_idem_ids = Vec::with_capacity (32);
loop { loop {
@ -110,7 +107,7 @@ async fn serve_interface(params: Params, socket: UdpSocket) -> Result<(), AppErr
Err (e) => { Err (e) => {
println! ("Error while receiving message: {:?}", e); println! ("Error while receiving message: {:?}", e);
continue; continue;
} },
}; };
let req = match req_msgs.into_iter ().next () { let req = match req_msgs.into_iter ().next () {
@ -118,14 +115,18 @@ async fn serve_interface(params: Params, socket: UdpSocket) -> Result<(), AppErr
_ => { _ => {
println! ("Don't know how to handle this message, ignoring"); println! ("Don't know how to handle this message, ignoring");
continue; continue;
} },
}; };
let resp = match req { let resp = match req {
Message::Request1 { mac: None, idem_id } => { Message::Request1 {
mac: None,
idem_id,
} => {
if recent_idem_ids.contains (&idem_id) { if recent_idem_ids.contains (&idem_id) {
None None
} else { }
else {
recent_idem_ids.insert (0, idem_id); recent_idem_ids.insert (0, idem_id);
recent_idem_ids.truncate (30); recent_idem_ids.truncate (30);
Some (vec! [ Some (vec! [
@ -136,14 +137,12 @@ async fn serve_interface(params: Params, socket: UdpSocket) -> Result<(), AppErr
}), }),
]) ])
} }
} },
_ => continue, _ => continue,
}; };
if let Some (resp) = resp { if let Some (resp) = resp {
socket socket.send_to (&Message::many_to_vec (&resp)?, remote_addr).await?;
.send_to(&Message::many_to_vec(&resp)?, remote_addr)
.await?;
} }
} }
} }

View File

@ -11,6 +11,7 @@ pub enum TlvError {
// To live is to suffer, // To live is to suffer,
// The data is too big, // The data is too big,
// For the gosh-darn buffer. // For the gosh-darn buffer.
#[error ("Data too big")] #[error ("Data too big")]
DataTooBig, DataTooBig,
#[error (transparent)] #[error (transparent)]
@ -103,7 +104,11 @@ mod test {
let v = w.into_inner (); let v = w.into_inner ();
assert_eq!(v, vec![8, 0, 0, 0, 104, 105, 32, 116, 104, 101, 114, 101,]); assert_eq! (v, vec! [
8, 0, 0, 0,
104, 105, 32,
116, 104, 101, 114, 101,
]);
let mut r = Cursor::new (v); let mut r = Cursor::new (v);