moving things around
This commit is contained in:
@@ -5,11 +5,11 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Display, Error)]
|
||||
pub enum IPNeighParseError {
|
||||
IpWhere,
|
||||
IpWhere, // i never seen a ip neigh where the first thing aint an ip
|
||||
IpParseError(AddrParseError),
|
||||
DevWhere,
|
||||
// DevWhere,
|
||||
MacParseError(macaddr::ParseError),
|
||||
StateWhere,
|
||||
StateWhere, // i never seen a ip neigh without the big FAILED at the end
|
||||
StateParseError(strum::ParseError),
|
||||
}
|
||||
|
||||
|
||||
+25
-34
@@ -1,16 +1,27 @@
|
||||
use axum::{Router, response::Html, routing::get};
|
||||
use axum::{
|
||||
Router,
|
||||
extract::Query,
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse},
|
||||
routing::{get, post},
|
||||
};
|
||||
use tokio::net::TcpListener;
|
||||
mod arpparse;
|
||||
mod route;
|
||||
mod utils;
|
||||
use route::*;
|
||||
use utils::*;
|
||||
use crate::{
|
||||
route::DeviceQuery,
|
||||
utils::{ping::ping_ip, query::get_macs_2_1, wake::wake},
|
||||
};
|
||||
|
||||
const MACHINE_NAME: &str = "lda.lan";
|
||||
|
||||
async fn home() -> Html<String> {
|
||||
Html(format!(
|
||||
r#"
|
||||
<html>
|
||||
<body>
|
||||
<p><a href="/home_2">Alternate UI</a></p>
|
||||
<p>the machine is {}! <a href="/status">Status</a></p>
|
||||
<form method="POST" action="/wake">
|
||||
<button type="submit">Wake LDA</button>
|
||||
@@ -31,34 +42,15 @@ async fn home() -> Html<String> {
|
||||
))
|
||||
}
|
||||
|
||||
async fn wake_handler() -> axum::response::Result<&'static str> {
|
||||
match wake(MACHINE_NAME).await {
|
||||
Ok(x) if x > 0 => Ok("Packet sent!"),
|
||||
_ => Err("Wake failed".into()),
|
||||
async fn wake_handler(
|
||||
Query(DeviceQuery { name }): Query<DeviceQuery>,
|
||||
) -> axum::response::Result<impl IntoResponse> {
|
||||
match wake(name.as_deref().unwrap_or(MACHINE_NAME)).await {
|
||||
Ok(x) if x > 0 => Ok((StatusCode::ACCEPTED, format!("{x} packets sent!"))),
|
||||
_ => Err((StatusCode::GATEWAY_TIMEOUT, "Wake failed").into()),
|
||||
}
|
||||
}
|
||||
|
||||
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!(
|
||||
// r#"<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>"),
|
||||
// };
|
||||
Html(status_build(MACHINE_NAME).await)
|
||||
}
|
||||
|
||||
pub async fn status_build(machine_name: &str) -> String {
|
||||
let formatted_macs = match get_macs_2_1(machine_name).await {
|
||||
Ok(table) => {
|
||||
@@ -101,16 +93,15 @@ pub async fn status_build(machine_name: &str) -> String {
|
||||
#[cfg(target_os = "linux")]
|
||||
#[tokio::main]
|
||||
async fn main() -> color_eyre::Result<()> {
|
||||
use crate::route::{api_status, get_status_2, home_2_route};
|
||||
|
||||
color_eyre::install()?;
|
||||
let app = Router::new()
|
||||
.route("/", get(home))
|
||||
.route("/home_2", get(home_2))
|
||||
.route("/wake", axum::routing::post(wake_handler))
|
||||
// .route("/wake", axum::routing::post(wake_handler))
|
||||
// .route("/status", get(status))
|
||||
.merge(home_2_route())
|
||||
.route("/wake", post(wake_handler))
|
||||
.route("/status", get(get_status_2))
|
||||
.route("/api/status/{name}", get(get_status_json))
|
||||
;
|
||||
.merge(api_status());
|
||||
|
||||
let port = TcpListener::bind("0.0.0.0:12012").await?;
|
||||
axum::serve(port, app.into_make_service()).await?;
|
||||
|
||||
+62
-12
@@ -1,19 +1,46 @@
|
||||
use axum::{
|
||||
Json,
|
||||
Json, Router,
|
||||
extract::{Path, Query},
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse},
|
||||
http::{StatusCode, header},
|
||||
response::{Html, IntoResponse, Redirect},
|
||||
routing::get,
|
||||
};
|
||||
|
||||
use crate::{MACHINE_NAME, arpparse, status_build, utils::get_macs_1};
|
||||
use crate::{MACHINE_NAME, arpparse, status_build, utils::query::get_macs_1};
|
||||
|
||||
pub async fn home_2() -> Html<&'static str> {
|
||||
async fn home_2() -> Html<&'static str> {
|
||||
Html(include_str!("../static/home_2"))
|
||||
}
|
||||
async fn home_2_css() -> impl IntoResponse {
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, "text/css; charset=utf-8"),
|
||||
(header::CACHE_CONTROL, "public, max-age=300"),
|
||||
],
|
||||
include_str!("../static/assets/home_2.css"),
|
||||
)
|
||||
}
|
||||
async fn home_2_js() -> impl IntoResponse {
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, "application/javascript"),
|
||||
(header::CACHE_CONTROL, "public, max-age=300"),
|
||||
],
|
||||
include_str!("../static/assets/home_2.js"),
|
||||
)
|
||||
}
|
||||
|
||||
/// all the pages related to home_2
|
||||
pub fn home_2_route() -> Router {
|
||||
Router::new()
|
||||
.route("/home_2", get(home_2))
|
||||
.route("/home_2.css", get(home_2_css))
|
||||
.route("/home_2.js", get(home_2_js)) //js
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Hash, serde::Deserialize)]
|
||||
pub struct DeviceQuery {
|
||||
name: Option<String>,
|
||||
pub name: Option<String>,
|
||||
// ip: Option<IpAddr>,
|
||||
// #[serde(deserialize_with = "des_opm")]
|
||||
// mac: Option<MacAddr>,
|
||||
@@ -35,13 +62,24 @@ pub struct StatusError {
|
||||
}
|
||||
// pub struct statuserror? table? and error? on status? what is the strat here
|
||||
|
||||
pub async fn get_status_json(p: Option<Path<NamePath>>) -> impl IntoResponse {
|
||||
let name = match p {
|
||||
Some(Path(NamePath { name })) => name,
|
||||
_ => MACHINE_NAME.to_owned(),
|
||||
};
|
||||
pub async fn get_status_json(
|
||||
// p: Option<Path<NamePath>>,
|
||||
Query(DeviceQuery { name }): Query<DeviceQuery>,
|
||||
) -> impl IntoResponse {
|
||||
let name = /* p
|
||||
.map(|Path(n)| n.name)
|
||||
.or */(name)
|
||||
.unwrap_or_else(|| MACHINE_NAME.to_owned());
|
||||
match get_macs_1(&name).await {
|
||||
Ok(table) => (StatusCode::OK, Json(Status { name, table })).into_response(),
|
||||
Ok(table) => {
|
||||
// let canonical = format!("/api/status?name={name}");
|
||||
(
|
||||
StatusCode::OK,
|
||||
// [(header::LINK, format!("<{canonical}>; rel=\"canonical\""))],
|
||||
Json(Status { name, table }),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
Err(error) => (
|
||||
StatusCode::BAD_GATEWAY,
|
||||
Json(StatusError {
|
||||
@@ -52,6 +90,18 @@ pub async fn get_status_json(p: Option<Path<NamePath>>) -> impl IntoResponse {
|
||||
.into_response(), // holy clutch. Couldve been disasterous
|
||||
}
|
||||
}
|
||||
pub async fn status_redirect(Path(NamePath { name }): Path<NamePath>) -> Redirect {
|
||||
Redirect::permanent(&format!(
|
||||
"/api/status?name={name}",
|
||||
name = urlencoding::encode(&name) // just for
|
||||
))
|
||||
}
|
||||
|
||||
pub fn api_status() -> Router {
|
||||
Router::new()
|
||||
.route("/api/status/{name}", get(status_redirect)) // api/status should be like the entire ip neigh br lan like idk like
|
||||
.route("/api/status", get(get_status_json))
|
||||
}
|
||||
|
||||
pub async fn get_status_2(q: Query<DeviceQuery>) -> Html<String> {
|
||||
let name = match q {
|
||||
|
||||
+6
-110
@@ -7,53 +7,15 @@ pub const LDA_MACS: [[u8; 6]; 2] = [
|
||||
|
||||
/// is it time to lookup host lda.lan for this...
|
||||
pub static LDA_MACS_2: LazyLock<[MacAddr; 2]> = LazyLock::new(|| LDA_MACS.map(MacAddr::from));
|
||||
pub async fn wake(machine_name: &str) -> io::Result<u32> {
|
||||
let suh = UdpSocket::bind("0.0.0.0:0").await?;
|
||||
suh.set_broadcast(true)?;
|
||||
let mut macs = get_macs_2_mac(machine_name).await.unwrap_or_default();
|
||||
macs.extend(*LDA_MACS_2);
|
||||
let mut sent_ok = 0;
|
||||
for mac in macs {
|
||||
let mb = mac.as_bytes();
|
||||
|
||||
let mut pac = [0; 6 + 6 * 16]; // 6x FF + 16x mac6
|
||||
pac[..6].fill(0xff);
|
||||
for i in 1..=16 {
|
||||
pac[i * 6..(i + 1) * 6].copy_from_slice(mb);
|
||||
}
|
||||
|
||||
match suh
|
||||
.send_to(&pac, (IpAddr::from([192, 168, 100, 255]), 9))
|
||||
.await
|
||||
{
|
||||
Ok(n) if n == pac.len() => sent_ok += 1,
|
||||
Ok(n) => eprintln!("partial send ({n}/{})", pac.len()),
|
||||
Err(e) => eprintln!("send error: {e}"),
|
||||
}
|
||||
}
|
||||
Ok(sent_ok)
|
||||
}
|
||||
use std::{collections::HashSet, net::IpAddr, sync::LazyLock, time::Duration};
|
||||
pub mod wake;
|
||||
use std::{net::IpAddr, sync::LazyLock};
|
||||
|
||||
use macaddr::MacAddr;
|
||||
use tokio::{
|
||||
io,
|
||||
net::{TcpStream, ToSocketAddrs, UdpSocket},
|
||||
time::timeout,
|
||||
};
|
||||
|
||||
use crate::arpparse::{self, IpNeighLine, NUDState};
|
||||
use tokio::io;
|
||||
|
||||
/// generic so you can do "123.45.67.89:22" or "lda.lan:22" as an input
|
||||
// this is so bad
|
||||
pub async fn ping_ip<T: ToSocketAddrs>(addr: T) -> bool {
|
||||
timeout(Duration::from_secs(1), TcpStream::connect(addr))
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
pub async fn _ping_ip_2<T: ToSocketAddrs>(_addr: T) -> bool {
|
||||
todo!("use icmp")
|
||||
}
|
||||
pub mod ping;
|
||||
|
||||
/// this is because i like [`IpAddr`] more than [`SocketAddr`](std::net::SocketAddr)
|
||||
pub async fn get_ips(machine_name: &str) -> io::Result<Vec<IpAddr>> {
|
||||
@@ -63,71 +25,5 @@ pub async fn get_ips(machine_name: &str) -> io::Result<Vec<IpAddr>> {
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get_macs_1(machine_name: &str) -> io::Result<Vec<arpparse::IpNeighLine>> {
|
||||
let ips = get_ips(machine_name).await?;
|
||||
let futures = ips.iter().map(|ip| {
|
||||
let ip = ip.to_canonical();
|
||||
async move {
|
||||
let o = exec_command(
|
||||
"ip",
|
||||
["neigh", "show", "to", &ip.to_string(), "dev", "br-lan"],
|
||||
)
|
||||
.await?;
|
||||
if !o.status.success() {
|
||||
return Err(io::Error::other(format!(
|
||||
"`ip neigh` failed for {ip} (status: {st}): {err}",
|
||||
st = o.status,
|
||||
err = String::from_utf8_lossy(&o.stderr),
|
||||
)));
|
||||
};
|
||||
Ok(String::from_utf8_lossy(&o.stdout)
|
||||
.lines()
|
||||
.map(arpparse::parse_ip_neigh_line)
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
});
|
||||
let res = futures::future::try_join_all(futures).await?; // async move block errs.
|
||||
Ok(res
|
||||
.into_iter()
|
||||
.flatten() /* resolve double vec */
|
||||
.flatten() /* drop parse errors */
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn exec_command<S: AsRef<std::ffi::OsStr>>(
|
||||
cmd: S,
|
||||
args: impl IntoIterator<Item = S>,
|
||||
) -> io::Result<std::process::Output> {
|
||||
let mut u = tokio::process::Command::new(cmd);
|
||||
u.args(args);
|
||||
u.output().await
|
||||
}
|
||||
|
||||
pub async fn get_macs_2_1(machine_name: &str) -> io::Result<HashSet<(IpAddr, MacAddr, NUDState)>> {
|
||||
Ok(get_macs_1(machine_name)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(
|
||||
|IpNeighLine {
|
||||
ip,
|
||||
dev: _,
|
||||
mac,
|
||||
state,
|
||||
}| mac.map(|mac| (ip, mac, state)),
|
||||
)
|
||||
.collect())
|
||||
}
|
||||
pub async fn get_macs_2_mac(machine_name: &str) -> io::Result<HashSet<MacAddr>> {
|
||||
Ok(get_macs_1(machine_name)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(
|
||||
|IpNeighLine {
|
||||
ip: _,
|
||||
dev: _,
|
||||
mac,
|
||||
state: _,
|
||||
}| mac,
|
||||
)
|
||||
.collect())
|
||||
}
|
||||
pub mod cmd;
|
||||
pub mod query;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
use std::io;
|
||||
|
||||
pub(crate) async fn exec_command<S: AsRef<std::ffi::OsStr>>(
|
||||
cmd: S,
|
||||
args: impl IntoIterator<Item = S>,
|
||||
) -> io::Result<std::process::Output> {
|
||||
let mut u = tokio::process::Command::new(cmd);
|
||||
u.args(args);
|
||||
u.output().await
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::{net::{TcpStream, ToSocketAddrs}, time::timeout};
|
||||
|
||||
pub async fn ping_ip<T: ToSocketAddrs>(addr: T) -> bool {
|
||||
timeout(Duration::from_secs(1), TcpStream::connect(addr))
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
pub async fn _ping_ip_2<T: ToSocketAddrs>(_addr: T) -> bool {
|
||||
todo!("use icmp")
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
use std::{collections::HashSet, net::IpAddr};
|
||||
|
||||
use macaddr::MacAddr;
|
||||
use tokio::io;
|
||||
|
||||
use crate::{
|
||||
arpparse::{self, IpNeighLine, NUDState},
|
||||
utils::{cmd::exec_command, get_ips},
|
||||
};
|
||||
|
||||
pub async fn get_macs_2_1(machine_name: &str) -> io::Result<HashSet<(IpAddr, MacAddr, NUDState)>> {
|
||||
Ok(get_macs_1(machine_name)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(
|
||||
|IpNeighLine {
|
||||
ip,
|
||||
dev: _,
|
||||
mac,
|
||||
state,
|
||||
}| mac.map(|mac| (ip, mac, state)),
|
||||
)
|
||||
.collect())
|
||||
}
|
||||
pub async fn get_macs_2_mac(machine_name: &str) -> io::Result<HashSet<MacAddr>> {
|
||||
Ok(get_macs_1(machine_name)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(
|
||||
|IpNeighLine {
|
||||
ip: _,
|
||||
dev: _,
|
||||
mac,
|
||||
state: _,
|
||||
}| mac,
|
||||
)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get_macs_1(machine_name: &str) -> io::Result<Vec<arpparse::IpNeighLine>> {
|
||||
let ips = get_ips(machine_name).await?;
|
||||
let futures = ips.iter().map(|ip| {
|
||||
let ip = ip.to_canonical();
|
||||
async move {
|
||||
let o = exec_command(
|
||||
"ip",
|
||||
["neigh", "show", "to", &ip.to_string(), "dev", "br-lan"],
|
||||
)
|
||||
.await?;
|
||||
if !o.status.success() {
|
||||
return Err(io::Error::other(format!(
|
||||
"`ip neigh` failed for {ip} (status: {st}): {err}",
|
||||
st = o.status,
|
||||
err = String::from_utf8_lossy(&o.stderr),
|
||||
)));
|
||||
};
|
||||
Ok(String::from_utf8_lossy(&o.stdout)
|
||||
.lines()
|
||||
.map(arpparse::parse_ip_neigh_line)
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
});
|
||||
let res = futures::future::try_join_all(futures).await?; // async move block errs.
|
||||
Ok(res
|
||||
.into_iter()
|
||||
.flatten() /* resolve double vec */
|
||||
.flatten() /* drop parse errors */
|
||||
.collect())
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
use std::{io, net::IpAddr};
|
||||
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::utils::{query::get_macs_2_mac, LDA_MACS_2};
|
||||
|
||||
pub async fn wake(machine_name: &str) -> io::Result<u32> {
|
||||
let suh = UdpSocket::bind("0.0.0.0:0").await?;
|
||||
suh.set_broadcast(true)?;
|
||||
let mut macs = get_macs_2_mac(machine_name).await.unwrap_or_default();
|
||||
macs.extend(*LDA_MACS_2);
|
||||
let mut sent_ok = 0;
|
||||
for mac in macs {
|
||||
let mb = mac.as_bytes();
|
||||
|
||||
let mut pac = [0; 6 + 6 * 16]; // 6x FF + 16x mac6
|
||||
pac[..6].fill(0xff);
|
||||
for i in 1..=16 {
|
||||
pac[i * 6..(i + 1) * 6].copy_from_slice(mb);
|
||||
}
|
||||
|
||||
match suh
|
||||
.send_to(&pac, (IpAddr::from([192, 168, 100, 255]), 9))
|
||||
.await
|
||||
{
|
||||
Ok(n) if n == pac.len() => sent_ok += 1,
|
||||
Ok(n) => eprintln!("partial send ({n}/{})", pac.len()),
|
||||
Err(e) => eprintln!("send error: {e}"),
|
||||
}
|
||||
}
|
||||
Ok(sent_ok)
|
||||
}
|
||||
Reference in New Issue
Block a user