use std::ffi::CStr;
use std::time::{Duration, UNIX_EPOCH, SystemTime};
use crate::addr::ScamperAddr;
use crate::list::{ScamperList, ScamperCycle};
use crate::ffi::scamper_ping::{self, ScamperPingT, ScamperPingProbeT, ScamperPingReplyT,
ScamperPingStatsT,
SCAMPER_PING_STOP_NONE, SCAMPER_PING_STOP_COMPLETED, SCAMPER_PING_STOP_ERROR,
SCAMPER_PING_STOP_HALTED, SCAMPER_PING_STOP_INPROGRESS};
fn timeval_to_duration(tv: *const libc::timeval) -> Option<Duration> {
if tv.is_null() {
return None;
}
let tv = unsafe { &*tv };
Some(Duration::new(tv.tv_sec as u64, tv.tv_usec as u32 * 1000))
}
fn timeval_to_systemtime(tv: *const libc::timeval) -> Option<SystemTime> {
let d = timeval_to_duration(tv)?;
Some(UNIX_EPOCH + d)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PingStopReason {
None = SCAMPER_PING_STOP_NONE,
Completed = SCAMPER_PING_STOP_COMPLETED,
Error = SCAMPER_PING_STOP_ERROR,
Halted = SCAMPER_PING_STOP_HALTED,
InProgress = SCAMPER_PING_STOP_INPROGRESS,
Unknown(u8),
}
impl From<u8> for PingStopReason {
fn from(v: u8) -> Self {
match v {
SCAMPER_PING_STOP_NONE => Self::None,
SCAMPER_PING_STOP_COMPLETED => Self::Completed,
SCAMPER_PING_STOP_ERROR => Self::Error,
SCAMPER_PING_STOP_HALTED => Self::Halted,
SCAMPER_PING_STOP_INPROGRESS => Self::InProgress,
other => Self::Unknown(other),
}
}
}
pub struct ScamperPingStats {
inner: *mut ScamperPingStatsT,
}
impl ScamperPingStats {
pub fn nreplies(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_stats_nreplies_get(self.inner) }
}
pub fn ndups(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_stats_ndups_get(self.inner) }
}
pub fn nloss(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_stats_nloss_get(self.inner) }
}
pub fn nerrs(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_stats_nerrs_get(self.inner) }
}
pub fn min_rtt(&self) -> Option<Duration> {
let tv = unsafe { scamper_ping::scamper_ping_stats_min_rtt_get(self.inner) };
timeval_to_duration(tv)
}
pub fn max_rtt(&self) -> Option<Duration> {
let tv = unsafe { scamper_ping::scamper_ping_stats_max_rtt_get(self.inner) };
timeval_to_duration(tv)
}
pub fn avg_rtt(&self) -> Option<Duration> {
let tv = unsafe { scamper_ping::scamper_ping_stats_avg_rtt_get(self.inner) };
timeval_to_duration(tv)
}
pub fn stddev_rtt(&self) -> Option<Duration> {
let tv = unsafe { scamper_ping::scamper_ping_stats_stddev_rtt_get(self.inner) };
timeval_to_duration(tv)
}
}
impl Drop for ScamperPingStats {
fn drop(&mut self) {
unsafe { scamper_ping::scamper_ping_stats_free(self.inner) };
}
}
unsafe impl Send for ScamperPingStats {}
unsafe impl Sync for ScamperPingStats {}
pub struct ScamperPingReply {
inner: *mut ScamperPingReplyT,
}
impl ScamperPingReply {
pub(crate) unsafe fn from_ptr(ptr: *mut ScamperPingReplyT) -> Option<Self> {
if ptr.is_null() {
return None;
}
let ptr = unsafe { scamper_ping::scamper_ping_reply_use(ptr) };
Some(ScamperPingReply { inner: ptr })
}
pub fn addr(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_ping::scamper_ping_reply_addr_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn proto(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_reply_proto_get(self.inner) }
}
pub fn ttl(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_reply_ttl_get(self.inner) }
}
pub fn size(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_reply_size_get(self.inner) }
}
pub fn ipid(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_reply_ipid_get(self.inner) }
}
pub fn rtt(&self) -> Option<Duration> {
let tv = unsafe { scamper_ping::scamper_ping_reply_rtt_get(self.inner) };
timeval_to_duration(tv)
}
pub fn is_icmp(&self) -> bool {
unsafe { scamper_ping::scamper_ping_reply_is_icmp(self.inner) != 0 }
}
pub fn is_tcp(&self) -> bool {
unsafe { scamper_ping::scamper_ping_reply_is_tcp(self.inner) != 0 }
}
pub fn is_udp(&self) -> bool {
unsafe { scamper_ping::scamper_ping_reply_is_udp(self.inner) != 0 }
}
pub fn is_icmp_echo_reply(&self) -> bool {
unsafe { scamper_ping::scamper_ping_reply_is_icmp_echo_reply(self.inner) != 0 }
}
pub fn is_icmp_unreach(&self) -> bool {
unsafe { scamper_ping::scamper_ping_reply_is_icmp_unreach(self.inner) != 0 }
}
pub fn is_icmp_ttl_exp(&self) -> bool {
unsafe { scamper_ping::scamper_ping_reply_is_icmp_ttl_exp(self.inner) != 0 }
}
pub fn is_icmp_ptb(&self) -> bool {
unsafe { scamper_ping::scamper_ping_reply_is_icmp_ptb(self.inner) != 0 }
}
pub fn icmp_type(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_reply_icmp_type_get(self.inner) }
}
pub fn icmp_code(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_reply_icmp_code_get(self.inner) }
}
pub fn tcp_flags(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_reply_tcp_flags_get(self.inner) }
}
pub fn flags(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_reply_flags_get(self.inner) }
}
pub fn ifname(&self) -> Option<&str> {
let ptr = unsafe { scamper_ping::scamper_ping_reply_ifname_get(self.inner) };
if ptr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
}
}
}
impl Drop for ScamperPingReply {
fn drop(&mut self) {
unsafe { scamper_ping::scamper_ping_reply_free(self.inner) };
}
}
unsafe impl Send for ScamperPingReply {}
unsafe impl Sync for ScamperPingReply {}
pub struct ScamperPingProbe {
inner: *mut ScamperPingProbeT,
ping: *const ScamperPingT,
}
impl ScamperPingProbe {
pub(crate) unsafe fn from_ptr(
ping: *const ScamperPingT,
ptr: *mut ScamperPingProbeT,
) -> Option<Self> {
if ptr.is_null() {
return None;
}
let ptr = unsafe { scamper_ping::scamper_ping_probe_use(ptr) };
Some(ScamperPingProbe { inner: ptr, ping })
}
pub fn id(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_probe_id_get(self.inner) }
}
pub fn ipid(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_probe_ipid_get(self.inner) }
}
pub fn tx(&self) -> Option<SystemTime> {
let tv = unsafe { scamper_ping::scamper_ping_probe_tx_get(self.inner) };
timeval_to_systemtime(tv)
}
pub fn flags(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_probe_flags_get(self.inner) }
}
pub fn reply(&self, i: u16) -> Option<ScamperPingReply> {
let ptr =
unsafe { scamper_ping::scamper_ping_probe_reply_get(self.inner, i) };
unsafe { ScamperPingReply::from_ptr(ptr) }
}
pub fn reply_is_from_target(&self, reply: &ScamperPingReply) -> bool {
unsafe {
scamper_ping::scamper_ping_reply_is_from_target(self.ping, reply.inner) != 0
}
}
}
impl Drop for ScamperPingProbe {
fn drop(&mut self) {
unsafe { scamper_ping::scamper_ping_probe_free(self.inner) };
}
}
unsafe impl Send for ScamperPingProbe {}
unsafe impl Sync for ScamperPingProbe {}
pub struct ScamperPing {
pub(crate) inner: *mut ScamperPingT,
}
impl ScamperPing {
pub(crate) unsafe fn from_ptr(ptr: *mut ScamperPingT) -> Option<Self> {
if ptr.is_null() {
return None;
}
Some(ScamperPing { inner: ptr })
}
pub fn dst(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_ping::scamper_ping_dst_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn src(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_ping::scamper_ping_src_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn rtr(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_ping::scamper_ping_rtr_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn userid(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_userid_get(self.inner) }
}
pub fn start(&self) -> Option<SystemTime> {
let tv = unsafe { scamper_ping::scamper_ping_start_get(self.inner) };
timeval_to_systemtime(tv)
}
pub fn stop_reason(&self) -> PingStopReason {
let v = unsafe { scamper_ping::scamper_ping_stop_reason_get(self.inner) };
PingStopReason::from(v)
}
pub fn stop_data(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_stop_data_get(self.inner) }
}
pub fn errmsg(&self) -> Option<&str> {
let ptr = unsafe { scamper_ping::scamper_ping_errmsg_get(self.inner) };
if ptr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
}
}
pub fn attempts(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_attempts_get(self.inner) }
}
pub fn pktsize(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_pktsize_get(self.inner) }
}
pub fn method(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_method_get(self.inner) }
}
pub fn ttl(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_ttl_get(self.inner) }
}
pub fn tos(&self) -> u8 {
unsafe { scamper_ping::scamper_ping_tos_get(self.inner) }
}
pub fn sport(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_sport_get(self.inner) }
}
pub fn dport(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_dport_get(self.inner) }
}
pub fn icmpsum(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_icmpsum_get(self.inner) }
}
pub fn tcpseq(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_tcpseq_get(self.inner) }
}
pub fn tcpack(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_tcpack_get(self.inner) }
}
pub fn flags(&self) -> u32 {
unsafe { scamper_ping::scamper_ping_flags_get(self.inner) }
}
pub fn stop_count(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_stop_count_get(self.inner) }
}
pub fn pmtu(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_pmtu_get(self.inner) }
}
pub fn sent(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_sent_get(self.inner) }
}
pub fn datalen(&self) -> u16 {
unsafe { scamper_ping::scamper_ping_datalen_get(self.inner) }
}
pub fn data(&self) -> &[u8] {
let len = unsafe { scamper_ping::scamper_ping_datalen_get(self.inner) };
let ptr = unsafe { scamper_ping::scamper_ping_data_get(self.inner) };
if ptr.is_null() || len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr, len as usize) }
}
pub fn wait_probe(&self) -> Option<Duration> {
let tv = unsafe { scamper_ping::scamper_ping_wait_probe_get(self.inner) };
timeval_to_duration(tv)
}
pub fn wait_timeout(&self) -> Option<Duration> {
let tv = unsafe { scamper_ping::scamper_ping_wait_timeout_get(self.inner) };
timeval_to_duration(tv)
}
pub fn is_method_icmp(&self) -> bool {
unsafe { scamper_ping::scamper_ping_method_is_icmp(self.inner) != 0 }
}
pub fn is_method_tcp(&self) -> bool {
unsafe { scamper_ping::scamper_ping_method_is_tcp(self.inner) != 0 }
}
pub fn is_method_udp(&self) -> bool {
unsafe { scamper_ping::scamper_ping_method_is_udp(self.inner) != 0 }
}
pub fn is_method_vary_sport(&self) -> bool {
unsafe { scamper_ping::scamper_ping_method_is_vary_sport(self.inner) != 0 }
}
pub fn is_method_vary_dport(&self) -> bool {
unsafe { scamper_ping::scamper_ping_method_is_vary_dport(self.inner) != 0 }
}
pub fn list(&self) -> Option<ScamperList> {
let ptr = unsafe { scamper_ping::scamper_ping_list_get(self.inner) };
unsafe { ScamperList::from_ptr(ptr) }
}
pub fn cycle(&self) -> Option<ScamperCycle> {
let ptr = unsafe { scamper_ping::scamper_ping_cycle_get(self.inner) };
unsafe { ScamperCycle::from_ptr(ptr) }
}
pub fn probe(&self, i: u16) -> Option<ScamperPingProbe> {
let ptr = unsafe { scamper_ping::scamper_ping_probe_get(self.inner, i) };
unsafe { ScamperPingProbe::from_ptr(self.inner, ptr) }
}
pub fn stats(&self) -> Option<ScamperPingStats> {
let ptr = unsafe { scamper_ping::scamper_ping_stats_alloc(self.inner) };
if ptr.is_null() {
None
} else {
Some(ScamperPingStats { inner: ptr })
}
}
pub fn to_json(&self) -> Option<String> {
let mut len = 0usize;
let ptr = unsafe { scamper_ping::scamper_ping_tojson(self.inner, &mut len) };
if ptr.is_null() {
return None;
}
let s = unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned();
unsafe { libc::free(ptr as *mut libc::c_void) };
Some(s)
}
}
impl Drop for ScamperPing {
fn drop(&mut self) {
unsafe { scamper_ping::scamper_ping_free(self.inner) };
}
}
unsafe impl Send for ScamperPing {}
unsafe impl Sync for ScamperPing {}