1use std::ffi::CStr;
10use std::time::{Duration, UNIX_EPOCH, SystemTime};
11
12use crate::addr::ScamperAddr;
13use crate::list::{ScamperList, ScamperCycle};
14use crate::ffi::scamper_ping::{self, ScamperPingT, ScamperPingProbeT, ScamperPingReplyT,
15 ScamperPingStatsT,
16 SCAMPER_PING_STOP_NONE, SCAMPER_PING_STOP_COMPLETED, SCAMPER_PING_STOP_ERROR,
17 SCAMPER_PING_STOP_HALTED, SCAMPER_PING_STOP_INPROGRESS};
18
19fn timeval_to_duration(tv: *const libc::timeval) -> Option<Duration> {
20 if tv.is_null() {
21 return None;
22 }
23 let tv = unsafe { &*tv };
24 Some(Duration::new(tv.tv_sec as u64, tv.tv_usec as u32 * 1000))
25}
26
27fn timeval_to_systemtime(tv: *const libc::timeval) -> Option<SystemTime> {
28 let d = timeval_to_duration(tv)?;
29 Some(UNIX_EPOCH + d)
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[repr(u8)]
35pub enum PingStopReason {
36 None = SCAMPER_PING_STOP_NONE,
37 Completed = SCAMPER_PING_STOP_COMPLETED,
38 Error = SCAMPER_PING_STOP_ERROR,
39 Halted = SCAMPER_PING_STOP_HALTED,
40 InProgress = SCAMPER_PING_STOP_INPROGRESS,
41 Unknown(u8),
42}
43
44impl From<u8> for PingStopReason {
45 fn from(v: u8) -> Self {
46 match v {
47 SCAMPER_PING_STOP_NONE => Self::None,
48 SCAMPER_PING_STOP_COMPLETED => Self::Completed,
49 SCAMPER_PING_STOP_ERROR => Self::Error,
50 SCAMPER_PING_STOP_HALTED => Self::Halted,
51 SCAMPER_PING_STOP_INPROGRESS => Self::InProgress,
52 other => Self::Unknown(other),
53 }
54 }
55}
56
57pub struct ScamperPingStats {
59 inner: *mut ScamperPingStatsT,
60}
61
62impl ScamperPingStats {
63 pub fn nreplies(&self) -> u32 {
64 unsafe { scamper_ping::scamper_ping_stats_nreplies_get(self.inner) }
65 }
66
67 pub fn ndups(&self) -> u32 {
68 unsafe { scamper_ping::scamper_ping_stats_ndups_get(self.inner) }
69 }
70
71 pub fn nloss(&self) -> u32 {
72 unsafe { scamper_ping::scamper_ping_stats_nloss_get(self.inner) }
73 }
74
75 pub fn nerrs(&self) -> u32 {
76 unsafe { scamper_ping::scamper_ping_stats_nerrs_get(self.inner) }
77 }
78
79 pub fn min_rtt(&self) -> Option<Duration> {
80 let tv = unsafe { scamper_ping::scamper_ping_stats_min_rtt_get(self.inner) };
81 timeval_to_duration(tv)
82 }
83
84 pub fn max_rtt(&self) -> Option<Duration> {
85 let tv = unsafe { scamper_ping::scamper_ping_stats_max_rtt_get(self.inner) };
86 timeval_to_duration(tv)
87 }
88
89 pub fn avg_rtt(&self) -> Option<Duration> {
90 let tv = unsafe { scamper_ping::scamper_ping_stats_avg_rtt_get(self.inner) };
91 timeval_to_duration(tv)
92 }
93
94 pub fn stddev_rtt(&self) -> Option<Duration> {
95 let tv = unsafe { scamper_ping::scamper_ping_stats_stddev_rtt_get(self.inner) };
96 timeval_to_duration(tv)
97 }
98}
99
100impl Drop for ScamperPingStats {
101 fn drop(&mut self) {
102 unsafe { scamper_ping::scamper_ping_stats_free(self.inner) };
103 }
104}
105
106unsafe impl Send for ScamperPingStats {}
107unsafe impl Sync for ScamperPingStats {}
108
109pub struct ScamperPingReply {
111 inner: *mut ScamperPingReplyT,
112}
113
114impl ScamperPingReply {
115 pub(crate) unsafe fn from_ptr(ptr: *mut ScamperPingReplyT) -> Option<Self> {
116 if ptr.is_null() {
117 return None;
118 }
119 let ptr = unsafe { scamper_ping::scamper_ping_reply_use(ptr) };
120 Some(ScamperPingReply { inner: ptr })
121 }
122
123 pub fn addr(&self) -> Option<ScamperAddr> {
124 let ptr = unsafe { scamper_ping::scamper_ping_reply_addr_get(self.inner) };
125 unsafe { ScamperAddr::from_ptr(ptr) }
126 }
127
128 pub fn proto(&self) -> u8 {
129 unsafe { scamper_ping::scamper_ping_reply_proto_get(self.inner) }
130 }
131
132 pub fn ttl(&self) -> u8 {
133 unsafe { scamper_ping::scamper_ping_reply_ttl_get(self.inner) }
134 }
135
136 pub fn size(&self) -> u16 {
137 unsafe { scamper_ping::scamper_ping_reply_size_get(self.inner) }
138 }
139
140 pub fn ipid(&self) -> u16 {
141 unsafe { scamper_ping::scamper_ping_reply_ipid_get(self.inner) }
142 }
143
144 pub fn rtt(&self) -> Option<Duration> {
145 let tv = unsafe { scamper_ping::scamper_ping_reply_rtt_get(self.inner) };
146 timeval_to_duration(tv)
147 }
148
149 pub fn is_icmp(&self) -> bool {
150 unsafe { scamper_ping::scamper_ping_reply_is_icmp(self.inner) != 0 }
151 }
152
153 pub fn is_tcp(&self) -> bool {
154 unsafe { scamper_ping::scamper_ping_reply_is_tcp(self.inner) != 0 }
155 }
156
157 pub fn is_udp(&self) -> bool {
158 unsafe { scamper_ping::scamper_ping_reply_is_udp(self.inner) != 0 }
159 }
160
161 pub fn is_icmp_echo_reply(&self) -> bool {
162 unsafe { scamper_ping::scamper_ping_reply_is_icmp_echo_reply(self.inner) != 0 }
163 }
164
165 pub fn is_icmp_unreach(&self) -> bool {
166 unsafe { scamper_ping::scamper_ping_reply_is_icmp_unreach(self.inner) != 0 }
167 }
168
169 pub fn is_icmp_ttl_exp(&self) -> bool {
170 unsafe { scamper_ping::scamper_ping_reply_is_icmp_ttl_exp(self.inner) != 0 }
171 }
172
173 pub fn is_icmp_ptb(&self) -> bool {
174 unsafe { scamper_ping::scamper_ping_reply_is_icmp_ptb(self.inner) != 0 }
175 }
176
177 pub fn icmp_type(&self) -> u8 {
178 unsafe { scamper_ping::scamper_ping_reply_icmp_type_get(self.inner) }
179 }
180
181 pub fn icmp_code(&self) -> u8 {
182 unsafe { scamper_ping::scamper_ping_reply_icmp_code_get(self.inner) }
183 }
184
185 pub fn tcp_flags(&self) -> u8 {
186 unsafe { scamper_ping::scamper_ping_reply_tcp_flags_get(self.inner) }
187 }
188
189 pub fn flags(&self) -> u32 {
190 unsafe { scamper_ping::scamper_ping_reply_flags_get(self.inner) }
191 }
192
193 pub fn ifname(&self) -> Option<&str> {
194 let ptr = unsafe { scamper_ping::scamper_ping_reply_ifname_get(self.inner) };
195 if ptr.is_null() {
196 None
197 } else {
198 Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
199 }
200 }
201}
202
203impl Drop for ScamperPingReply {
204 fn drop(&mut self) {
205 unsafe { scamper_ping::scamper_ping_reply_free(self.inner) };
206 }
207}
208
209unsafe impl Send for ScamperPingReply {}
210unsafe impl Sync for ScamperPingReply {}
211
212pub struct ScamperPingProbe {
214 inner: *mut ScamperPingProbeT,
215 ping: *const ScamperPingT,
216}
217
218impl ScamperPingProbe {
219 pub(crate) unsafe fn from_ptr(
220 ping: *const ScamperPingT,
221 ptr: *mut ScamperPingProbeT,
222 ) -> Option<Self> {
223 if ptr.is_null() {
224 return None;
225 }
226 let ptr = unsafe { scamper_ping::scamper_ping_probe_use(ptr) };
227 Some(ScamperPingProbe { inner: ptr, ping })
228 }
229
230 pub fn id(&self) -> u16 {
231 unsafe { scamper_ping::scamper_ping_probe_id_get(self.inner) }
232 }
233
234 pub fn ipid(&self) -> u16 {
235 unsafe { scamper_ping::scamper_ping_probe_ipid_get(self.inner) }
236 }
237
238 pub fn tx(&self) -> Option<SystemTime> {
239 let tv = unsafe { scamper_ping::scamper_ping_probe_tx_get(self.inner) };
240 timeval_to_systemtime(tv)
241 }
242
243 pub fn flags(&self) -> u32 {
244 unsafe { scamper_ping::scamper_ping_probe_flags_get(self.inner) }
245 }
246
247 pub fn reply(&self, i: u16) -> Option<ScamperPingReply> {
249 let ptr =
250 unsafe { scamper_ping::scamper_ping_probe_reply_get(self.inner, i) };
251 unsafe { ScamperPingReply::from_ptr(ptr) }
252 }
253
254 pub fn reply_is_from_target(&self, reply: &ScamperPingReply) -> bool {
256 unsafe {
257 scamper_ping::scamper_ping_reply_is_from_target(self.ping, reply.inner) != 0
258 }
259 }
260}
261
262impl Drop for ScamperPingProbe {
263 fn drop(&mut self) {
264 unsafe { scamper_ping::scamper_ping_probe_free(self.inner) };
265 }
266}
267
268unsafe impl Send for ScamperPingProbe {}
269unsafe impl Sync for ScamperPingProbe {}
270
271pub struct ScamperPing {
273 pub(crate) inner: *mut ScamperPingT,
274}
275
276impl ScamperPing {
277 pub(crate) unsafe fn from_ptr(ptr: *mut ScamperPingT) -> Option<Self> {
278 if ptr.is_null() {
279 return None;
280 }
281 Some(ScamperPing { inner: ptr })
282 }
283
284 pub fn dst(&self) -> Option<ScamperAddr> {
285 let ptr = unsafe { scamper_ping::scamper_ping_dst_get(self.inner) };
286 unsafe { ScamperAddr::from_ptr(ptr) }
287 }
288
289 pub fn src(&self) -> Option<ScamperAddr> {
290 let ptr = unsafe { scamper_ping::scamper_ping_src_get(self.inner) };
291 unsafe { ScamperAddr::from_ptr(ptr) }
292 }
293
294 pub fn rtr(&self) -> Option<ScamperAddr> {
295 let ptr = unsafe { scamper_ping::scamper_ping_rtr_get(self.inner) };
296 unsafe { ScamperAddr::from_ptr(ptr) }
297 }
298
299 pub fn userid(&self) -> u32 {
300 unsafe { scamper_ping::scamper_ping_userid_get(self.inner) }
301 }
302
303 pub fn start(&self) -> Option<SystemTime> {
304 let tv = unsafe { scamper_ping::scamper_ping_start_get(self.inner) };
305 timeval_to_systemtime(tv)
306 }
307
308 pub fn stop_reason(&self) -> PingStopReason {
309 let v = unsafe { scamper_ping::scamper_ping_stop_reason_get(self.inner) };
310 PingStopReason::from(v)
311 }
312
313 pub fn stop_data(&self) -> u8 {
314 unsafe { scamper_ping::scamper_ping_stop_data_get(self.inner) }
315 }
316
317 pub fn errmsg(&self) -> Option<&str> {
318 let ptr = unsafe { scamper_ping::scamper_ping_errmsg_get(self.inner) };
319 if ptr.is_null() {
320 None
321 } else {
322 Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
323 }
324 }
325
326 pub fn attempts(&self) -> u16 {
327 unsafe { scamper_ping::scamper_ping_attempts_get(self.inner) }
328 }
329
330 pub fn pktsize(&self) -> u16 {
331 unsafe { scamper_ping::scamper_ping_pktsize_get(self.inner) }
332 }
333
334 pub fn method(&self) -> u8 {
335 unsafe { scamper_ping::scamper_ping_method_get(self.inner) }
336 }
337
338 pub fn ttl(&self) -> u8 {
339 unsafe { scamper_ping::scamper_ping_ttl_get(self.inner) }
340 }
341
342 pub fn tos(&self) -> u8 {
343 unsafe { scamper_ping::scamper_ping_tos_get(self.inner) }
344 }
345
346 pub fn sport(&self) -> u16 {
347 unsafe { scamper_ping::scamper_ping_sport_get(self.inner) }
348 }
349
350 pub fn dport(&self) -> u16 {
351 unsafe { scamper_ping::scamper_ping_dport_get(self.inner) }
352 }
353
354 pub fn icmpsum(&self) -> u16 {
355 unsafe { scamper_ping::scamper_ping_icmpsum_get(self.inner) }
356 }
357
358 pub fn tcpseq(&self) -> u32 {
359 unsafe { scamper_ping::scamper_ping_tcpseq_get(self.inner) }
360 }
361
362 pub fn tcpack(&self) -> u32 {
363 unsafe { scamper_ping::scamper_ping_tcpack_get(self.inner) }
364 }
365
366 pub fn flags(&self) -> u32 {
367 unsafe { scamper_ping::scamper_ping_flags_get(self.inner) }
368 }
369
370 pub fn stop_count(&self) -> u16 {
371 unsafe { scamper_ping::scamper_ping_stop_count_get(self.inner) }
372 }
373
374 pub fn pmtu(&self) -> u16 {
375 unsafe { scamper_ping::scamper_ping_pmtu_get(self.inner) }
376 }
377
378 pub fn sent(&self) -> u16 {
379 unsafe { scamper_ping::scamper_ping_sent_get(self.inner) }
380 }
381
382 pub fn datalen(&self) -> u16 {
383 unsafe { scamper_ping::scamper_ping_datalen_get(self.inner) }
384 }
385
386 pub fn data(&self) -> &[u8] {
387 let len = unsafe { scamper_ping::scamper_ping_datalen_get(self.inner) };
388 let ptr = unsafe { scamper_ping::scamper_ping_data_get(self.inner) };
389 if ptr.is_null() || len == 0 {
390 return &[];
391 }
392 unsafe { std::slice::from_raw_parts(ptr, len as usize) }
393 }
394
395 pub fn wait_probe(&self) -> Option<Duration> {
396 let tv = unsafe { scamper_ping::scamper_ping_wait_probe_get(self.inner) };
397 timeval_to_duration(tv)
398 }
399
400 pub fn wait_timeout(&self) -> Option<Duration> {
401 let tv = unsafe { scamper_ping::scamper_ping_wait_timeout_get(self.inner) };
402 timeval_to_duration(tv)
403 }
404
405 pub fn is_method_icmp(&self) -> bool {
406 unsafe { scamper_ping::scamper_ping_method_is_icmp(self.inner) != 0 }
407 }
408
409 pub fn is_method_tcp(&self) -> bool {
410 unsafe { scamper_ping::scamper_ping_method_is_tcp(self.inner) != 0 }
411 }
412
413 pub fn is_method_udp(&self) -> bool {
414 unsafe { scamper_ping::scamper_ping_method_is_udp(self.inner) != 0 }
415 }
416
417 pub fn is_method_vary_sport(&self) -> bool {
418 unsafe { scamper_ping::scamper_ping_method_is_vary_sport(self.inner) != 0 }
419 }
420
421 pub fn is_method_vary_dport(&self) -> bool {
422 unsafe { scamper_ping::scamper_ping_method_is_vary_dport(self.inner) != 0 }
423 }
424
425 pub fn list(&self) -> Option<ScamperList> {
426 let ptr = unsafe { scamper_ping::scamper_ping_list_get(self.inner) };
427 unsafe { ScamperList::from_ptr(ptr) }
428 }
429
430 pub fn cycle(&self) -> Option<ScamperCycle> {
431 let ptr = unsafe { scamper_ping::scamper_ping_cycle_get(self.inner) };
432 unsafe { ScamperCycle::from_ptr(ptr) }
433 }
434
435 pub fn probe(&self, i: u16) -> Option<ScamperPingProbe> {
437 let ptr = unsafe { scamper_ping::scamper_ping_probe_get(self.inner, i) };
438 unsafe { ScamperPingProbe::from_ptr(self.inner, ptr) }
439 }
440
441 pub fn stats(&self) -> Option<ScamperPingStats> {
443 let ptr = unsafe { scamper_ping::scamper_ping_stats_alloc(self.inner) };
444 if ptr.is_null() {
445 None
446 } else {
447 Some(ScamperPingStats { inner: ptr })
448 }
449 }
450
451 pub fn to_json(&self) -> Option<String> {
453 let mut len = 0usize;
454 let ptr = unsafe { scamper_ping::scamper_ping_tojson(self.inner, &mut len) };
455 if ptr.is_null() {
456 return None;
457 }
458 let s = unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned();
459 unsafe { libc::free(ptr as *mut libc::c_void) };
460 Some(s)
461 }
462}
463
464impl Drop for ScamperPing {
465 fn drop(&mut self) {
466 unsafe { scamper_ping::scamper_ping_free(self.inner) };
467 }
468}
469
470unsafe impl Send for ScamperPing {}
471unsafe impl Sync for ScamperPing {}