Skip to main content

pistol/
ping.rs

1#[cfg(feature = "ping")]
2use chrono::DateTime;
3#[cfg(feature = "ping")]
4use chrono::Local;
5#[cfg(feature = "ping")]
6use prettytable::Cell;
7#[cfg(feature = "ping")]
8use prettytable::Row;
9#[cfg(feature = "ping")]
10use prettytable::Table;
11#[cfg(feature = "ping")]
12use prettytable::row;
13#[cfg(feature = "ping")]
14use std::collections::BTreeMap;
15#[cfg(feature = "ping")]
16use std::fmt;
17#[cfg(feature = "ping")]
18use std::net::IpAddr;
19#[cfg(feature = "ping")]
20use std::net::Ipv4Addr;
21#[cfg(feature = "ping")]
22use std::net::Ipv6Addr;
23#[cfg(feature = "ping")]
24use std::sync::mpsc::channel;
25#[cfg(feature = "ping")]
26use std::time::Duration;
27#[cfg(feature = "ping")]
28use std::time::Instant;
29#[cfg(feature = "ping")]
30use tracing::error;
31
32#[cfg(feature = "ping")]
33pub mod icmp;
34#[cfg(feature = "ping")]
35pub mod icmpv6;
36
37#[cfg(feature = "ping")]
38use crate::Target;
39#[cfg(feature = "ping")]
40use crate::error::PistolError;
41#[cfg(feature = "ping")]
42use crate::layer::infer_addr;
43#[cfg(feature = "ping")]
44use crate::scan::DataRecvStatus;
45#[cfg(feature = "ping")]
46use crate::scan::PortStatus;
47#[cfg(feature = "ping")]
48use crate::scan::tcp;
49#[cfg(feature = "ping")]
50use crate::scan::tcp6;
51#[cfg(feature = "ping")]
52use crate::scan::udp;
53#[cfg(feature = "ping")]
54use crate::scan::udp6;
55#[cfg(feature = "ping")]
56use crate::utils::get_threads_pool;
57#[cfg(feature = "ping")]
58use crate::utils::num_threads_check;
59#[cfg(feature = "ping")]
60use crate::utils::random_port;
61#[cfg(feature = "ping")]
62use crate::utils::time_sec_to_string;
63#[cfg(feature = "ping")]
64use tracing::warn;
65
66#[cfg(feature = "ping")]
67const SYN_PING_DEFAULT_PORT: u16 = 80;
68#[cfg(feature = "ping")]
69const ACK_PING_DEFAULT_PORT: u16 = 80;
70#[cfg(feature = "ping")]
71const UDP_PING_DEFAULT_PORT: u16 = 125;
72
73#[cfg(feature = "ping")]
74#[derive(Debug, Clone, Copy, PartialEq)]
75pub enum PingStatus {
76    Up,
77    Down,
78    Error,
79}
80
81#[cfg(feature = "ping")]
82impl fmt::Display for PingStatus {
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        let s = match self {
85            PingStatus::Up => "up",
86            PingStatus::Down => "down",
87            PingStatus::Error => "error",
88        };
89        write!(f, "{}", s)
90    }
91}
92
93#[cfg(feature = "ping")]
94#[derive(Debug, Clone)]
95pub struct PingReport {
96    pub addr: IpAddr,
97    pub origin: Option<String>,
98    pub status: PingStatus,
99    pub cost: Duration,
100}
101
102#[cfg(feature = "ping")]
103#[derive(Debug, Clone)]
104pub struct PistolPings {
105    pub ping_reports: Vec<PingReport>,
106    pub start_time: DateTime<Local>,
107    pub end_time: DateTime<Local>,
108    max_attempts: usize,
109}
110
111#[cfg(feature = "ping")]
112impl PistolPings {
113    pub fn new(max_attempts: usize) -> PistolPings {
114        PistolPings {
115            ping_reports: Vec::new(),
116            start_time: Local::now(),
117            end_time: Local::now(),
118            max_attempts,
119        }
120    }
121    pub fn value(&self) -> Vec<PingReport> {
122        self.ping_reports.clone()
123    }
124    pub fn finish(&mut self, ping_reports: Vec<PingReport>) {
125        self.end_time = Local::now();
126        self.ping_reports = ping_reports;
127    }
128}
129
130#[cfg(feature = "ping")]
131impl fmt::Display for PistolPings {
132    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        let total_cost = self.end_time - self.start_time;
134
135        let mut table = Table::new();
136        table.add_row(Row::new(vec![
137            Cell::new(&format!(
138                "Ping Results (max_attempts:{})",
139                self.max_attempts
140            ))
141            .style_spec("c")
142            .with_hspan(4),
143        ]));
144
145        table.add_row(row![
146            c -> "id",
147            c -> "addr",
148            c -> "status",
149            c -> "time cost"
150        ]);
151
152        // sorted
153        let mut btm_addr: BTreeMap<IpAddr, PingReport> = BTreeMap::new();
154        for report in &self.ping_reports {
155            btm_addr.insert(report.addr, report.clone());
156        }
157
158        let mut alive_hosts = 0;
159        let mut i = 1;
160        for (_addr, report) in btm_addr {
161            match report.status {
162                PingStatus::Up => alive_hosts += 1,
163                _ => (),
164            }
165            let addr_str = match report.origin {
166                Some(o) => format!("{}({})", report.addr, o),
167                None => format!("{}", report.addr),
168            };
169            let status_str = format!("{}", report.status);
170            let rtt_str = time_sec_to_string(report.cost);
171            table.add_row(row![c -> i, c -> addr_str, c -> status_str, c -> rtt_str]);
172            i += 1;
173        }
174
175        // let help_info = "NOTE:\nThe target host is considered alive\nas long as one of the packets returns\na result that is considered to be alive.";
176        // table.add_row(Row::new(vec![Cell::new(&help_info).with_hspan(4)]));
177
178        let avg_cost = total_cost.as_seconds_f64() / self.ping_reports.len() as f64;
179        let summary = format!(
180            "total cost: {:.3}s\navg cost: {:.3}s\nalive hosts: {}",
181            total_cost.as_seconds_f64(),
182            avg_cost,
183            alive_hosts,
184        );
185        table.add_row(Row::new(vec![Cell::new(&summary).with_hspan(4)]));
186        write!(f, "{}", table)
187    }
188}
189
190#[cfg(feature = "ping")]
191#[derive(Debug, Clone, Copy, PartialEq)]
192pub enum PingMethods {
193    Syn,
194    Ack,
195    Udp,
196    IcmpEcho,
197    IcmpTimeStamp,
198    IcmpAddressMask,
199    Icmpv6Echo,
200}
201
202#[cfg(feature = "ping")]
203fn ping_thread(
204    method: PingMethods,
205    dst_ipv4: Ipv4Addr,
206    dst_port: Option<u16>,
207    src_ipv4: Ipv4Addr,
208    src_port: u16,
209    timeout: Option<Duration>,
210) -> Result<(PingStatus, DataRecvStatus, Duration), PistolError> {
211    let (ping_status, data_return, rtt) = match method {
212        PingMethods::Syn => {
213            let dst_port = match dst_port {
214                Some(p) => p,
215                None => SYN_PING_DEFAULT_PORT,
216            };
217
218            let (port_status, data_return, rtt) =
219                tcp::send_syn_scan_packet(dst_ipv4, dst_port, src_ipv4, src_port, timeout)?;
220            match port_status {
221                PortStatus::Open => (PingStatus::Up, data_return, rtt),
222                _ => (PingStatus::Down, data_return, rtt),
223            }
224        }
225        PingMethods::Ack => {
226            let dst_port = match dst_port {
227                Some(p) => p,
228                None => ACK_PING_DEFAULT_PORT,
229            };
230
231            let (ret, data_return, rtt) =
232                tcp::send_ack_scan_packet(dst_ipv4, dst_port, src_ipv4, src_port, timeout)?;
233            match ret {
234                PortStatus::Unfiltered => (PingStatus::Up, data_return, rtt),
235                _ => (PingStatus::Down, data_return, rtt),
236            }
237        }
238        PingMethods::Udp => {
239            let dst_port = match dst_port {
240                Some(p) => p,
241                None => UDP_PING_DEFAULT_PORT,
242            };
243
244            let (ret, data_return, rtt) =
245                udp::send_udp_scan_packet(dst_ipv4, dst_port, src_ipv4, src_port, timeout)?;
246            match ret {
247                PortStatus::Open => (PingStatus::Up, data_return, rtt),
248                // PortStatus::OpenOrFiltered => (PingStatus::Up, rtt),
249                _ => (PingStatus::Down, data_return, rtt),
250            }
251        }
252        PingMethods::IcmpEcho => {
253            let (ret, data_return, rtt) = icmp::send_icmp_echo_packet(dst_ipv4, src_ipv4, timeout)?;
254            (ret, data_return, rtt)
255        }
256        PingMethods::IcmpTimeStamp => {
257            let (ret, data_return, rtt) =
258                icmp::send_icmp_timestamp_packet(dst_ipv4, src_ipv4, timeout)?;
259            (ret, data_return, rtt)
260        }
261        PingMethods::IcmpAddressMask => {
262            let (ret, data_return, rtt) =
263                icmp::send_icmp_address_mask_packet(dst_ipv4, src_ipv4, timeout)?;
264            (ret, data_return, rtt)
265        }
266        PingMethods::Icmpv6Echo => {
267            return Err(PistolError::PingDetectionMethodError {
268                target: dst_ipv4.into(),
269                method: String::from("icmpv6"),
270            });
271        }
272    };
273    Ok((ping_status, data_return, rtt))
274}
275
276#[cfg(feature = "ping")]
277fn ping_thread6(
278    method: PingMethods,
279    dst_ipv6: Ipv6Addr,
280    dst_port: Option<u16>,
281    src_ipv6: Ipv6Addr,
282    src_port: u16,
283    timeout: Option<Duration>,
284) -> Result<(PingStatus, DataRecvStatus, Duration), PistolError> {
285    let (ping_status, data_return, rtt) = match method {
286        PingMethods::Syn => {
287            let dst_port = match dst_port {
288                Some(p) => p,
289                None => SYN_PING_DEFAULT_PORT,
290            };
291
292            let (ret, data_return, rtt) =
293                tcp6::send_syn_scan_packet(dst_ipv6, dst_port, src_ipv6, src_port, timeout)?;
294            match ret {
295                PortStatus::Open => (PingStatus::Up, data_return, rtt),
296                _ => (PingStatus::Down, data_return, rtt),
297            }
298        }
299        PingMethods::Ack => {
300            let dst_port = match dst_port {
301                Some(p) => p,
302                None => ACK_PING_DEFAULT_PORT,
303            };
304
305            let (ret, data_return, rtt) =
306                tcp6::send_ack_scan_packet(dst_ipv6, dst_port, src_ipv6, src_port, timeout)?;
307            match ret {
308                PortStatus::Unfiltered => (PingStatus::Up, data_return, rtt),
309                _ => (PingStatus::Down, data_return, rtt),
310            }
311        }
312        PingMethods::Udp => {
313            let dst_port = match dst_port {
314                Some(p) => p,
315                None => UDP_PING_DEFAULT_PORT,
316            };
317
318            let (ret, data_return, rtt) =
319                udp6::send_udp_scan_packet(dst_ipv6, dst_port, src_ipv6, src_port, timeout)?;
320            match ret {
321                PortStatus::Open => (PingStatus::Up, data_return, rtt),
322                PortStatus::OpenOrFiltered => (PingStatus::Up, data_return, rtt),
323                _ => (PingStatus::Down, data_return, rtt),
324            }
325        }
326        PingMethods::IcmpEcho | PingMethods::IcmpTimeStamp | PingMethods::IcmpAddressMask => {
327            return Err(PistolError::PingDetectionMethodError {
328                target: dst_ipv6.into(),
329                method: String::from("icmp"),
330            });
331        }
332        PingMethods::Icmpv6Echo => icmpv6::send_icmpv6_ping_packet(dst_ipv6, src_ipv6, timeout)?,
333    };
334    Ok((ping_status, data_return, rtt))
335}
336
337#[cfg(feature = "ping")]
338fn ping(
339    targets: &[Target],
340    num_threads: Option<usize>,
341    method: PingMethods,
342    src_addr: Option<IpAddr>,
343    src_port: Option<u16>,
344    timeout: Option<Duration>,
345    max_attempts: usize,
346) -> Result<PistolPings, PistolError> {
347    let mut pistol_pings = PistolPings::new(max_attempts);
348
349    let num_threads = match num_threads {
350        Some(t) => t,
351        None => {
352            let num_threads = targets.len();
353            let num_threads = num_threads_check(num_threads);
354            num_threads
355        }
356    };
357
358    let pool = get_threads_pool(num_threads);
359    let (tx, rx) = channel();
360    let mut recv_size = 0;
361
362    for target in targets {
363        let dst_addr = target.addr;
364        let origin = target.origin.clone();
365        match dst_addr {
366            IpAddr::V4(_) => {
367                let src_port = match src_port {
368                    Some(p) => p,
369                    None => random_port(),
370                };
371                let tx = tx.clone();
372                let (dst_ipv4, src_ipv4) = match infer_addr(dst_addr, src_addr)? {
373                    Some(ia) => ia.ipv4_addr()?,
374                    None => return Err(PistolError::CanNotFoundSourceAddress),
375                };
376                // println!("{} - {}", src_ipv4, dst_ipv4);
377                let dst_port = if target.ports.len() > 0 {
378                    Some(target.ports[0])
379                } else {
380                    None
381                };
382
383                let no_port_vec = vec![
384                    PingMethods::IcmpEcho,
385                    PingMethods::IcmpTimeStamp,
386                    PingMethods::Icmpv6Echo,
387                ];
388
389                let dst_port = if !no_port_vec.contains(&method) {
390                    dst_port
391                } else {
392                    None
393                };
394                recv_size += 1;
395                pool.execute(move || {
396                    for ind in 0..max_attempts {
397                        let start_time = Instant::now();
398                        let ping_ret =
399                            ping_thread(method, dst_ipv4, dst_port, src_ipv4, src_port, timeout);
400                        if ind == max_attempts - 1 {
401                            // last attempt
402                            let _ =
403                                tx.send((dst_addr, origin.clone(), ping_ret, start_time.elapsed()));
404                        } else {
405                            match ping_ret {
406                                Ok((_port_status, data_return, _)) => {
407                                    match data_return {
408                                        DataRecvStatus::Yes => {
409                                            // conclusions drawn from the returned data
410                                            let _ = tx.send((
411                                                dst_addr,
412                                                origin.clone(),
413                                                ping_ret,
414                                                start_time.elapsed(),
415                                            ));
416                                            break; // quit loop now
417                                        }
418                                        // conclusions from the default policy
419                                        DataRecvStatus::No => (), // continue probing
420                                    }
421                                }
422                                Err(_) => {
423                                    // stop probe immediately if an error occurs
424                                    let _ = tx.send((
425                                        dst_addr,
426                                        origin.clone(),
427                                        ping_ret,
428                                        start_time.elapsed(),
429                                    ));
430                                    break;
431                                }
432                            }
433                        }
434                    }
435                });
436            }
437            IpAddr::V6(_) => {
438                let src_port = match src_port {
439                    Some(p) => p,
440                    None => random_port(),
441                };
442                let tx = tx.clone();
443                let (dst_ipv6, src_ipv6) = match infer_addr(dst_addr, src_addr)? {
444                    Some(ia) => ia.ipv6_addr()?,
445                    None => return Err(PistolError::CanNotFoundSourceAddress),
446                };
447                let dst_port = if target.ports.len() > 0 {
448                    Some(target.ports[0])
449                } else {
450                    None
451                };
452                let dst_port =
453                    if method != PingMethods::IcmpEcho && method != PingMethods::Icmpv6Echo {
454                        dst_port
455                    } else {
456                        None
457                    };
458                recv_size += 1;
459                pool.execute(move || {
460                    for ind in 0..max_attempts {
461                        let start_time = Instant::now();
462                        let ping_ret =
463                            ping_thread6(method, dst_ipv6, dst_port, src_ipv6, src_port, timeout);
464                        if ind == max_attempts - 1 {
465                            // last attempt
466                            let _ =
467                                tx.send((dst_addr, origin.clone(), ping_ret, start_time.elapsed()));
468                        } else {
469                            match ping_ret {
470                                Ok((_port_status, data_return, _)) => {
471                                    match data_return {
472                                        DataRecvStatus::Yes => {
473                                            // conclusions drawn from the returned data
474                                            let _ = tx.send((
475                                                dst_addr,
476                                                origin.clone(),
477                                                ping_ret,
478                                                start_time.elapsed(),
479                                            ));
480                                            break; // quit loop now
481                                        }
482                                        // conclusions from the default policy
483                                        DataRecvStatus::No => (), // continue probing
484                                    }
485                                }
486                                Err(_) => {
487                                    // stop probe immediately if an error occurs
488                                    let _ = tx.send((
489                                        dst_addr,
490                                        origin.clone(),
491                                        ping_ret,
492                                        start_time.elapsed(),
493                                    ));
494                                    break;
495                                }
496                            }
497                        }
498                    }
499                });
500            }
501        }
502    }
503
504    let iter = rx.into_iter().take(recv_size);
505    let mut reports = Vec::new();
506    for (dst_addr, origin, v, elapsed) in iter {
507        match v {
508            Ok((status, _data_return, rtt)) => {
509                let ping_report = PingReport {
510                    addr: dst_addr,
511                    origin,
512                    status,
513                    cost: rtt,
514                };
515                reports.push(ping_report);
516            }
517            Err(e) => match e {
518                PistolError::CanNotFoundMacAddress => {
519                    let scan_report = PingReport {
520                        addr: dst_addr,
521                        origin,
522                        status: PingStatus::Down,
523                        cost: elapsed,
524                    };
525                    reports.push(scan_report);
526                }
527                _ => {
528                    error!("ping error: {}", e);
529                    let scan_report = PingReport {
530                        addr: dst_addr,
531                        origin,
532                        status: PingStatus::Error,
533                        cost: elapsed,
534                    };
535                    reports.push(scan_report);
536                }
537            },
538        }
539    }
540    pistol_pings.finish(reports);
541    Ok(pistol_pings)
542}
543
544/// TCP SYN Ping.
545/// This ping probe stays away from being similar to a SYN port scan, and to keep the probe stealthy,
546/// we chose to have the user manually provide a port number that is open on the target machine instead of traversing all ports.
547#[cfg(feature = "ping")]
548pub fn tcp_syn_ping(
549    targets: &[Target],
550    num_threads: Option<usize>,
551    src_addr: Option<IpAddr>,
552    src_port: Option<u16>,
553    timeout: Option<Duration>,
554    max_attempts: usize,
555) -> Result<PistolPings, PistolError> {
556    ping(
557        targets,
558        num_threads,
559        PingMethods::Syn,
560        src_addr,
561        src_port,
562        timeout,
563        max_attempts,
564    )
565}
566
567/// TCP SYN Ping, raw version.
568#[cfg(feature = "ping")]
569pub fn tcp_syn_ping_raw(
570    dst_addr: IpAddr,
571    dst_port: u16,
572    src_addr: Option<IpAddr>,
573    src_port: Option<u16>,
574    timeout: Option<Duration>,
575) -> Result<(PingStatus, Duration), PistolError> {
576    let src_port = match src_port {
577        Some(p) => p,
578        None => random_port(),
579    };
580    let ia = match infer_addr(dst_addr, src_addr)? {
581        Some(ia) => ia,
582        None => return Err(PistolError::CanNotFoundSourceAddress),
583    };
584    match dst_addr {
585        IpAddr::V4(_) => {
586            let (dst_ipv4, src_ipv4) = ia.ipv4_addr()?;
587            let (ret, _data_return, rtt) =
588                tcp::send_syn_scan_packet(dst_ipv4, dst_port, src_ipv4, src_port, timeout)?;
589            let (s, rtt) = match ret {
590                PortStatus::Open => (PingStatus::Up, rtt),
591                _ => (PingStatus::Down, rtt),
592            };
593            Ok((s, rtt))
594        }
595        IpAddr::V6(_) => {
596            let (dst_ipv6, src_ipv6) = ia.ipv6_addr()?;
597            let (ret, _data_return, rtt) =
598                tcp6::send_syn_scan_packet(dst_ipv6, dst_port, src_ipv6, src_port, timeout)?;
599            let (s, rtt) = match ret {
600                PortStatus::Open => (PingStatus::Up, rtt),
601                _ => (PingStatus::Down, rtt),
602            };
603            Ok((s, rtt))
604        }
605    }
606}
607
608/// TCP ACK Ping.
609/// This ping probe stays away from being similar to a ACK port scan, and to keep the probe stealthy,
610/// we chose to have the user manually provide a port number that is open on the target machine instead of traversing all ports.
611#[cfg(feature = "ping")]
612pub fn tcp_ack_ping(
613    targets: &[Target],
614    num_threads: Option<usize>,
615    src_addr: Option<IpAddr>,
616    src_port: Option<u16>,
617    timeout: Option<Duration>,
618    max_attempts: usize,
619) -> Result<PistolPings, PistolError> {
620    ping(
621        targets,
622        num_threads,
623        PingMethods::Ack,
624        src_addr,
625        src_port,
626        timeout,
627        max_attempts,
628    )
629}
630
631/// TCP ACK Ping, raw version.
632#[cfg(feature = "ping")]
633pub fn tcp_ack_ping_raw(
634    dst_addr: IpAddr,
635    dst_port: u16,
636    src_addr: Option<IpAddr>,
637    src_port: Option<u16>,
638    timeout: Option<Duration>,
639) -> Result<(PingStatus, Duration), PistolError> {
640    let src_port = match src_port {
641        Some(p) => p,
642        None => random_port(),
643    };
644    let ia = match infer_addr(dst_addr, src_addr)? {
645        Some(ia) => ia,
646        None => return Err(PistolError::CanNotFoundSourceAddress),
647    };
648    match dst_addr {
649        IpAddr::V4(_) => {
650            let (dst_ipv4, src_ipv4) = ia.ipv4_addr()?;
651            let (ret, _data_return, rtt) =
652                tcp::send_ack_scan_packet(dst_ipv4, dst_port, src_ipv4, src_port, timeout)?;
653            let (s, rtt) = match ret {
654                PortStatus::Unfiltered => (PingStatus::Up, rtt),
655                _ => (PingStatus::Down, rtt),
656            };
657            Ok((s, rtt))
658        }
659        IpAddr::V6(_) => {
660            let (dst_ipv6, src_ipv6) = ia.ipv6_addr()?;
661            let (ret, _data_return, rtt) =
662                tcp6::send_ack_scan_packet(dst_ipv6, dst_port, src_ipv6, src_port, timeout)?;
663            let (s, rtt) = match ret {
664                PortStatus::Unfiltered => (PingStatus::Up, rtt),
665                _ => (PingStatus::Down, rtt),
666            };
667            Ok((s, rtt))
668        }
669    }
670}
671
672/// UDP Ping.
673/// This ping probe stays away from being similar to a UDP port scan, and to keep the probe stealthy,
674/// we chose to have the user manually provide a port number that is open on the target machine instead of traversing all ports.
675#[cfg(feature = "ping")]
676pub fn udp_ping(
677    targets: &[Target],
678    num_threads: Option<usize>,
679    src_addr: Option<IpAddr>,
680    src_port: Option<u16>,
681    timeout: Option<Duration>,
682    max_attempts: usize,
683) -> Result<PistolPings, PistolError> {
684    ping(
685        targets,
686        num_threads,
687        PingMethods::Udp,
688        src_addr,
689        src_port,
690        timeout,
691        max_attempts,
692    )
693}
694
695/// UDP Ping, raw version.
696#[cfg(feature = "ping")]
697pub fn udp_ping_raw(
698    dst_addr: IpAddr,
699    dst_port: u16,
700    src_addr: Option<IpAddr>,
701    src_port: Option<u16>,
702    timeout: Option<Duration>,
703) -> Result<(PingStatus, Duration), PistolError> {
704    let src_port = match src_port {
705        Some(p) => p,
706        None => random_port(),
707    };
708    let ia = match infer_addr(dst_addr, src_addr)? {
709        Some(ia) => ia,
710        None => return Err(PistolError::CanNotFoundSourceAddress),
711    };
712    match dst_addr {
713        IpAddr::V4(_) => {
714            let (dst_ipv4, src_ipv4) = ia.ipv4_addr()?;
715            let (ret, _data_return, rtt) =
716                udp::send_udp_scan_packet(dst_ipv4, dst_port, src_ipv4, src_port, timeout)?;
717            let (s, rtt) = match ret {
718                PortStatus::Open => (PingStatus::Up, rtt),
719                // PortStatus::OpenOrFiltered => (PingStatus::Up, rtt),
720                _ => (PingStatus::Down, rtt),
721            };
722            Ok((s, rtt))
723        }
724        IpAddr::V6(_) => {
725            let (dst_ipv6, src_ipv6) = ia.ipv6_addr()?;
726            let (ret, _data_return, rtt) =
727                udp6::send_udp_scan_packet(dst_ipv6, dst_port, src_ipv6, src_port, timeout)?;
728            let (s, rtt) = match ret {
729                PortStatus::Open => (PingStatus::Up, rtt),
730                // PortStatus::OpenOrFiltered => (PingStatus::Up, rtt),
731                _ => (PingStatus::Down, rtt),
732            };
733            Ok((s, rtt))
734        }
735    }
736}
737
738/// ICMP Ping (Standard Echo Request).
739/// In addition to the unusual TCP and UDP host discovery types discussed previously,
740/// we can send the standard packets sent by the ubiquitous ping program.
741/// We sends an ICMP type 8 (echo request) packet to the target IP addresses, expecting a type 0 (echo reply) in return from available hosts.
742/// As noted at the beginning of this chapter, many hosts and firewalls now block these packets, rather than responding as required by RFC 1122.
743/// For this reason, ICMP-only scans are rarely reliable enough against unknown targets over the Internet.
744/// But for system administrators monitoring an internal network, this can be a practical and efficient approach.
745#[cfg(feature = "ping")]
746pub fn icmp_echo_ping(
747    targets: &[Target],
748    num_threads: Option<usize>,
749    src_addr: Option<IpAddr>,
750    src_port: Option<u16>,
751    timeout: Option<Duration>,
752    max_attempts: usize,
753) -> Result<PistolPings, PistolError> {
754    ping(
755        targets,
756        num_threads,
757        PingMethods::IcmpEcho,
758        src_addr,
759        src_port,
760        timeout,
761        max_attempts,
762    )
763}
764
765#[cfg(feature = "ping")]
766pub fn icmp_echo_ping_raw(
767    dst_addr: IpAddr,
768    src_addr: Option<IpAddr>,
769    timeout: Option<Duration>,
770) -> Result<PingStatus, PistolError> {
771    let ia = match infer_addr(dst_addr, src_addr)? {
772        Some(ia) => ia,
773        None => return Err(PistolError::CanNotFoundSourceAddress),
774    };
775    match dst_addr {
776        IpAddr::V4(_) => {
777            let (dst_ipv4, src_ipv4) = ia.ipv4_addr()?;
778            let (ret, _data_return, _rtt) =
779                icmp::send_icmp_echo_packet(dst_ipv4, src_ipv4, timeout)?;
780            Ok(ret)
781        }
782        IpAddr::V6(_) => {
783            let (dst_ipv6, src_ipv6) = ia.ipv6_addr()?;
784            let (ret, _data_return, _rtt) =
785                icmpv6::send_icmpv6_ping_packet(dst_ipv6, src_ipv6, timeout)?;
786            Ok(ret)
787        }
788    }
789}
790
791/// ICMP Ping (Timestamp Request).
792/// While echo request is the standard ICMP ping query, Nmap does not stop there.
793/// The ICMP standards (RFC 792 and RFC 950 ) also specify timestamp request,
794/// information request, and address mask request packets as codes 13, 15, and 17,
795/// respectively. While the ostensible purpose for these queries is to learn information such
796/// as address masks and current times, they can easily be used for host discovery.
797/// A system that replies is up and available. Nmap does not currently implement information request packets,
798/// as they are not widely supported. RFC 1122 insists that "a host SHOULD NOT implement these messages".
799/// Timestamp and address mask queries can be sent with the -PP and -PM options, respectively.
800/// A timestamp reply (ICMP code 14) or address mask reply (code 18) discloses that the host is available.
801/// These two queries can be valuable when administrators specifically block echo request packets
802/// while forgetting that other ICMP queries can be used for the same purpose.
803#[cfg(feature = "ping")]
804pub fn icmp_timestamp_ping(
805    targets: &[Target],
806    num_threads: Option<usize>,
807    src_addr: Option<IpAddr>,
808    src_port: Option<u16>,
809    timeout: Option<Duration>,
810    max_attempts: usize,
811) -> Result<PistolPings, PistolError> {
812    ping(
813        targets,
814        num_threads,
815        PingMethods::IcmpTimeStamp,
816        src_addr,
817        src_port,
818        timeout,
819        max_attempts,
820    )
821}
822
823#[cfg(feature = "ping")]
824pub fn icmp_timestamp_ping_raw(
825    dst_addr: IpAddr,
826    src_addr: Option<IpAddr>,
827    timeout: Option<Duration>,
828) -> Result<PingStatus, PistolError> {
829    let ia = match infer_addr(dst_addr, src_addr)? {
830        Some(ia) => ia,
831        None => return Err(PistolError::CanNotFoundSourceAddress),
832    };
833    match dst_addr {
834        IpAddr::V4(_) => {
835            let (dst_ipv4, src_ipv4) = ia.ipv4_addr()?;
836            let (ret, _data_return, _rtt) =
837                icmp::send_icmp_timestamp_packet(dst_ipv4, src_ipv4, timeout)?;
838            Ok(ret)
839        }
840        IpAddr::V6(_) => {
841            warn!("ipv6 address not supported the icmp timestamp ping");
842            let (dst_ipv6, src_ipv6) = ia.ipv6_addr()?;
843            let (ret, _data_return, _rtt) =
844                icmpv6::send_icmpv6_ping_packet(dst_ipv6, src_ipv6, timeout)?;
845            Ok(ret)
846        }
847    }
848}
849
850/// ICMP Ping (Address Mask Request).
851/// (Note: in my local test, this request did not return any reply).
852/// While echo request is the standard ICMP ping query, Nmap does not stop there.
853/// The ICMP standards (RFC 792 and RFC 950 ) also specify timestamp request,
854/// information request, and address mask request packets as codes 13, 15, and 17,
855/// respectively. While the ostensible purpose for these queries is to learn information such
856/// as address masks and current times, they can easily be used for host discovery.
857/// A system that replies is up and available. Nmap does not currently implement information request packets,
858/// as they are not widely supported. RFC 1122 insists that "a host SHOULD NOT implement these messages".
859/// Timestamp and address mask queries can be sent with the -PP and -PM options, respectively.
860/// A timestamp reply (ICMP code 14) or address mask reply (code 18) discloses that the host is available.
861/// These two queries can be valuable when administrators specifically block echo request packets
862/// while forgetting that other ICMP queries can be used for the same purpose.
863#[cfg(feature = "ping")]
864pub fn icmp_address_mask_ping(
865    targets: &[Target],
866    num_threads: Option<usize>,
867    src_addr: Option<IpAddr>,
868    src_port: Option<u16>,
869    timeout: Option<Duration>,
870    max_attempts: usize,
871) -> Result<PistolPings, PistolError> {
872    ping(
873        targets,
874        num_threads,
875        PingMethods::IcmpAddressMask,
876        src_addr,
877        src_port,
878        timeout,
879        max_attempts,
880    )
881}
882
883#[cfg(feature = "ping")]
884pub fn icmp_address_mask_ping_raw(
885    dst_addr: IpAddr,
886    src_addr: Option<IpAddr>,
887    timeout: Option<Duration>,
888) -> Result<PingStatus, PistolError> {
889    let ia = match infer_addr(dst_addr, src_addr)? {
890        Some(ia) => ia,
891        None => return Err(PistolError::CanNotFoundSourceAddress),
892    };
893    match dst_addr {
894        IpAddr::V4(_) => {
895            let (dst_ipv4, src_ipv4) = ia.ipv4_addr()?;
896            let (ret, _data_return, _rtt) =
897                icmp::send_icmp_address_mask_packet(dst_ipv4, src_ipv4, timeout)?;
898            Ok(ret)
899        }
900        IpAddr::V6(_) => {
901            warn!("ipv6 address not supported the icmp address mask ping");
902            let (dst_ipv6, src_ipv6) = ia.ipv6_addr()?;
903            let (ret, _data_return, _rtt) =
904                icmpv6::send_icmpv6_ping_packet(dst_ipv6, src_ipv6, timeout)?;
905            Ok(ret)
906        }
907    }
908}
909
910/// Sends an ICMPv6 type 128 (echo request) packet (IPv6).
911#[cfg(feature = "ping")]
912pub fn icmpv6_ping(
913    targets: &[Target],
914    num_threads: Option<usize>,
915    src_addr: Option<IpAddr>,
916    src_port: Option<u16>,
917    timeout: Option<Duration>,
918    max_attempts: usize,
919) -> Result<PistolPings, PistolError> {
920    ping(
921        targets,
922        num_threads,
923        PingMethods::Icmpv6Echo,
924        src_addr,
925        src_port,
926        timeout,
927        max_attempts,
928    )
929}
930
931/// ICMP ping, raw version.
932#[cfg(feature = "ping")]
933pub fn icmp_ping_raw(
934    dst_addr: IpAddr,
935    src_addr: Option<IpAddr>,
936    timeout: Option<Duration>,
937) -> Result<(PingStatus, Duration), PistolError> {
938    let ia = match infer_addr(dst_addr, src_addr)? {
939        Some(ia) => ia,
940        None => return Err(PistolError::CanNotFoundSourceAddress),
941    };
942    match dst_addr {
943        IpAddr::V4(_) => {
944            let (dst_ipv4, src_ipv4) = ia.ipv4_addr()?;
945            let (ret, _data_return, rtt) =
946                icmp::send_icmp_echo_packet(dst_ipv4, src_ipv4, timeout)?;
947            Ok((ret, rtt))
948        }
949        IpAddr::V6(_) => {
950            let (dst_ipv6, src_ipv6) = ia.ipv6_addr()?;
951            let (ret, _data_return, rtt) =
952                icmpv6::send_icmpv6_ping_packet(dst_ipv6, src_ipv6, timeout)?;
953            Ok((ret, rtt))
954        }
955    }
956}
957
958#[cfg(feature = "ping")]
959#[cfg(test)]
960mod max_attempts {
961    use super::*;
962    use crate::PistolLogger;
963    use crate::PistolRunner;
964    use crate::Target;
965    use std::str::FromStr;
966    #[test]
967    fn test_tcp_syn_ping() {
968        let _pr = PistolRunner::init(
969            PistolLogger::None,
970            Some(String::from("tcp_syn_ping.pcapng")),
971            None, // use default value
972        )
973        .unwrap();
974        let src_ipv4 = None;
975        let src_port = None;
976        let timeout = Some(Duration::new(1, 0));
977        let addr1 = IpAddr::V4(Ipv4Addr::new(192, 168, 5, 2));
978        let addr2 = IpAddr::V4(Ipv4Addr::new(192, 168, 5, 5));
979        let addr3 = IpAddr::V4(Ipv4Addr::new(192, 168, 5, 10));
980        let target1 = Target::new(addr1, Some(vec![80]));
981        let target2 = Target::new(addr2, Some(vec![80]));
982        let target3 = Target::new(addr3, Some(vec![80]));
983        let max_attempts = 2;
984        let num_threads = Some(8);
985        let ret = tcp_syn_ping(
986            &[target1, target2, target3],
987            // &[target1, target2, target3],
988            num_threads,
989            src_ipv4,
990            src_port,
991            timeout,
992            max_attempts,
993        )
994        .unwrap();
995        println!("{}", ret);
996    }
997    #[test]
998    fn test_tcp_syn_ping_raw() {
999        let _pr = PistolRunner::init(
1000            PistolLogger::None,
1001            Some(String::from("tcp_syn_ping_raw.pcapng")),
1002            None, // use default value
1003        )
1004        .unwrap();
1005        let src_ipv4 = None;
1006        let src_port = None;
1007        let timeout = Some(Duration::new(3, 0));
1008        let addr1 = IpAddr::V4(Ipv4Addr::new(192, 168, 5, 5));
1009        let dst_port = 80;
1010        let (ret, _rtt) = tcp_syn_ping_raw(addr1, dst_port, src_ipv4, src_port, timeout).unwrap();
1011        println!("{:?}", ret);
1012    }
1013    #[test]
1014    fn test_tcp_syn_ping6() {
1015        let _pr = PistolRunner::init(
1016            PistolLogger::None,
1017            Some(String::from("tcp_syn_ping6.pcapng")),
1018            None, // use default value
1019        )
1020        .unwrap();
1021        let src_ipv4 = None;
1022        let src_port = None;
1023        let timeout = Some(Duration::new(1, 0));
1024        let addr1 = Ipv6Addr::from_str("fe80::20c:29ff:fe2c:9e4").unwrap();
1025        let addr2 = Ipv6Addr::from_str("fe80::20c:29ff:fe2c:9e5").unwrap();
1026        let addr3 = Ipv6Addr::from_str("fe80::20c:29ff:fe2c:9e6").unwrap();
1027        let target1 = Target::new(addr1.into(), Some(vec![80]));
1028        let target2 = Target::new(addr2.into(), Some(vec![80]));
1029        let target3 = Target::new(addr3.into(), Some(vec![80]));
1030        let max_attempts = 4;
1031        let num_threads = Some(8);
1032        let ret = tcp_syn_ping(
1033            &[target1, target2, target3],
1034            num_threads,
1035            src_ipv4,
1036            src_port,
1037            timeout,
1038            max_attempts,
1039        )
1040        .unwrap();
1041        println!("{}", ret);
1042    }
1043    #[test]
1044    fn test_icmp_echo_ping() {
1045        let _pr = PistolRunner::init(
1046            PistolLogger::None,
1047            None,
1048            None, // use default value
1049        )
1050        .unwrap();
1051        let src_ipv4 = None;
1052        let src_port: Option<u16> = None;
1053        let timeout = Some(Duration::new(1, 0));
1054        let addr1 = Ipv4Addr::new(192, 168, 5, 5);
1055        // let addr2 = Ipv4Addr::new(192, 168, 5, 1);
1056        // let addr3 = Ipv4Addr::new(192, 168, 5, 100);
1057        // let addr4 = Ipv4Addr::new(192, 168, 1, 4);
1058        let target1 = Target::new(addr1.into(), Some(vec![]));
1059        // let target2 = Target::new(addr2.into(), Some(vec![]));
1060        // let target3 = Target::new(addr3.into(), Some(vec![]));
1061        // let target4 = Target::new(addr4.into(), Some(vec![]));
1062        let max_attempts = 4;
1063        let num_threads = Some(8);
1064        let ret = icmp_echo_ping(
1065            // &[target1, target2, target3, target4],
1066            &[target1],
1067            num_threads,
1068            src_ipv4,
1069            src_port,
1070            timeout,
1071            max_attempts,
1072        )
1073        .unwrap();
1074
1075        println!("{}", ret);
1076    }
1077    #[test]
1078    fn test_icmp_timestamp_ping() {
1079        let _pr = PistolRunner::init(
1080            PistolLogger::None,
1081            None,
1082            None, // use default value
1083        )
1084        .unwrap();
1085        let src_ipv4 = None;
1086        let src_port: Option<u16> = None;
1087        let timeout = Some(Duration::new(1, 0));
1088        let addr1 = Ipv4Addr::new(192, 168, 5, 5);
1089        // let addr2 = Ipv4Addr::new(192, 168, 5, 1);
1090        // let addr3 = Ipv4Addr::new(192, 168, 5, 100);
1091        // let addr4 = Ipv4Addr::new(192, 168, 1, 4);
1092        let target1 = Target::new(addr1.into(), Some(vec![]));
1093        // let target2 = Target::new(addr2.into(), Some(vec![]));
1094        // let target3 = Target::new(addr3.into(), Some(vec![]));
1095        // let target4 = Target::new(addr4.into(), Some(vec![]));
1096        let max_attempts = 4;
1097        let num_threads = Some(8);
1098        let ret = icmp_timestamp_ping(
1099            // &[target1, target2, target3, target4],
1100            &[target1],
1101            num_threads,
1102            src_ipv4,
1103            src_port,
1104            timeout,
1105            max_attempts,
1106        )
1107        .unwrap();
1108        println!("{}", ret);
1109    }
1110    #[test]
1111    fn test_icmp_ping_debug() {
1112        let _pr = PistolRunner::init(
1113            PistolLogger::None,
1114            None,
1115            None, // use default value
1116        )
1117        .unwrap();
1118        let src_ipv4 = None;
1119        let src_port: Option<u16> = None;
1120        let timeout = Some(Duration::new(1, 0));
1121        let targets = Target::from_domain("scanme.nmap.org", None).unwrap();
1122
1123        let max_attempts = 4;
1124        let num_threads = Some(8);
1125        let ret = icmp_echo_ping(
1126            &targets,
1127            num_threads,
1128            src_ipv4,
1129            src_port,
1130            timeout,
1131            max_attempts,
1132        )
1133        .unwrap();
1134        println!("{}", ret.ping_reports.len());
1135        println!("{}", ret);
1136    }
1137    #[test]
1138    fn test_icmpv6_ping() {
1139        let _pr = PistolRunner::init(
1140            PistolLogger::None,
1141            Some(String::from("icmpv6_ping.pcapng")),
1142            None, // use default value
1143        )
1144        .unwrap();
1145        let src_port: Option<u16> = None;
1146        let src_addr = None;
1147        let addr1 = Ipv6Addr::from_str("fe80::20c:29ff:fe2c:9e4").unwrap();
1148        let addr2 = Ipv6Addr::from_str("fe80::20c:29ff:fe2c:9e5").unwrap();
1149        let addr3 = Ipv6Addr::from_str("fe80::20c:29ff:fe2c:9e6").unwrap();
1150        let target1 = Target::new(addr1.into(), Some(vec![80]));
1151        let target2 = Target::new(addr2.into(), Some(vec![80]));
1152        let target3 = Target::new(addr3.into(), Some(vec![80]));
1153        let max_attempts = 4;
1154        let num_threads = Some(8);
1155        let timeout = Some(Duration::new(1, 0));
1156        let ret = icmpv6_ping(
1157            &[target1, target2, target3],
1158            num_threads,
1159            src_addr,
1160            src_port,
1161            timeout,
1162            max_attempts,
1163        )
1164        .unwrap();
1165        println!("{}", ret);
1166    }
1167    #[test]
1168    #[ignore]
1169    fn test_github_issues_14() {
1170        use std::process::Command;
1171        let pid = std::process::id();
1172        let num_threads = Some(8);
1173
1174        for i in 0..10_000 {
1175            let c2 = Command::new("bash")
1176                .arg("-c")
1177                .arg(&format!("lsof -p {} | wc -l", pid))
1178                .output()
1179                .unwrap();
1180            println!(
1181                "pid: {}, lsof output: {}",
1182                &pid,
1183                String::from_utf8_lossy(&c2.stdout)
1184            );
1185            let addr1 = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2));
1186            let target = Target::new(addr1, None);
1187            let _ret = icmp_echo_ping(
1188                &[target],
1189                num_threads,
1190                None,
1191                None,
1192                Some(Duration::new(1, 0)),
1193                1,
1194            )
1195            .unwrap();
1196            // println!("{}\n{:?}", i, ret);
1197            println!("id: {}", i);
1198            // std::thread::sleep(Duration::new(1, 0));
1199        }
1200    }
1201}