mirror of
https://github.com/zaphar/icmp-socket.git
synced 2025-07-22 19:30:05 -04:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
02b0714302 | |||
b01f831a61 | |||
6d12901286 | |||
4c666baf78 | |||
dad85434a4 | |||
![]() |
65ef9acf45 | ||
![]() |
b30dfb48bd | ||
a798645326 | |||
07c88084d8 | |||
d5672b00c6 | |||
3b5f3f9a25 | |||
5a00edf5e8 | |||
2dda318253 | |||
6eb42ddf36 | |||
3a73734015 | |||
e331d94a26 |
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "icmp-socket"
|
||||
description = "ICMP sockets for both IPv4 and IPv6"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
repository = "https://github.com/zaphar/icmp-socket"
|
||||
authors = ["Jeremy Wall <jeremy@marzhillstudios.com>"]
|
||||
edition = "2018"
|
||||
@ -10,5 +10,8 @@ license = "Apache-2.0"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
socket2 = "0.3.19"
|
||||
byteorder = "1.3.4"
|
||||
byteorder = "1.3.4"
|
||||
|
||||
[dependencies.socket2]
|
||||
version = "0.4.4"
|
||||
features=[ "all" ]
|
||||
|
22
README.md
22
README.md
@ -1 +1,21 @@
|
||||
# ICMP Sockets for both IPv4 and IPv6
|
||||
# ICMP Sockets for both IPv4 and IPv6
|
||||
|
||||
An implementation of ICMP Sockets for both IPv4 and IPv6.
|
||||
|
||||
Sockets can be created from IP addresses. IPv4 addresses will construct ICMP4 sockets. IPv6 will construct ICMP6 sockets.
|
||||
|
||||
```rust
|
||||
let parsed_addr = "127.0.0.1".parse::<Ipv4Addr>().unwrap();
|
||||
let socket = IcmpSocket4::try_from(parsed_addr).unwrap();
|
||||
```
|
||||
|
||||
It can construct and parse the common ICMP packets for both ICMP4 and ICMP6.
|
||||
|
||||
```rust
|
||||
let packet4 = Icmpv4Packet::with_echo_request(42, 1, "payload".to_bytes());
|
||||
let packet6 = Icmpv6Packet::with_echo_request(42, 1, "payload".to_bytes());
|
||||
```
|
||||
|
||||
# API Documentation
|
||||
|
||||
https://docs.rs/icmp-socket/0.2.0
|
@ -74,15 +74,17 @@ pub fn main() {
|
||||
socket4
|
||||
.send_to(address.parse::<Ipv4Addr>().unwrap(), packet)
|
||||
.unwrap();
|
||||
socket4.set_timeout(Some(Duration::from_secs(1)));
|
||||
loop {
|
||||
let (resp, sock_addr) = match socket4.rcv_with_timeout(Duration::from_secs(1)) {
|
||||
let (resp, sock_addr) = match socket4.rcv_from() {
|
||||
Ok(tpl) => tpl,
|
||||
Err(e) => {
|
||||
//eprintln!("{:?}", e);
|
||||
eprintln!("{:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if packet_handler(resp, send_time, *sock_addr.as_inet().unwrap().ip()).is_some() {
|
||||
if packet_handler(resp, send_time, *sock_addr.as_socket_ipv4().unwrap().ip()).is_some()
|
||||
{
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
break;
|
||||
}
|
||||
|
@ -72,15 +72,17 @@ pub fn main() {
|
||||
socket6
|
||||
.send_to(address.parse::<Ipv6Addr>().unwrap(), packet)
|
||||
.unwrap();
|
||||
socket6.set_timeout(Some(Duration::from_secs(1)));
|
||||
loop {
|
||||
let (resp, sock_addr) = match socket6.rcv_with_timeout(Duration::from_secs(1)) {
|
||||
let (resp, sock_addr) = match socket6.rcv_from() {
|
||||
Ok(tpl) => tpl,
|
||||
Err(e) => {
|
||||
//eprintln!("{:?}", e);
|
||||
eprintln!("{:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if packet_handler(resp, send_time, *sock_addr.as_inet6().unwrap().ip()).is_some() {
|
||||
if packet_handler(resp, send_time, *sock_addr.as_socket_ipv6().unwrap().ip()).is_some()
|
||||
{
|
||||
std::thread::sleep(Duration::from_millis(1000));
|
||||
break;
|
||||
}
|
||||
|
@ -11,7 +11,10 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! An ICMP socket library that tries to be ergonomic to use.
|
||||
//!
|
||||
//! The standard ping examples for both Ipv6 and IPv4 are in the examples
|
||||
//! directory.
|
||||
pub mod packet;
|
||||
pub mod socket;
|
||||
|
||||
|
307
src/packet.rs
307
src/packet.rs
@ -11,6 +11,38 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//! Packet parsing and construction.
|
||||
//!
|
||||
//! Where possible we use traits to support a common API for constructing the
|
||||
//! ICMPv4 and ICMPv6 versions of the packets.
|
||||
//!
|
||||
//! Both packet types can be constructed from a slice: `&[u8]` via the [`TryFrom`] trait.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Constructing an ICMPv4 echo request.
|
||||
//! ```
|
||||
//! # use icmp_socket::packet::*;
|
||||
//! let packet = Icmpv4Packet::with_echo_request(
|
||||
//! 42, // An identifier so you can recognize responses to your own packets.
|
||||
//! 0, // the first echo request packet in our sequence.
|
||||
//! "a payload big enough to matter".as_bytes().to_vec()
|
||||
//! ).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Parsing an ICMPv4 packet from a byte buffer.
|
||||
//! ```
|
||||
//! # use icmp_socket::packet::*;
|
||||
//! use std::convert::TryFrom;
|
||||
//! # let packet = Icmpv4Packet::with_echo_request(
|
||||
//! # 42, // An identifier so you can recognize responses to your own packets.
|
||||
//! # 0, // the first echo request packet in our sequence.
|
||||
//! # "a payload big enough to matter".as_bytes().to_vec()
|
||||
//! # ).unwrap();
|
||||
//! # let mut byte_buffer = vec![0; 20];
|
||||
//! # byte_buffer.extend(packet.get_bytes(true)); // convert a packet to bytes with a checksum.
|
||||
//! let parsed_packet = Icmpv4Packet::try_from(byte_buffer.as_slice()).unwrap();
|
||||
//! ```
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
@ -42,6 +74,7 @@ fn sum_big_endian_words(bs: &[u8]) -> u32 {
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// Construct a packet for the EchoRequest messages.
|
||||
pub trait WithEchoRequest {
|
||||
type Packet;
|
||||
|
||||
@ -52,6 +85,45 @@ pub trait WithEchoRequest {
|
||||
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||
}
|
||||
|
||||
/// Construct a packet for Echo Reply messages.
|
||||
/// This packet type is really only used for the ICMPv6 protocol.
|
||||
pub trait WithEchoReply {
|
||||
type Packet;
|
||||
|
||||
fn with_echo_reply(
|
||||
identifier: u16,
|
||||
sequence: u16,
|
||||
payload: Vec<u8>,
|
||||
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||
}
|
||||
|
||||
/// Construct a packet for Destination Unreachable messages.
|
||||
pub trait WithUnreachable {
|
||||
type Packet;
|
||||
|
||||
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||
}
|
||||
|
||||
/// Construct a packet for Parameter Problem messages.
|
||||
pub trait WithParameterProblem {
|
||||
type Packet;
|
||||
type Pointer;
|
||||
|
||||
fn with_parameter_problem(
|
||||
code: u8,
|
||||
pointer: Self::Pointer,
|
||||
packet: Vec<u8>,
|
||||
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||
}
|
||||
|
||||
/// Construct a packet for Time Exceeded messages.
|
||||
pub trait WithTimeExceeded {
|
||||
type Packet;
|
||||
|
||||
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||
}
|
||||
|
||||
/// The possible Icmpv6 Message types.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Icmpv6Message {
|
||||
// NOTE(JWALL): All of the below integers should be parsed as big endian on the
|
||||
@ -94,6 +166,7 @@ use Icmpv6Message::{
|
||||
};
|
||||
|
||||
impl Icmpv6Message {
|
||||
/// Get this Icmpv6Message serialized to bytes.
|
||||
pub fn get_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
match self {
|
||||
@ -155,9 +228,12 @@ pub struct Icmpv6Packet {
|
||||
pub message: Icmpv6Message,
|
||||
}
|
||||
|
||||
/// Error type returned by parsing the ICMP packets.
|
||||
#[derive(Debug)]
|
||||
pub enum PacketParseError {
|
||||
/// Not enough bytes to properly parse the packet from.
|
||||
PacketTooSmall(usize),
|
||||
/// An unrecognized ICMP type.
|
||||
UnrecognizedICMPType(u8),
|
||||
}
|
||||
|
||||
@ -258,24 +334,6 @@ impl Icmpv6Packet {
|
||||
self
|
||||
}
|
||||
|
||||
/// Construct a packet for Destination Unreachable messages.
|
||||
pub fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||
if code > 6 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 1,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
// TODO(jwall): Should we enforce that the packet isn't too big?
|
||||
// It is not supposed to be larger than the minimum IPv6 MTU
|
||||
message: Unreachable {
|
||||
_unused: 0,
|
||||
invoking_packet: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a packet for Packet Too Big messages.
|
||||
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||
Ok(Self {
|
||||
@ -290,62 +348,6 @@ impl Icmpv6Packet {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a packet for Time Exceeded messages.
|
||||
pub fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||
if code > 1 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 3,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
// TODO(jwall): Should we enforce that the packet isn't too big?
|
||||
// It is not supposed to be larger than the minimum IPv6 MTU
|
||||
message: TimeExceeded {
|
||||
_unused: 0,
|
||||
invoking_packet: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a packet for Parameter Problem messages.
|
||||
pub fn with_parameter_problem(
|
||||
code: u8,
|
||||
pointer: u32,
|
||||
packet: Vec<u8>,
|
||||
) -> Result<Self, IcmpPacketBuildError> {
|
||||
if code > 1 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 4,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
message: ParameterProblem {
|
||||
pointer: pointer,
|
||||
invoking_packet: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a packet for Echo Reply messages.
|
||||
pub fn with_echo_reply(
|
||||
identifier: u16,
|
||||
sequence: u16,
|
||||
payload: Vec<u8>,
|
||||
) -> Result<Self, IcmpPacketBuildError> {
|
||||
Ok(Self {
|
||||
typ: 129,
|
||||
code: 0,
|
||||
checksum: 0,
|
||||
message: EchoReply {
|
||||
identifier: identifier,
|
||||
sequence: sequence,
|
||||
payload: payload,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithEchoRequest for Icmpv6Packet {
|
||||
@ -369,6 +371,93 @@ impl WithEchoRequest for Icmpv6Packet {
|
||||
}
|
||||
}
|
||||
|
||||
impl WithEchoReply for Icmpv6Packet {
|
||||
type Packet = Icmpv6Packet;
|
||||
|
||||
fn with_echo_reply(
|
||||
identifier: u16,
|
||||
sequence: u16,
|
||||
payload: Vec<u8>,
|
||||
) -> Result<Self, IcmpPacketBuildError> {
|
||||
Ok(Self {
|
||||
typ: 129,
|
||||
code: 0,
|
||||
checksum: 0,
|
||||
message: EchoReply {
|
||||
identifier: identifier,
|
||||
sequence: sequence,
|
||||
payload: payload,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithUnreachable for Icmpv6Packet {
|
||||
type Packet = Icmpv6Packet;
|
||||
|
||||
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||
if code > 6 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 1,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
// TODO(jwall): Should we enforce that the packet isn't too big?
|
||||
// It is not supposed to be larger than the minimum IPv6 MTU
|
||||
message: Unreachable {
|
||||
_unused: 0,
|
||||
invoking_packet: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithParameterProblem for Icmpv6Packet {
|
||||
type Packet = Icmpv6Packet;
|
||||
type Pointer = u32;
|
||||
|
||||
fn with_parameter_problem(
|
||||
code: u8,
|
||||
pointer: Self::Pointer,
|
||||
packet: Vec<u8>,
|
||||
) -> Result<Self, IcmpPacketBuildError> {
|
||||
if code > 1 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 4,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
message: ParameterProblem {
|
||||
pointer: pointer,
|
||||
invoking_packet: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithTimeExceeded for Icmpv6Packet {
|
||||
type Packet = Icmpv6Packet;
|
||||
|
||||
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||
if code > 1 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 3,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
// TODO(jwall): Should we enforce that the packet isn't too big?
|
||||
// It is not supposed to be larger than the minimum IPv6 MTU
|
||||
message: TimeExceeded {
|
||||
_unused: 0,
|
||||
invoking_packet: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Icmpv6Packet {
|
||||
type Error = PacketParseError;
|
||||
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
||||
@ -376,8 +465,10 @@ impl TryFrom<&[u8]> for Icmpv6Packet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors returned by constructors for a given packet.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum IcmpPacketBuildError {
|
||||
/// The code passed in for the payload was invalid for the message type.
|
||||
InvalidCode(u8),
|
||||
}
|
||||
use IcmpPacketBuildError::InvalidCode;
|
||||
@ -419,6 +510,7 @@ impl From<PacketParseError> for std::io::Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// The various messages for an Icmpv4 packet.
|
||||
#[derive(Debug)]
|
||||
pub enum Icmpv4Message {
|
||||
Unreachable {
|
||||
@ -488,6 +580,7 @@ pub enum Icmpv4Message {
|
||||
}
|
||||
|
||||
impl Icmpv4Message {
|
||||
/// Get this Icmpv4Message serialized as bytes.
|
||||
pub fn get_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(20);
|
||||
match self {
|
||||
@ -599,6 +692,7 @@ impl Icmpv4Message {
|
||||
}
|
||||
}
|
||||
|
||||
/// An Icmpv4 Packet.
|
||||
#[derive(Debug)]
|
||||
pub struct Icmpv4Packet {
|
||||
pub typ: u8,
|
||||
@ -608,6 +702,7 @@ pub struct Icmpv4Packet {
|
||||
}
|
||||
|
||||
impl Icmpv4Packet {
|
||||
/// Parse an Icmpv4Packet from bytes including the IPv4 header.
|
||||
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
|
||||
let mut bytes = bytes.as_ref();
|
||||
let mut packet_len = bytes.len();
|
||||
@ -721,6 +816,7 @@ impl Icmpv4Packet {
|
||||
!sum as u16
|
||||
}
|
||||
|
||||
/// Populate the checksum field of this Packet.
|
||||
pub fn with_checksum(mut self) -> Self {
|
||||
self.checksum = self.calculate_checksum();
|
||||
self
|
||||
@ -755,6 +851,69 @@ impl WithEchoRequest for Icmpv4Packet {
|
||||
}
|
||||
}
|
||||
|
||||
impl WithUnreachable for Icmpv4Packet {
|
||||
type Packet = Icmpv4Packet;
|
||||
|
||||
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
|
||||
if code > 5 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 3,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
message: Icmpv4Message::Unreachable {
|
||||
padding: 0,
|
||||
header: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithParameterProblem for Icmpv4Packet {
|
||||
type Packet = Icmpv4Packet;
|
||||
type Pointer = u8;
|
||||
|
||||
fn with_parameter_problem(
|
||||
code: u8,
|
||||
pointer: Self::Pointer,
|
||||
packet: Vec<u8>,
|
||||
) -> Result<Self::Packet, IcmpPacketBuildError> {
|
||||
if code != 0 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 12,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
message: Icmpv4Message::ParameterProblem {
|
||||
pointer: pointer,
|
||||
padding: (0, 0),
|
||||
header: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithTimeExceeded for Icmpv4Packet {
|
||||
type Packet = Icmpv4Packet;
|
||||
|
||||
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
|
||||
if code > 1 {
|
||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||
}
|
||||
Ok(Self {
|
||||
typ: 11,
|
||||
code: code,
|
||||
checksum: 0,
|
||||
message: Icmpv4Message::TimeExceeded {
|
||||
padding: 0,
|
||||
header: packet,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
105
src/socket.rs
105
src/socket.rs
@ -11,10 +11,14 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! ICMP Socket implementations for both ICMP4 and ICMP6 protocols.
|
||||
//!
|
||||
//! There is a common IcmpSocket trait implemented for both the v4 and v6 protocols.
|
||||
//! The socket is associated to both an address type and packet type.
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::{
|
||||
convert::{Into, TryFrom, TryInto},
|
||||
mem::MaybeUninit,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@ -26,28 +30,38 @@ fn ip_to_socket(ip: &IpAddr) -> SocketAddr {
|
||||
SocketAddr::new(*ip, 0)
|
||||
}
|
||||
|
||||
/// Trait for an IcmpSocket implemented by Icmpv4Socket and Icmpv6Socket.
|
||||
pub trait IcmpSocket {
|
||||
/// The type of address this socket operates on.
|
||||
type AddrType;
|
||||
/// The type of packet this socket handles.
|
||||
type PacketType;
|
||||
|
||||
/// Sets the timeout on the socket for rcv_from. A value of None
|
||||
/// will cause rcv_from to block.
|
||||
fn set_timeout(&mut self, timeout: Option<Duration>);
|
||||
|
||||
/// Sets the ttl for packets sent on this socket. Controls the number of
|
||||
/// hops the packet will be allowed to traverse.
|
||||
fn set_max_hops(&mut self, hops: u32);
|
||||
|
||||
/// Binds this socket to an address.
|
||||
fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()>;
|
||||
|
||||
/// Sends the packet to the given destination.
|
||||
fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()>;
|
||||
|
||||
/// Receive a packet on this socket.
|
||||
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)>;
|
||||
|
||||
fn rcv_with_timeout(
|
||||
&mut self,
|
||||
timeout: Duration,
|
||||
) -> std::io::Result<(Self::PacketType, SockAddr)>;
|
||||
}
|
||||
|
||||
pub struct Opts {
|
||||
/// Options for this socket.
|
||||
struct Opts {
|
||||
hops: u32,
|
||||
timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
/// An ICMPv4 socket.
|
||||
pub struct IcmpSocket4 {
|
||||
bound_to: Option<Ipv4Addr>,
|
||||
buf: Vec<u8>,
|
||||
@ -56,16 +70,32 @@ pub struct IcmpSocket4 {
|
||||
}
|
||||
|
||||
impl IcmpSocket4 {
|
||||
/// Construct a new raw socket. The socket must be bound to an address using `bind_to`
|
||||
/// before it can be used to send and receive packets.
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
let socket = Socket::new(Domain::ipv4(), Type::raw(), Some(Protocol::icmpv4()))?;
|
||||
let socket = Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))?;
|
||||
Self::new_from_socket(socket)
|
||||
}
|
||||
|
||||
fn new_from_socket(socket: Socket) -> std::io::Result<Self> {
|
||||
socket.set_recv_buffer_size(512)?;
|
||||
Ok(Self {
|
||||
bound_to: None,
|
||||
inner: socket,
|
||||
buf: vec![0; 512],
|
||||
opts: Opts { hops: 50 },
|
||||
opts: Opts {
|
||||
hops: 50,
|
||||
timeout: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a new dgram socket. The socket must be bound to an address using `bind_to`
|
||||
/// before it can be used to send and receive packets.
|
||||
pub fn new_dgram_socket() -> std::io::Result<Self> {
|
||||
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?;
|
||||
Self::new_from_socket(socket)
|
||||
}
|
||||
}
|
||||
|
||||
impl IcmpSocket for IcmpSocket4 {
|
||||
@ -93,21 +123,22 @@ impl IcmpSocket for IcmpSocket4 {
|
||||
}
|
||||
|
||||
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
||||
self.inner.set_read_timeout(None);
|
||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
||||
self.inner.set_read_timeout(self.opts.timeout)?;
|
||||
// NOTE(jwall): the `recv_from` implementation promises not to write uninitialised
|
||||
// bytes to the `buf`fer, so this casting is safe.
|
||||
// TODO(jwall): change to `Vec::spare_capacity_mut` when it stabilizes.
|
||||
let mut buf =
|
||||
unsafe { &mut *(self.buf.as_mut_slice() as *mut [u8] as *mut [MaybeUninit<u8>]) };
|
||||
let (read_count, addr) = self.inner.recv_from(&mut buf)?;
|
||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
||||
}
|
||||
|
||||
fn rcv_with_timeout(
|
||||
&mut self,
|
||||
timeout: Duration,
|
||||
) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
||||
self.inner.set_read_timeout(Some(timeout));
|
||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
||||
fn set_timeout(&mut self, timeout: Option<Duration>) {
|
||||
self.opts.timeout = timeout;
|
||||
}
|
||||
}
|
||||
|
||||
/// An Icmpv6 socket.
|
||||
pub struct IcmpSocket6 {
|
||||
bound_to: Option<Ipv6Addr>,
|
||||
inner: Socket,
|
||||
@ -116,16 +147,32 @@ pub struct IcmpSocket6 {
|
||||
}
|
||||
|
||||
impl IcmpSocket6 {
|
||||
/// Construct a new raw socket. The socket must be bound to an address using `bind_to`
|
||||
/// before it can be used to send and receive packets.
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
let socket = Socket::new(Domain::ipv6(), Type::raw(), Some(Protocol::icmpv6()))?;
|
||||
let socket = Socket::new(Domain::IPV6, Type::RAW, Some(Protocol::ICMPV6))?;
|
||||
Self::new_from_socket(socket)
|
||||
}
|
||||
|
||||
fn new_from_socket(socket: Socket) -> std::io::Result<Self> {
|
||||
socket.set_recv_buffer_size(512)?;
|
||||
Ok(Self {
|
||||
bound_to: None,
|
||||
inner: socket,
|
||||
buf: vec![0; 512],
|
||||
opts: Opts { hops: 50 },
|
||||
opts: Opts {
|
||||
hops: 50,
|
||||
timeout: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a new dgram socket. The socket must be bound to an address using `bind_to`
|
||||
/// before it can be used to send and receive packets.
|
||||
pub fn new_dgram_socket() -> std::io::Result<Self> {
|
||||
let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::ICMPV6))?;
|
||||
Self::new_from_socket(socket)
|
||||
}
|
||||
}
|
||||
|
||||
impl IcmpSocket for IcmpSocket6 {
|
||||
@ -167,18 +214,18 @@ impl IcmpSocket for IcmpSocket6 {
|
||||
}
|
||||
|
||||
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
||||
self.inner.set_read_timeout(None);
|
||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
||||
self.inner.set_read_timeout(self.opts.timeout)?;
|
||||
// NOTE(jwall): the `recv_from` implementation promises not to write uninitialised
|
||||
// bytes to the `buf`fer, so this casting is safe.
|
||||
// TODO(jwall): change to `Vec::spare_capacity_mut` when it stabilizes.
|
||||
let mut buf =
|
||||
unsafe { &mut *(self.buf.as_mut_slice() as *mut [u8] as *mut [MaybeUninit<u8>]) };
|
||||
let (read_count, addr) = self.inner.recv_from(&mut buf)?;
|
||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
||||
}
|
||||
|
||||
fn rcv_with_timeout(
|
||||
&mut self,
|
||||
timeout: Duration,
|
||||
) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
||||
self.inner.set_read_timeout(Some(timeout));
|
||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
||||
fn set_timeout(&mut self, timeout: Option<Duration>) {
|
||||
self.opts.timeout = timeout;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user