use std::ffi::CStr;
use std::time::{Duration, UNIX_EPOCH, SystemTime};
use crate::addr::ScamperAddr;
use crate::icmpext::ScamperIcmpExts;
use crate::list::{ScamperList, ScamperCycle};
use crate::ffi::scamper_trace::{self, ScamperTraceT, ScamperTraceProbeT, ScamperTraceReplyT,
ScamperTraceHopiterT, ScamperTracePmtudT,
SCAMPER_TRACE_STOP_NONE, SCAMPER_TRACE_STOP_COMPLETED, SCAMPER_TRACE_STOP_UNREACH,
SCAMPER_TRACE_STOP_ICMP, SCAMPER_TRACE_STOP_LOOP, SCAMPER_TRACE_STOP_GAPLIMIT,
SCAMPER_TRACE_STOP_ERROR, SCAMPER_TRACE_STOP_HOPLIMIT, SCAMPER_TRACE_STOP_GSS,
SCAMPER_TRACE_STOP_HALTED, SCAMPER_TRACE_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 TraceStopReason {
None = SCAMPER_TRACE_STOP_NONE,
Completed = SCAMPER_TRACE_STOP_COMPLETED,
Unreach = SCAMPER_TRACE_STOP_UNREACH,
Icmp = SCAMPER_TRACE_STOP_ICMP,
Loop = SCAMPER_TRACE_STOP_LOOP,
GapLimit = SCAMPER_TRACE_STOP_GAPLIMIT,
Error = SCAMPER_TRACE_STOP_ERROR,
HopLimit = SCAMPER_TRACE_STOP_HOPLIMIT,
Gss = SCAMPER_TRACE_STOP_GSS,
Halted = SCAMPER_TRACE_STOP_HALTED,
InProgress = SCAMPER_TRACE_STOP_INPROGRESS,
Unknown(u8),
}
impl From<u8> for TraceStopReason {
fn from(v: u8) -> Self {
match v {
SCAMPER_TRACE_STOP_NONE => Self::None,
SCAMPER_TRACE_STOP_COMPLETED => Self::Completed,
SCAMPER_TRACE_STOP_UNREACH => Self::Unreach,
SCAMPER_TRACE_STOP_ICMP => Self::Icmp,
SCAMPER_TRACE_STOP_LOOP => Self::Loop,
SCAMPER_TRACE_STOP_GAPLIMIT => Self::GapLimit,
SCAMPER_TRACE_STOP_ERROR => Self::Error,
SCAMPER_TRACE_STOP_HOPLIMIT => Self::HopLimit,
SCAMPER_TRACE_STOP_GSS => Self::Gss,
SCAMPER_TRACE_STOP_HALTED => Self::Halted,
SCAMPER_TRACE_STOP_INPROGRESS => Self::InProgress,
other => Self::Unknown(other),
}
}
}
pub struct ScamperTraceProbe {
inner: *mut ScamperTraceProbeT,
}
impl ScamperTraceProbe {
pub(crate) unsafe fn from_ptr(ptr: *mut ScamperTraceProbeT) -> Option<Self> {
if ptr.is_null() {
return None;
}
let ptr = unsafe { scamper_trace::scamper_trace_probe_use(ptr) };
Some(ScamperTraceProbe { inner: ptr })
}
pub fn id(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_probe_id_get(self.inner) }
}
pub fn ttl(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_probe_ttl_get(self.inner) }
}
pub fn size(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_probe_size_get(self.inner) }
}
pub fn tx(&self) -> Option<SystemTime> {
let tv = unsafe { scamper_trace::scamper_trace_probe_tx_get(self.inner) };
timeval_to_systemtime(tv)
}
}
impl Drop for ScamperTraceProbe {
fn drop(&mut self) {
unsafe { scamper_trace::scamper_trace_probe_free(self.inner) };
}
}
unsafe impl Send for ScamperTraceProbe {}
unsafe impl Sync for ScamperTraceProbe {}
pub struct ScamperTraceReply {
inner: *mut ScamperTraceReplyT,
}
impl ScamperTraceReply {
pub(crate) unsafe fn from_ptr(ptr: *mut ScamperTraceReplyT) -> Option<Self> {
if ptr.is_null() {
return None;
}
let ptr = unsafe { scamper_trace::scamper_trace_reply_use(ptr) };
Some(ScamperTraceReply { inner: ptr })
}
pub fn addr(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_trace::scamper_trace_reply_addr_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn name(&self) -> Option<&str> {
let ptr = unsafe { scamper_trace::scamper_trace_reply_name_get(self.inner) };
if ptr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
}
}
pub fn rtt(&self) -> Option<Duration> {
let tv = unsafe { scamper_trace::scamper_trace_reply_rtt_get(self.inner) };
timeval_to_duration(tv)
}
pub fn ttl(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_reply_ttl_get(self.inner) }
}
pub fn tos(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_reply_tos_get(self.inner) }
}
pub fn size(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_reply_size_get(self.inner) }
}
pub fn ipid(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_reply_ipid_get(self.inner) }
}
pub fn icmp_type(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_reply_icmp_type_get(self.inner) }
}
pub fn icmp_code(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_reply_icmp_code_get(self.inner) }
}
pub fn is_tcp(&self) -> bool {
unsafe { scamper_trace::scamper_trace_reply_is_tcp(self.inner) != 0 }
}
pub fn is_icmp(&self) -> bool {
unsafe { scamper_trace::scamper_trace_reply_is_icmp(self.inner) != 0 }
}
pub fn is_icmp_ttl_exp(&self) -> bool {
unsafe { scamper_trace::scamper_trace_reply_is_icmp_ttl_exp(self.inner) != 0 }
}
pub fn is_icmp_unreach_port(&self) -> bool {
unsafe { scamper_trace::scamper_trace_reply_is_icmp_unreach_port(self.inner) != 0 }
}
pub fn is_icmp_echo_reply(&self) -> bool {
unsafe { scamper_trace::scamper_trace_reply_is_icmp_echo_reply(self.inner) != 0 }
}
pub fn is_icmp_ptb(&self) -> bool {
unsafe { scamper_trace::scamper_trace_reply_is_icmp_ptb(self.inner) != 0 }
}
pub fn icmp_nhmtu(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_reply_icmp_nhmtu_get(self.inner) }
}
pub fn tcp_flags(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_reply_tcp_flags_get(self.inner) }
}
pub fn flags(&self) -> u32 {
unsafe { scamper_trace::scamper_trace_reply_flags_get(self.inner) }
}
pub fn icmp_exts(&self) -> Option<ScamperIcmpExts> {
let ptr = unsafe { scamper_trace::scamper_trace_reply_icmp_exts_get(self.inner) };
unsafe { ScamperIcmpExts::from_ptr(ptr) }
}
}
impl Drop for ScamperTraceReply {
fn drop(&mut self) {
unsafe { scamper_trace::scamper_trace_reply_free(self.inner) };
}
}
unsafe impl Send for ScamperTraceReply {}
unsafe impl Sync for ScamperTraceReply {}
pub struct ScamperTraceHopIter {
trace: *const ScamperTraceT,
inner: *mut ScamperTraceHopiterT,
}
impl ScamperTraceHopIter {
fn new(trace: *const ScamperTraceT) -> Self {
let inner = unsafe { scamper_trace::scamper_trace_hopiter_alloc() };
ScamperTraceHopIter { trace, inner }
}
}
impl Drop for ScamperTraceHopIter {
fn drop(&mut self) {
unsafe { scamper_trace::scamper_trace_hopiter_free(self.inner) };
}
}
impl Iterator for ScamperTraceHopIter {
type Item = (ScamperTraceProbe, ScamperTraceReply);
fn next(&mut self) -> Option<Self::Item> {
let reply_ptr =
unsafe { scamper_trace::scamper_trace_hopiter_next(self.trace, self.inner) };
if reply_ptr.is_null() {
return None;
}
let probe_ptr =
unsafe { scamper_trace::scamper_trace_hopiter_probe_get(self.inner) };
let probe = unsafe { ScamperTraceProbe::from_ptr(probe_ptr) }?;
let reply = unsafe { ScamperTraceReply::from_ptr(reply_ptr) }?;
Some((probe, reply))
}
}
pub struct ScamperTracePmtud {
inner: *mut ScamperTracePmtudT,
}
impl ScamperTracePmtud {
pub(crate) unsafe fn from_ptr(ptr: *mut ScamperTracePmtudT) -> Option<Self> {
if ptr.is_null() {
return None;
}
let ptr = unsafe { scamper_trace::scamper_trace_pmtud_use(ptr) };
Some(ScamperTracePmtud { inner: ptr })
}
pub fn ver(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_pmtud_ver_get(self.inner) }
}
pub fn pmtu(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_pmtud_pmtu_get(self.inner) }
}
pub fn ifmtu(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_pmtud_ifmtu_get(self.inner) }
}
pub fn outmtu(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_pmtud_outmtu_get(self.inner) }
}
}
impl Drop for ScamperTracePmtud {
fn drop(&mut self) {
unsafe { scamper_trace::scamper_trace_pmtud_free(self.inner) };
}
}
unsafe impl Send for ScamperTracePmtud {}
unsafe impl Sync for ScamperTracePmtud {}
pub struct ScamperTrace {
pub(crate) inner: *mut ScamperTraceT,
}
impl ScamperTrace {
pub(crate) unsafe fn from_ptr(ptr: *mut ScamperTraceT) -> Option<Self> {
if ptr.is_null() {
return None;
}
Some(ScamperTrace { inner: ptr })
}
pub fn src(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_trace::scamper_trace_src_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn dst(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_trace::scamper_trace_dst_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn rtr(&self) -> Option<ScamperAddr> {
let ptr = unsafe { scamper_trace::scamper_trace_rtr_get(self.inner) };
unsafe { ScamperAddr::from_ptr(ptr) }
}
pub fn userid(&self) -> u32 {
unsafe { scamper_trace::scamper_trace_userid_get(self.inner) }
}
pub fn start(&self) -> Option<SystemTime> {
let tv = unsafe { scamper_trace::scamper_trace_start_get(self.inner) };
timeval_to_systemtime(tv)
}
pub fn stop_reason(&self) -> TraceStopReason {
let v = unsafe { scamper_trace::scamper_trace_stop_reason_get(self.inner) };
TraceStopReason::from(v)
}
pub fn stop_data(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_stop_data_get(self.inner) }
}
pub fn errmsg(&self) -> Option<&str> {
let ptr = unsafe { scamper_trace::scamper_trace_errmsg_get(self.inner) };
if ptr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
}
}
pub fn hop_count(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_hop_count_get(self.inner) }
}
pub fn stop_hop(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_stop_hop_get(self.inner) }
}
pub fn trace_type(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_type_get(self.inner) }
}
pub fn attempts(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_attempts_get(self.inner) }
}
pub fn hoplimit(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_hoplimit_get(self.inner) }
}
pub fn firsthop(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_firsthop_get(self.inner) }
}
pub fn gaplimit(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_gaplimit_get(self.inner) }
}
pub fn tos(&self) -> u8 {
unsafe { scamper_trace::scamper_trace_tos_get(self.inner) }
}
pub fn sport(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_sport_get(self.inner) }
}
pub fn dport(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_dport_get(self.inner) }
}
pub fn size(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_size_get(self.inner) }
}
pub fn offset(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_offset_get(self.inner) }
}
pub fn flags(&self) -> u32 {
unsafe { scamper_trace::scamper_trace_flags_get(self.inner) }
}
pub fn probec(&self) -> u16 {
unsafe { scamper_trace::scamper_trace_probec_get(self.inner) }
}
pub fn payload(&self) -> &[u8] {
let len = unsafe { scamper_trace::scamper_trace_payload_len_get(self.inner) };
let ptr = unsafe { scamper_trace::scamper_trace_payload_get(self.inner) };
if ptr.is_null() || len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr, len as usize) }
}
pub fn wait_timeout(&self) -> Option<Duration> {
let tv = unsafe { scamper_trace::scamper_trace_wait_timeout_get(self.inner) };
timeval_to_duration(tv)
}
pub fn wait_probe(&self) -> Option<Duration> {
let tv = unsafe { scamper_trace::scamper_trace_wait_probe_get(self.inner) };
timeval_to_duration(tv)
}
pub fn is_type_udp(&self) -> bool {
unsafe { scamper_trace::scamper_trace_type_is_udp(self.inner) != 0 }
}
pub fn is_type_tcp(&self) -> bool {
unsafe { scamper_trace::scamper_trace_type_is_tcp(self.inner) != 0 }
}
pub fn is_type_icmp(&self) -> bool {
unsafe { scamper_trace::scamper_trace_type_is_icmp(self.inner) != 0 }
}
pub fn list(&self) -> Option<ScamperList> {
let ptr = unsafe { scamper_trace::scamper_trace_list_get(self.inner) };
unsafe { ScamperList::from_ptr(ptr) }
}
pub fn cycle(&self) -> Option<ScamperCycle> {
let ptr = unsafe { scamper_trace::scamper_trace_cycle_get(self.inner) };
unsafe { ScamperCycle::from_ptr(ptr) }
}
pub fn pmtud(&self) -> Option<ScamperTracePmtud> {
let ptr = unsafe { scamper_trace::scamper_trace_pmtud_get(self.inner) };
unsafe { ScamperTracePmtud::from_ptr(ptr) }
}
pub fn hops(&self) -> ScamperTraceHopIter {
ScamperTraceHopIter::new(self.inner)
}
pub fn to_json(&self) -> Option<String> {
let mut len = 0usize;
let ptr = unsafe { scamper_trace::scamper_trace_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 ScamperTrace {
fn drop(&mut self) {
unsafe { scamper_trace::scamper_trace_free(self.inner) };
}
}
unsafe impl Send for ScamperTrace {}
unsafe impl Sync for ScamperTrace {}