This commit is contained in:
lda
2025-08-22 17:27:08 +07:00 Unverified
parent a88682f1c2
commit 48fb5bbac7
6 changed files with 226 additions and 24 deletions
+8
View File
@@ -0,0 +1,8 @@
// struct arp;
// async fn read_arp() -> io::Result<()> {
// let arp_file = tokio::fs::File::open("/proc/net/arp").await?;
// let arp_read = BufReader::new(arp_file);
// Ok(())
// }
+71 -8
View File
@@ -1,41 +1,104 @@
use axum::{Router, response::Html, routing::get};
use std::net::SocketAddr;
use axum::{
Router,
response::Html,
routing::get,
};
use tokio::net::TcpListener;
mod arpparse;
mod utils;
use utils::*;
const MACHINE_NAME: &str = "lda.lan";
async fn home() -> Html<String> {
Html(format!(
r#"
<html>
<body>
<p>the machine is {}!</p>
<p>the machine is {}! <a href="/status">Status</a></p>
<form method="POST" action="/wake">
<button type="submit">Wake LDA</button>
</form>
</body>
</html>
"#,
if ping_ip("192.168.100.94:22").await {
"on"
} else {
"off"
match get_ips(MACHINE_NAME).await {
Ok(ips) => {
let addrs: Vec<SocketAddr> = ips.into_iter().map(|ip|(ip, 22).into()).collect();
if ping_ip(&*addrs).await { "on" } else { "off" }
}
Err(_) => "off",
}
))
}
async fn wake_handler() -> &'static str {
match wake("lda.lan").await {
match wake(MACHINE_NAME).await {
Err(_) => "Wake failed",
Ok(_) => "Packet sent!",
}
}
async fn status() -> Html<String> {
let formatted_ips = match get_ips(MACHINE_NAME).await {
Ok(ips) => {
let string: String = ips
.iter()
.map(|ip| format!("<tr><td>{ip}</td></tr>"))
.collect();
format!(
"<p>the ips of {m} are:</p>
<table>
<tr><th>IP</th></tr>
{string}
</table>",
m = MACHINE_NAME )
}
Err(e) => format!("<p>error getting ips: {e}</p>"),
};
let formatted_macs = match get_macs(MACHINE_NAME).await {
Ok(table) => {
let the: String = table
.iter()
.map(|(ip, mac)| {
let mac_str = if let Some(mac) = mac {
back_to_str(mac)
} else {
"None".into()
};
format!("<tr><td>{ip}</td><td>{mac_str}</td></tr>")
})
.collect();
format!(
"<p>the macs here:</p>
<table>
<tr><th>IP</th><th>MAC</th></tr>
{the}
</table>"
)
}
Err(e) => format!("<p>cant get macs either: {e}</p>"),
};
Html(format!(
r#"
<html>
<body>
{formatted_ips}
{formatted_macs}
</body>
</html>
"#,
))
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let app = Router::new()
.route("/", get(home))
.route("/wake", axum::routing::post(wake_handler));
.route("/wake", axum::routing::post(wake_handler))
.route("/status", get(status));
let port = TcpListener::bind("0.0.0.0:12012").await?;
axum::serve(port, app.into_make_service()).await?;
+68 -14
View File
@@ -1,13 +1,15 @@
pub const LDA_MACS: [[u8; 6]; 2] = [
[0x04, 0x7c, 0x16, 0x79, 0x6d, 0xee],
[0xbc, 0x09, 0x1b, 0xec, 0x65, 0xd0],
];
]; // is it time to lookup host lda.lan for this...
pub async fn wake(_machine_name: &str) -> io::Result<()> {
pub async fn wake(machine_name: &str) -> io::Result<()> {
let suh = UdpSocket::bind("0.0.0.0:0").await?;
suh.set_broadcast(true)?;
for mac in LDA_MACS {
for (_, mac) in get_macs(machine_name).await? {
let Some(mac) = mac else {
continue;
};
let pac: Vec<u8> = iter::once([0xff; 6])
.chain(iter::repeat_n(mac, 16))
.flatten()
@@ -16,8 +18,9 @@ pub async fn wake(_machine_name: &str) -> io::Result<()> {
}
Ok(())
}
use std::{iter, time::Duration};
use std::{collections::HashSet, iter, net::IpAddr, time::Duration};
use macaddr::MacAddr;
use tokio::{
io,
net::{TcpStream, ToSocketAddrs, UdpSocket},
@@ -32,14 +35,65 @@ pub async fn ping_ip<T: ToSocketAddrs>(addr: T) -> bool {
.is_ok()
}
pub async fn get_ips(machine_name: &str) -> io::Result<Vec<IpAddr>> {
Ok(tokio::net::lookup_host((machine_name, 0))
.await?
.map(|c| c.ip())
.collect())
}
// pub async fn get_ips(macs: &[&[u8;6]]) -> Vec<IpAddr> {
// /*
// root@AP-AX3000CV2-0A55:~# ip neigh show 192.168.100.94
// 192.168.100.94 dev eth1.6 FAILED
// 192.168.100.94 dev br-lan lladdr 04:7c:16:79:6d:ee REACHABLE
// 192.168.100.94 dev eth1.7 FAILED
// 192.168.100.94 dev eth1.5 FAILED
// */
pub async fn get_macs(machine_name: &str) -> io::Result<Vec<(IpAddr, Option<[u8; 6]>)>> {
let ips = get_ips(machine_name).await?;
let futures = ips
.iter()
// .filter(|f| f.is_ipv4())
.map(|ip| {
let ip = ip.to_canonical();
async move {
let mut u = tokio::process::Command::new("ip");
u.args(["neigh", "show", "to", &ip.to_string()]);
let o = u.output().await.ok()?;
let mac = o.status.success().then(|| {
let stdout = String::from_utf8_lossy(&o.stdout);
stdout
.lines()
.filter_map(|line| {
let mut parts = line.split_whitespace();
parts.find(|&x| x == "lladdr")?;
let macstr = parts.next()?;
to_arr(macstr)
})
.next()
})?;
Some((ip, mac))
}
});
let results = futures::future::join_all(futures).await;
Ok(results.into_iter().flatten().collect())
}
// }
pub async fn get_macs_2(machine_name: &str) -> io::Result<HashSet<MacAddr>> {
Ok(get_macs(machine_name)
.await?
.into_iter()
.filter_map(|(_, m)| m.map(MacAddr::from))
.collect())
}
pub fn to_arr(macstr: &str) -> Option<[u8; 6]> {
let mut this = [0u8; 6];
(macstr.split(':').count() == 6).then(|| {
for (n, h) in this.iter_mut().zip(macstr.split(':')) {
*n = u8::from_str_radix(h, 16).ok()?;
}
Some(this)
})?
}
pub fn back_to_str(thing: &[u8; 6]) -> String {
thing
.iter()
.map(|n| format!("{n:02x}"))
.collect::<Vec<String>>()
.join(":")
}