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}