amog
This commit is contained in:
+3
-3
@@ -123,7 +123,7 @@ impl NUDState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// dumb boolean: Some(true)=on, Some(false)=off, None=shrug
|
/// dumb boolean: Some(true)=on, Some(false)=off, None=shrug
|
||||||
pub fn dumber_state_this_way(&self) -> Option<bool> {
|
pub fn _dumber_state_this_way(&self) -> Option<bool> {
|
||||||
match self {
|
match self {
|
||||||
NUDState::Permanent | NUDState::Reachable => Some(true),
|
NUDState::Permanent | NUDState::Reachable => Some(true),
|
||||||
NUDState::Failed => Some(false),
|
NUDState::Failed => Some(false),
|
||||||
@@ -199,7 +199,7 @@ impl IpNeighLine {
|
|||||||
..self_
|
..self_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_mac(mac: MacAddr) -> impl FnMut(Self) -> Self {
|
pub fn _with_mac(mac: MacAddr) -> impl FnMut(Self) -> Self {
|
||||||
move |self_| Self {
|
move |self_| Self {
|
||||||
mac: Some(mac),
|
mac: Some(mac),
|
||||||
..self_
|
..self_
|
||||||
@@ -235,7 +235,7 @@ impl Ord for NUDState {
|
|||||||
|
|
||||||
impl IpNeighLine {
|
impl IpNeighLine {
|
||||||
// score for “local and online”: state, has-mac, v4, iface preference
|
// score for “local and online”: state, has-mac, v4, iface preference
|
||||||
pub fn score(&self) -> (u8, u8, u8, u8) {
|
pub fn _score(&self) -> (u8, u8, u8, u8) {
|
||||||
let iface = self
|
let iface = self
|
||||||
.dev
|
.dev
|
||||||
.as_deref()
|
.as_deref()
|
||||||
|
|||||||
+3
-3
@@ -115,11 +115,11 @@ async fn main() -> color_eyre::Result<()> {
|
|||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
fn main() -> color_eyre::Result<()> {
|
fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
use std::net::ToSocketAddrs;
|
||||||
// use crate::arpparse::NUDState;
|
// use crate::arpparse::NUDState;
|
||||||
// println!("{}", NUDState::Reachable.to_string().to_lowercase());
|
// println!("{}", NUDState::Reachable.to_string().to_lowercase());
|
||||||
// println!("{:?}", std::net::TcpStream::connect("svuhuvshdv:331"));
|
println!("{:?}", "svuhuvshdv:331".to_socket_addrs());
|
||||||
// // Err(Os { code: 11001, kind: Uncategorized, message: "No such host is known." })
|
// Err(Os { code: 11001, kind: Uncategorized, message: "No such host is known." })
|
||||||
Err(color_eyre::eyre::eyre!(
|
Err(color_eyre::eyre::eyre!(
|
||||||
"OS not supported! run this on your ahh router!"
|
"OS not supported! run this on your ahh router!"
|
||||||
))
|
))
|
||||||
|
|||||||
+6
-7
@@ -11,23 +11,22 @@ pub mod wake;
|
|||||||
use std::{net::IpAddr, sync::LazyLock};
|
use std::{net::IpAddr, sync::LazyLock};
|
||||||
|
|
||||||
use macaddr::MacAddr;
|
use macaddr::MacAddr;
|
||||||
use tokio::io;
|
pub mod error;
|
||||||
|
|
||||||
/// generic so you can do "123.45.67.89:22" or "lda.lan:22" as an input
|
/// generic so you can do "123.45.67.89:22" or "lda.lan:22" as an input
|
||||||
// this is so bad
|
// this is so bad
|
||||||
pub mod ping;
|
pub mod ping;
|
||||||
|
|
||||||
/// this is because i like [`IpAddr`] more than [`SocketAddr`](std::net::SocketAddr)
|
/// this is because i like [`IpAddr`] more than [`SocketAddr`](std::net::SocketAddr)
|
||||||
pub async fn get_ips(machine_name: &str) -> io::Result<Vec<IpAddr>> {
|
pub async fn get_ips(machine_name: &str) -> error::Result<Vec<IpAddr>> {
|
||||||
Ok(tokio::net::lookup_host((machine_name, 0))
|
let it = tokio::net::lookup_host((machine_name, 0))
|
||||||
.await?
|
.await
|
||||||
.map(|c| c.ip())
|
.map_err(|e| error::Error::DnsResolve { name: machine_name.to_string(), source: e })?;
|
||||||
.collect())
|
Ok(it.map(|c| c.ip()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod cmd;
|
pub mod cmd;
|
||||||
pub mod query;
|
pub mod query;
|
||||||
mod error;
|
|
||||||
|
|
||||||
// no custom ip deserializer needed when using axum_extra::extract::Query
|
// no custom ip deserializer needed when using axum_extra::extract::Query
|
||||||
// but we add a generic one to ignore blanks and accept OneOrMany
|
// but we add a generic one to ignore blanks and accept OneOrMany
|
||||||
|
|||||||
+43
-12
@@ -1,17 +1,48 @@
|
|||||||
use std::io;
|
use std::{fmt, io};
|
||||||
|
|
||||||
enum cuh<T> {
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
DNSError(io::Error),
|
|
||||||
ToolUseError,
|
#[derive(Debug)]
|
||||||
Other(T),
|
pub enum Error {
|
||||||
|
/// Failure to resolve a host name to IPs.
|
||||||
|
DnsResolve { name: String, source: io::Error },
|
||||||
|
/// External command failed (e.g., ip neigh)
|
||||||
|
CommandFailed {
|
||||||
|
cmd: &'static str,
|
||||||
|
args: Vec<String>,
|
||||||
|
status: Option<i32>,
|
||||||
|
stderr: String,
|
||||||
|
},
|
||||||
|
/// Generic IO error fallback
|
||||||
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
impl<T> From<io::Error> for cuh<T> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fn from(value: io::Error) -> Self {
|
match self {
|
||||||
todo!()
|
Error::DnsResolve { name, source } => write!(f, "DNS resolve failed for {name}: {source}"),
|
||||||
// if matches!(value.kind(), ) {
|
Error::CommandFailed { cmd, args, status, stderr } => {
|
||||||
|
let code = status.map(|c| c.to_string()).unwrap_or_else(|| "signal".into());
|
||||||
// }
|
write!(
|
||||||
|
f,
|
||||||
|
"{cmd} {args:?} failed (status: {code}): {stderr}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Error::Io(e) => write!(f, "IO error: {e}"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Error::DnsResolve { source, .. } => Some(source),
|
||||||
|
Error::Io(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(e: io::Error) -> Self { Error::Io(e) }
|
||||||
|
}
|
||||||
+23
-21
@@ -1,14 +1,14 @@
|
|||||||
use std::{collections::HashSet, net::IpAddr};
|
use std::{collections::HashSet, net::IpAddr};
|
||||||
|
|
||||||
use macaddr::MacAddr;
|
use macaddr::MacAddr;
|
||||||
use tokio::io;
|
// use tokio::io;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arpparse::{self, IpNeighLine, NUDState},
|
arpparse::{self, IpNeighLine, NUDState},
|
||||||
utils::{cmd::exec_command, get_ips},
|
utils::{cmd::exec_command, error::{self, Error, Result}, get_ips},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn get_macs_2_1(machine_name: &str) -> io::Result<HashSet<(IpAddr, MacAddr, NUDState)>> {
|
pub async fn get_macs_2_1(machine_name: &str) -> Result<HashSet<(IpAddr, MacAddr, NUDState)>> {
|
||||||
Ok(get_macs_1(machine_name)
|
Ok(get_macs_1(machine_name)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -22,7 +22,7 @@ pub async fn get_macs_2_1(machine_name: &str) -> io::Result<HashSet<(IpAddr, Mac
|
|||||||
)
|
)
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
pub async fn get_macs_2_mac(machine_name: &str) -> io::Result<HashSet<MacAddr>> {
|
pub async fn get_macs_2_mac(machine_name: &str) -> Result<HashSet<MacAddr>> {
|
||||||
Ok(get_macs_1(machine_name)
|
Ok(get_macs_1(machine_name)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -37,20 +37,22 @@ pub async fn get_macs_2_mac(machine_name: &str) -> io::Result<HashSet<MacAddr>>
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_macs_1(machine_name: &str) -> io::Result<Vec<arpparse::IpNeighLine>> {
|
pub async fn get_macs_1(machine_name: &str) -> Result<Vec<arpparse::IpNeighLine>> {
|
||||||
let dev = "br-lan";
|
let dev = "br-lan";
|
||||||
let ips = get_ips(machine_name).await?;
|
let ips = get_ips(machine_name).await?;
|
||||||
let futures = ips.iter().map(|ip| {
|
let futures = ips.iter().map(|ip| {
|
||||||
let ip = ip.to_canonical();
|
let ip = ip.to_canonical();
|
||||||
async move {
|
async move {
|
||||||
let o =
|
let cmd = "ip";
|
||||||
exec_command("ip", ["neigh", "show", "to", &ip.to_string(), "dev", dev]).await?;
|
let args = ["neigh", "show", "to", &ip.to_string(), "dev", dev];
|
||||||
|
let o = exec_command(cmd, args).await?;
|
||||||
if !o.status.success() {
|
if !o.status.success() {
|
||||||
return Err(io::Error::other(format!(
|
return Err(Error::CommandFailed {
|
||||||
"`ip neigh` failed for {ip} (status: {st}): {err}",
|
cmd,
|
||||||
st = o.status,
|
args: args.iter().map(ToString::to_string).collect(),
|
||||||
err = String::from_utf8_lossy(&o.stderr),
|
status: o.status.code(),
|
||||||
)));
|
stderr: String::from_utf8_lossy(&o.stderr).into(),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
Ok(String::from_utf8_lossy(&o.stdout)
|
Ok(String::from_utf8_lossy(&o.stdout)
|
||||||
.lines()
|
.lines()
|
||||||
@@ -72,7 +74,7 @@ pub async fn get_macs(
|
|||||||
ips: Option<&[IpAddr]>,
|
ips: Option<&[IpAddr]>,
|
||||||
dev: Option<&str>,
|
dev: Option<&str>,
|
||||||
state: Option<NUDState>,
|
state: Option<NUDState>,
|
||||||
) -> io::Result<Vec<IpNeighLine>> {
|
) -> Result<Vec<IpNeighLine>> {
|
||||||
// Collect IPs early (before any await) to avoid holding generics across await points
|
// Collect IPs early (before any await) to avoid holding generics across await points
|
||||||
let ip_list: Option<Vec<IpAddr>> = ips.map(|slice| {
|
let ip_list: Option<Vec<IpAddr>> = ips.map(|slice| {
|
||||||
slice
|
slice
|
||||||
@@ -110,12 +112,12 @@ pub async fn get_macs(
|
|||||||
|
|
||||||
let o = exec_command("ip", args.iter().map(String::as_str).collect::<Vec<_>>()).await?; // hope to rustc that it knows how to unfuck ts
|
let o = exec_command("ip", args.iter().map(String::as_str).collect::<Vec<_>>()).await?; // hope to rustc that it knows how to unfuck ts
|
||||||
if !o.status.success() {
|
if !o.status.success() {
|
||||||
return Err(io::Error::other(format!(
|
return Err(Error::CommandFailed {
|
||||||
"`ip neigh` failed{ctx} (status: {st}): {err}",
|
cmd: "ip",
|
||||||
ctx = to_ip.map(|ip| format!(" for {ip}")).unwrap_or_default(),
|
args,
|
||||||
st = o.status,
|
status: o.status.code(),
|
||||||
err = String::from_utf8_lossy(&o.stderr),
|
stderr: String::from_utf8_lossy(&o.stderr).into(),
|
||||||
)));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let lines = String::from_utf8_lossy(&o.stdout);
|
let lines = String::from_utf8_lossy(&o.stdout);
|
||||||
@@ -126,13 +128,13 @@ pub async fn get_macs(
|
|||||||
} else {
|
} else {
|
||||||
parsed.collect()
|
parsed.collect()
|
||||||
};
|
};
|
||||||
Ok::<Vec<IpNeighLine>, io::Error>(rows)
|
Ok::<Vec<IpNeighLine>, error::Error>(rows)
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we have specific IPs, query each; otherwise query the whole table once
|
// If we have specific IPs, query each; otherwise query the whole table once
|
||||||
if !ip_list.is_empty() {
|
if !ip_list.is_empty() {
|
||||||
let futures = ip_list.into_iter().map(|ip| run_one(Some(ip)));
|
let futures = ip_list.into_iter().map(|ip| run_one(Some(ip)));
|
||||||
let res = futures::future::try_join_all(futures).await?;
|
let res = futures::future::try_join_all(futures).await?;
|
||||||
Ok(res.into_iter().flatten().collect())
|
Ok(res.into_iter().flatten().collect())
|
||||||
} else {
|
} else {
|
||||||
run_one(None).await
|
run_one(None).await
|
||||||
|
|||||||
Reference in New Issue
Block a user