use crate::ping as core;
use crate::script::convert::anyhow_to_rhai;
use crate::script::defaults::ScriptDefaults;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
pub fn register(engine: &mut Engine, defaults: ScriptDefaults) {
{
let d = defaults.clone();
engine.register_fn("ping", move |host: &str| -> Result<Map, Box<EvalAltResult>> {
do_ping(host, d.ping_count)
});
}
engine.register_fn(
"ping",
move |host: &str, count: i64| -> Result<Map, Box<EvalAltResult>> {
if count <= 0 {
return Err("ping: count must be positive".into());
}
do_ping(host, count as u32)
},
);
}
fn do_ping(host: &str, count: u32) -> Result<Map, Box<EvalAltResult>> {
let result = core::probe(host, count).map_err(anyhow_to_rhai)?;
let mut m = Map::new();
m.insert("protocol".into(), result.protocol.to_string().into());
m.insert("host".into(), result.host.clone().into());
if let Some(ip) = result.resolved_ip {
m.insert("resolved_ip".into(), ip.to_string().into());
}
if let Some(p) = result.port {
m.insert("port".into(), (p as i64).into());
}
m.insert("sent".into(), (result.sent as i64).into());
m.insert("received".into(), (result.received as i64).into());
m.insert("loss_pct".into(), (result.loss_pct as i64).into());
let replies: Array = result
.replies
.iter()
.map(|r| {
let mut reply = Map::new();
reply.insert("seq".into(), (r.seq as i64).into());
reply.insert("ms".into(), r.ms.into());
Dynamic::from(reply)
})
.collect();
m.insert("replies".into(), replies.into());
if let Some(v) = result.min_ms() {
m.insert("min_ms".into(), v.into());
}
if let Some(v) = result.avg_ms() {
m.insert("avg_ms".into(), v.into());
}
if let Some(v) = result.max_ms() {
m.insert("max_ms".into(), v.into());
}
Ok(m)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::Args;
use clap::Parser;
use std::net::TcpListener;
use std::thread;
fn engine() -> Engine {
let args = Args::try_parse_from(["recon", "--script", "/dev/null"]).unwrap();
let defaults = ScriptDefaults::from_args(&args);
let mut e = Engine::new();
super::super::helpers::register(&mut e);
register(&mut e, defaults);
e
}
#[test]
fn tcp_ping_to_listener_reports_received() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();
let accept_thread = thread::spawn(move || {
let _ = listener.accept();
});
let e = engine();
let script = format!(r#"ping("127.0.0.1:{port}", 1)"#);
let m: Map = e.eval(&script).expect("eval");
assert_eq!(m.get("sent").unwrap().as_int().unwrap(), 1);
assert_eq!(m.get("received").unwrap().as_int().unwrap(), 1);
assert_eq!(
m.get("protocol").unwrap().clone().into_string().unwrap(),
"tcp"
);
accept_thread.join().unwrap();
}
#[test]
fn tcp_ping_to_closed_port_reports_loss() {
let e = engine();
let m: Map = e.eval(r#"ping("127.0.0.1:1", 1)"#).expect("eval");
assert_eq!(m.get("sent").unwrap().as_int().unwrap(), 1);
assert_eq!(m.get("received").unwrap().as_int().unwrap(), 0);
assert_eq!(m.get("loss_pct").unwrap().as_int().unwrap(), 100);
}
#[test]
fn ping_count_zero_errors() {
let e = engine();
let res = e.eval::<Map>(r#"ping("127.0.0.1", 0)"#);
assert!(res.is_err());
}
}