#![allow(deprecated)]
use std::net::IpAddr;
use std::time::Duration;
use crate::errors::Error;
use crate::pinger::{PingerBuilder, Reply, Request, SocketType};
#[cfg(feature = "tokio")]
mod tokio;
const TOKEN_SIZE: usize = 24;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(4);
type Token = [u8; TOKEN_SIZE];
struct Legacy {
builder: PingerBuilder,
request: Request,
timeout: Duration,
}
impl Legacy {
fn send(self) -> Result<PingResult, Error> {
let target = self.request.target;
let mut pinger = self.builder.build()?;
let result = pinger.ping(self.request, self.timeout);
finish(result, pinger.ident(target.is_ipv6()), target)
}
}
fn finish(
result: Result<Reply, Error>,
ident: Option<u16>,
target: IpAddr,
) -> Result<PingResult, Error> {
let reply = result.map_err(|error| match error {
Error::Timeout => Error::from(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"ping request timed out",
)),
error => error,
})?;
Ok(PingResult {
rtt: reply.rtt,
ident: ident.ok_or(Error::InternalError)?,
seq_cnt: reply.seq,
payload: reply.payload,
source: reply.source,
target,
ttl: reply.ttl,
})
}
fn ping_with_socktype(
socket_type: SocketType,
addr: IpAddr,
timeout: Option<Duration>,
ttl: Option<u32>,
ident: Option<u16>,
seq_cnt: Option<u16>,
payload: Option<&Token>,
) -> Result<(), Error> {
Ping {
socket_type,
addr,
timeout,
ttl,
ident,
seq_cnt,
payload,
#[cfg(any(target_os = "linux", target_os = "android"))]
bind_device: None,
}
.send()?;
Ok(())
}
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "use `Pinger` and `Reply` instead")]
#[derive(Debug)]
#[non_exhaustive]
pub struct PingResult {
pub rtt: Duration,
pub ident: u16,
pub seq_cnt: u16,
pub payload: Vec<u8>,
pub source: IpAddr,
#[deprecated(since = "0.7.1", note = "use `source` instead")]
pub target: IpAddr,
pub ttl: Option<u8>,
}
#[doc(hidden)]
pub mod rawsock {
use super::*;
#[deprecated(since = "0.10.0", note = "use `Pinger` instead")]
pub fn ping(
addr: IpAddr,
timeout: Option<Duration>,
ttl: Option<u32>,
ident: Option<u16>,
seq_cnt: Option<u16>,
payload: Option<&Token>,
) -> Result<(), Error> {
ping_with_socktype(SocketType::RAW, addr, timeout, ttl, ident, seq_cnt, payload)
}
}
#[doc(hidden)]
pub mod dgramsock {
use super::*;
#[deprecated(since = "0.10.0", note = "use `Pinger` instead")]
pub fn ping(
addr: IpAddr,
timeout: Option<Duration>,
ttl: Option<u32>,
ident: Option<u16>,
seq_cnt: Option<u16>,
payload: Option<&Token>,
) -> Result<(), Error> {
ping_with_socktype(
SocketType::DGRAM,
addr,
timeout,
ttl,
ident,
seq_cnt,
payload,
)
}
}
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "use `Pinger` instead")]
#[derive(Debug, Clone)]
pub struct Ping<'a> {
socket_type: SocketType,
addr: IpAddr,
timeout: Option<Duration>,
ttl: Option<u32>,
ident: Option<u16>,
seq_cnt: Option<u16>,
payload: Option<&'a Token>,
#[cfg(any(target_os = "linux", target_os = "android"))]
bind_device: Option<&'a str>,
}
impl<'a> Ping<'a> {
pub fn new(addr: IpAddr) -> Self {
let socket_type = if std::env::consts::OS == "windows" {
SocketType::RAW
} else {
SocketType::DGRAM
};
return Ping {
socket_type,
addr,
timeout: None,
ttl: None,
ident: None,
seq_cnt: None,
payload: None,
#[cfg(any(target_os = "linux", target_os = "android"))]
bind_device: None,
};
}
pub fn socket_type(&mut self, socket_type: SocketType) -> &mut Self {
self.socket_type = socket_type;
return self;
}
pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
self.timeout = Some(timeout);
return self;
}
pub fn ttl(&mut self, ttl: u32) -> &mut Self {
self.ttl = Some(ttl);
return self;
}
pub fn ident(&mut self, ident: u16) -> &mut Self {
self.ident = Some(ident);
return self;
}
pub fn seq_cnt(&mut self, seq_cnt: u16) -> &mut Self {
self.seq_cnt = Some(seq_cnt);
return self;
}
pub fn payload(&mut self, payload: &'a Token) -> &mut Self {
self.payload = Some(payload);
return self;
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn bind_device(&mut self, device: &'a str) -> &mut Self {
self.bind_device = Some(device);
return self;
}
pub fn send(&self) -> Result<PingResult, Error> {
self.legacy()?.send()
}
fn legacy(&self) -> Result<Legacy, Error> {
let mut builder = PingerBuilder::new().socket_type(self.socket_type);
let ignores_ident = cfg!(any(target_os = "linux", target_os = "android"))
&& self.socket_type == SocketType::DGRAM;
if let Some(ident) = self.ident.filter(|_| !ignores_ident) {
builder = builder.ident(ident);
}
#[cfg(any(target_os = "linux", target_os = "android"))]
if let Some(device) = self.bind_device {
builder = builder.bind_device(device);
}
let token = self.payload.copied().unwrap_or_else(rand::random);
let mut request = Request::new(self.addr)
.seq(self.seq_cnt.unwrap_or(1))
.payload(token.to_vec());
if let Some(ttl) = self.ttl {
let ttl = u8::try_from(ttl).map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "ttl out of range")
})?;
request = request.ttl(ttl);
}
Ok(Legacy {
builder,
request,
timeout: self.timeout.unwrap_or(DEFAULT_TIMEOUT),
})
}
}
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "use `Pinger` instead")]
pub fn new<'a>(addr: IpAddr) -> Ping<'a> {
return Ping::new(addr);
}
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "use `SocketType::RAW` instead")]
pub const RAW: SocketType = SocketType::RAW;
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "use `SocketType::DGRAM` instead")]
pub const DGRAM: SocketType = SocketType::DGRAM;