ping_rs/
lib.rs

1//! Provide ICMP Echo (ping) functionality for both Windows and Linux. This library does not need root/admin privilege for pinging.
2//! It provides sync and async ping functions: [`send_ping`] and [`send_ping_async`].
3//!
4//! Linux version still does not support "Do not Fragment" flag yet.
5//!
6//! # Usage Example
7//!
8//! An example is also provided in `/bin/sample_ping.rs`
9//!
10//! ## Synchronous ping
11//!
12//! ```rust,no_run
13//! use std::time::Duration;
14//!
15//! fn main(){
16//!     let addr = "8.8.8.8".parse().unwrap();
17//!     let data = [1,2,3,4];  // ping data
18//!     let timeout = Duration::from_secs(1);
19//!     let options = ping_rs::PingOptions { ttl: 128, dont_fragment: true };
20//!     let result = ping_rs::send_ping(&addr, timeout, &data, Some(&options));
21//!     match result {
22//!         Ok(reply) => println!("Reply from {}: bytes={} time={}ms TTL={}", reply.address, data.len(), reply.rtt, options.ttl),
23//!         Err(e) => println!("{:?}", e)
24//!     }
25//! }
26//! ```
27//!
28//! ## Asynchronous ping
29//!
30//! Note that `futures` crate is used in this example. Also, data passed in the function has to be wrapped with `Arc` because in Windows' implementation
31//! the address of this data will be passed to Win32 API.
32//!
33//! ```rust,no_run
34//! use std::sync::Arc;
35//! use std::time::Duration;
36//!
37//! fn main(){
38//!     let addr = "8.8.8.8".parse().unwrap();
39//!     let data = [1,2,3,4];  // ping data
40//!     let data_arc = Arc::new(&data[..]);
41//!     let timeout = Duration::from_secs(1);
42//!     let options = ping_rs::PingOptions { ttl: 128, dont_fragment: true };
43//!     let future = ping_rs::send_ping_async(&addr, timeout, data_arc, Some(&options));
44//!     let result = futures::executor::block_on(future);
45//!     match result {
46//!         Ok(reply) => println!("Reply from {}: bytes={} time={}ms TTL={}", reply.address, data.len(), reply.rtt, options.ttl),
47//!         Err(e) => println!("{:?}", e)
48//!     }
49//! }
50//! ```
51
52mod windows_ping;
53mod linux_ping;
54
55use std::io;
56use std::net::IpAddr;
57use std::sync::Arc;
58use std::time::Duration;
59
60/// Contains constant values represent general errors.
61#[allow(non_snake_case)]
62pub mod IpStatus {
63    #![allow(non_upper_case_globals)]
64
65    pub type Type = u32;
66    pub const Success: Type = 0;
67    //BufferTooSmall = 11000 + 1;
68
69    pub const DestinationNetworkUnreachable: Type = 11000 + 2;
70    pub const DestinationHostUnreachable: Type = 11000 + 3;
71    pub const DestinationProtocolUnreachable: Type = 11000 + 4;
72    pub const DestinationPortUnreachable: Type = 11000 + 5;
73    pub const DestinationProhibited: Type = 11000 + 19;
74
75    pub const NoResources: Type = 11000 + 6;
76    pub const BadOption: Type = 11000 + 7;
77    pub const HardwareError: Type = 11000 + 8;
78    pub const PacketTooBig: Type = 11000 + 9;
79    pub const TimedOut: Type = 11000 + 10;
80    pub const BadRoute: Type = 11000 + 12;
81
82    pub const TtlExpired: Type = 11000 + 13;
83    pub const TtlReassemblyTimeExceeded: Type = 11000 + 14;
84
85    pub const ParameterProblem: Type = 11000 + 15;
86    pub const SourceQuench: Type = 11000 + 16;
87    pub const BadDestination: Type = 11000 + 18;
88
89    pub const DestinationUnreachable: Type = 11000 + 40;
90    pub const TimeExceeded: Type = 11000 + 41;
91    pub const BadHeader: Type = 11000 + 42;
92    pub const UnrecognizedNextHeader: Type = 11000 + 43;
93    pub const IcmpError: Type = 11000 + 44;
94    pub const DestinationScopeMismatch: Type = 11000 + 45;
95
96    // for example, no network interfaces are suitable to route the ping package.
97    pub const GeneralFailure: Type = 11000 + 50;
98}
99
100#[derive(Debug, Clone)]
101pub struct PingOptions {
102    /// Package TTL
103    pub ttl: u8,
104
105    /// Socket's Dont Fragment
106    pub dont_fragment: bool
107}
108
109/// Ping reply contains the destination address (from ICMP reply) and Round-Trip Time
110#[derive(Debug, Clone)]
111#[allow(dead_code)]
112pub struct PingReply {
113    /// Destination address from ICMP reply
114    pub address: IpAddr,
115    /// Round-Trip Time in milliseconds
116    pub rtt: u32,
117}
118
119/// Ping errors
120#[derive(Debug, Clone)]
121pub enum PingError {
122    /// Bad request parameters
123    BadParameter(&'static str),
124
125    /// Unspecific OS errors
126    OsError(u32, String),
127
128    /// General Ping errors
129    IpError(IpStatus::Type),
130
131    /// Ping timed out
132    TimedOut,
133
134    /// I/O async pending
135    IoPending,
136
137    /// size of data buffer for ping is too big. The first parameter is the maximum allowed size.
138    DataSizeTooBig(usize),
139}
140
141impl From<io::Error> for PingError {
142    fn from(value: io::Error) -> Self {
143        if value.kind() == io::ErrorKind::WouldBlock { PingError::IoPending }
144        else { PingError::OsError(value.raw_os_error().unwrap_or(-1) as u32, value.to_string()) }
145    }
146}
147
148pub type Result<T> = std::result::Result<T, PingError>;
149pub type PingApiOutput = Result<PingReply>;
150
151#[cfg(windows)]
152use windows_ping as ping_mod;
153
154#[cfg(unix)]
155use linux_ping as ping_mod;
156
157/// Send ICMP Echo package (ping) to the given address.
158#[inline(always)]
159pub fn send_ping(addr: &IpAddr, timeout: Duration, data: &[u8], options: Option<&PingOptions>) -> PingApiOutput {
160    ping_mod::send_ping(addr, timeout, data, options)
161}
162
163/// Asynchronously schedule ICMP Echo package (ping) to the given address. Note that some parameter signatures are different
164/// from [`send_ping`] function, as the caller should manage those parameters' lifetime.
165#[inline(always)]
166pub async fn send_ping_async(addr: &IpAddr, timeout: Duration, data: Arc<&[u8]>, options: Option<&PingOptions>) -> PingApiOutput {
167    ping_mod::send_ping_async(addr, timeout, data, options).await
168}