Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/packet/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ impl IpV4Protocol {
pub struct IpV4Packet<'a> {
#[allow(unused)]
pub protocol: IpV4Protocol,
pub ttl: u8,
pub data: &'a [u8],
}

Expand All @@ -56,8 +57,11 @@ impl<'a> IpV4Packet<'a> {
None => return Err(Error::UnknownProtocol),
};

let ttl = data[8];

Ok(Self {
protocol: protocol,
ttl,
data: &data[header_size..],
})
}
Expand Down
26 changes: 22 additions & 4 deletions src/ping.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::io::Read;
use std::net::{IpAddr, SocketAddr};
use std::time::{Duration, SystemTime};

Expand All @@ -25,9 +24,16 @@ pub struct PingResult {
pub ident: u16,
pub seq_cnt: u16,
pub payload: Vec<u8>,
/// The actual source IP address from the reply packet.
pub source: IpAddr,
#[deprecated(since = "0.7.1", note = "use `source` instead")]
pub target: IpAddr,
/// The TTL from the reply IP header. Only available for IPv4 RAW sockets;
/// `None` for IPv4 DGRAM (Linux, no IP header) and all IPv6 responses.
pub ttl: Option<u8>,
}

#[allow(deprecated)]
fn ping_with_socktype(
socket_type: Type,
addr: IpAddr,
Expand Down Expand Up @@ -56,7 +62,7 @@ fn ping_with_socktype(
payload: payload.unwrap_or(default_payload),
};

let mut socket = if dest.is_ipv4() {
let socket = if dest.is_ipv4() {
if request.encode::<IcmpV4>(&mut buffer[..]).is_err() {
return Err(Error::InternalError.into());
}
Expand Down Expand Up @@ -96,8 +102,17 @@ fn ping_with_socktype(
socket.set_read_timeout(Some(timeout - elapsed_time))?;

let mut buffer: [u8; 2048] = [0; 2048];
let n = socket.read(&mut buffer)?;

// socket2 0.6 recv_from requires &mut [MaybeUninit<u8>]; cast is sound
// because MaybeUninit<u8> has the same layout as u8.
let (n, src_addr) = socket.recv_from(unsafe {
std::slice::from_raw_parts_mut(
buffer.as_mut_ptr() as *mut std::mem::MaybeUninit<u8>,
buffer.len(),
)
})?;
let source_ip = src_addr.as_socket().map(|s| s.ip()).unwrap_or(addr);

let mut recv_ttl: Option<u8> = None;
let reply = if dest.is_ipv4() {
// DGRAM socket on Linux may return pure ICMP packet without IP header.
if n == ECHO_REQUEST_BUFFER_SIZE {
Expand All @@ -110,6 +125,7 @@ fn ping_with_socktype(
Ok(packet) => packet,
Err(_) => return Err(Error::DecodeV4Error.into()),
};
recv_ttl = Some(ipv4_packet.ttl);
match EchoReply::decode::<IcmpV4>(ipv4_packet.data) {
Ok(reply) => reply,
Err(_) => continue,
Expand All @@ -135,7 +151,9 @@ fn ping_with_socktype(
ident: reply.ident,
seq_cnt: reply.seq_cnt,
payload: reply.payload.to_vec(),
source: source_ip,
target: addr,
ttl: recv_ttl,
});
}

Expand Down
6 changes: 3 additions & 3 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn ping_result_fields() {
assert_eq!(result.seq_cnt, custom_seq);
// Check that our custom payload starts the response payload
assert!(result.payload.starts_with(&custom_payload));
assert_eq!(result.target, addr);
assert_eq!(result.source, addr);

// Verify payload is not empty and contains our custom data
assert!(!result.payload.is_empty());
Expand Down Expand Up @@ -195,7 +195,7 @@ fn ping_result_fields_v6() {

assert_eq!(result.ident, custom_ident);
assert_eq!(result.seq_cnt, custom_seq);
assert_eq!(result.target, addr);
assert_eq!(result.source, addr);

// Verify payload is not empty (should be random if not specified)
assert!(!result.payload.is_empty());
Expand All @@ -219,7 +219,7 @@ fn ping_result_raw_socket() {
// Verify PingResult contains expected data
assert!(result.rtt > Duration::from_secs(0));
assert!(result.rtt < timeout);
assert_eq!(result.target, addr);
assert_eq!(result.source, addr);

// Verify ident and seq_cnt are reasonable values
assert!(result.ident > 0);
Expand Down
Loading