mirror of
https://github.com/zaphar/icmp-socket.git
synced 2025-07-21 19:29:47 -04:00
Add rcv_with_timeout to the interface
This commit is contained in:
parent
37f3651995
commit
42e39a621f
@ -11,23 +11,58 @@
|
||||
// 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.
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{
|
||||
net::Ipv4Addr,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use icmp_socket::packet::WithEchoRequest;
|
||||
use icmp_socket::socket::IcmpSocket;
|
||||
use icmp_socket::*;
|
||||
|
||||
pub fn main() {
|
||||
let address = std::env::args().nth(1).unwrap_or("127.0.0.1".to_owned());
|
||||
let parsed_addr = address.parse::<Ipv4Addr>().unwrap();
|
||||
let packet_handler = |pkt: Icmpv4Packet, send_time: Instant, addr: Ipv4Addr| -> Option<()> {
|
||||
let now = Instant::now();
|
||||
let elapsed = now - send_time;
|
||||
if addr == parsed_addr {
|
||||
// TODO
|
||||
if let Icmpv4Message::EchoReply {
|
||||
identifier: _,
|
||||
sequence,
|
||||
payload,
|
||||
} = pkt.message
|
||||
{
|
||||
println!(
|
||||
"Ping {} seq={} time={}ms size={}",
|
||||
addr,
|
||||
sequence,
|
||||
(elapsed.as_micros() as f64) / 1000.0,
|
||||
payload.len()
|
||||
);
|
||||
} else {
|
||||
//eprintln!("Discarding non-reply {:?}", pkt);
|
||||
return None;
|
||||
}
|
||||
Some(())
|
||||
} else {
|
||||
eprintln!("Discarding packet from {}", addr);
|
||||
None
|
||||
}
|
||||
};
|
||||
let mut socket4 = IcmpSocket4::new().unwrap();
|
||||
socket4
|
||||
.bind("0.0.0.0".parse::<Ipv4Addr>().unwrap())
|
||||
.unwrap();
|
||||
let mut echo_socket = echo::EchoSocket::new(socket4);
|
||||
echo_socket
|
||||
.send_ping(
|
||||
address.parse::<Ipv4Addr>().unwrap(),
|
||||
// TODO(jwall): The first packet we recieve will be the one we sent.
|
||||
// We need to implement packet filtering for the socket.
|
||||
let mut sequence = 0 as u16;
|
||||
loop {
|
||||
let packet = Icmpv4Packet::with_echo_request(
|
||||
42,
|
||||
&[
|
||||
sequence,
|
||||
vec![
|
||||
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
|
||||
0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
|
||||
0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
|
||||
@ -35,12 +70,23 @@ pub fn main() {
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
let _ = echo_socket.recv_ping();
|
||||
let (resp, _addr) = echo_socket.recv_ping().unwrap();
|
||||
println!(
|
||||
"seq: {}, identifier: {} payload: {}",
|
||||
resp.sequence,
|
||||
resp.identifier,
|
||||
resp.payload.len()
|
||||
);
|
||||
let send_time = Instant::now();
|
||||
socket4
|
||||
.send_to(address.parse::<Ipv4Addr>().unwrap(), packet)
|
||||
.unwrap();
|
||||
loop {
|
||||
let (resp, sock_addr) = match socket4.rcv_with_timeout(Duration::from_secs(1)) {
|
||||
Ok(tpl) => tpl,
|
||||
Err(e) => {
|
||||
//eprintln!("{:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if packet_handler(resp, send_time, *sock_addr.as_inet().unwrap().ip()).is_some() {
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
|
@ -11,21 +11,56 @@
|
||||
// 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.
|
||||
use std::net::Ipv6Addr;
|
||||
use std::{
|
||||
net::Ipv6Addr,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use icmp_socket::packet::WithEchoRequest;
|
||||
use icmp_socket::socket::IcmpSocket;
|
||||
use icmp_socket::*;
|
||||
|
||||
pub fn main() {
|
||||
let address = std::env::args().nth(1).unwrap_or("::1".to_owned());
|
||||
let parsed_addr = address.parse::<Ipv6Addr>().unwrap();
|
||||
let packet_handler = |pkt: Icmpv6Packet, send_time: Instant, addr: Ipv6Addr| -> Option<()> {
|
||||
let now = Instant::now();
|
||||
let elapsed = now - send_time;
|
||||
if addr == parsed_addr {
|
||||
// TODO
|
||||
if let Icmpv6Message::EchoReply {
|
||||
identifier: _,
|
||||
sequence,
|
||||
payload,
|
||||
} = pkt.message
|
||||
{
|
||||
println!(
|
||||
"Ping {} seq={} time={}ms size={}",
|
||||
addr,
|
||||
sequence,
|
||||
(elapsed.as_micros() as f64) / 1000.0,
|
||||
payload.len()
|
||||
);
|
||||
} else {
|
||||
//eprintln!("Discarding non-reply {:?}", pkt);
|
||||
return None;
|
||||
}
|
||||
Some(())
|
||||
} else {
|
||||
eprintln!("Discarding packet from {}", addr);
|
||||
None
|
||||
}
|
||||
};
|
||||
let mut socket6 = IcmpSocket6::new().unwrap();
|
||||
socket6.bind("::0".parse::<Ipv6Addr>().unwrap()).unwrap();
|
||||
let mut echo_socket = echo::EchoSocket::new(socket6);
|
||||
echo_socket
|
||||
.send_ping(
|
||||
address.parse::<Ipv6Addr>().unwrap(),
|
||||
// TODO(jwall): The first packet we recieve will be the one we sent.
|
||||
// We need to implement packet filtering for the socket.
|
||||
let mut sequence = 0 as u16;
|
||||
loop {
|
||||
let packet = Icmpv6Packet::with_echo_request(
|
||||
42,
|
||||
&[
|
||||
sequence,
|
||||
vec![
|
||||
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
|
||||
0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
|
||||
0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
|
||||
@ -33,14 +68,23 @@ pub fn main() {
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
// TODO(jwall): The first packet we recieve will be the one we sent.
|
||||
// We need to implement packet filtering for the socket.
|
||||
let _ = echo_socket.recv_ping();
|
||||
let (resp, _addr) = echo_socket.recv_ping().unwrap();
|
||||
println!(
|
||||
"seq: {}, identifier: {} payload: {}",
|
||||
resp.sequence,
|
||||
resp.identifier,
|
||||
resp.payload.len()
|
||||
);
|
||||
let send_time = Instant::now();
|
||||
socket6
|
||||
.send_to(address.parse::<Ipv6Addr>().unwrap(), packet)
|
||||
.unwrap();
|
||||
loop {
|
||||
let (resp, sock_addr) = match socket6.rcv_with_timeout(Duration::from_secs(1)) {
|
||||
Ok(tpl) => tpl,
|
||||
Err(e) => {
|
||||
//eprintln!("{:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if packet_handler(resp, send_time, *sock_addr.as_inet6().unwrap().ip()).is_some() {
|
||||
std::thread::sleep(Duration::from_millis(1000));
|
||||
break;
|
||||
}
|
||||
}
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
|
137
src/echo.rs
137
src/echo.rs
@ -1,137 +0,0 @@
|
||||
// Copyright 2021 Jeremy Wall
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// 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.
|
||||
use std::convert::{From, TryFrom, TryInto};
|
||||
|
||||
use socket2::SockAddr;
|
||||
|
||||
use crate::{
|
||||
packet::{Icmpv4Message, Icmpv4Packet, Icmpv6Message, Icmpv6Packet, WithEchoRequest},
|
||||
socket::IcmpSocket,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EchoResponse {
|
||||
pub identifier: u16,
|
||||
pub sequence: u16,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TryFrom<Icmpv6Packet> for EchoResponse {
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn try_from(pkt: Icmpv6Packet) -> Result<Self, Self::Error> {
|
||||
if let Icmpv6Message::EchoReply {
|
||||
identifier,
|
||||
sequence,
|
||||
payload,
|
||||
} = pkt.message
|
||||
{
|
||||
Ok(EchoResponse {
|
||||
identifier,
|
||||
sequence,
|
||||
payload,
|
||||
})
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"Incorrect icmpv6 message: {:?}, code: {}",
|
||||
pkt.message, pkt.code
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Icmpv4Packet> for EchoResponse {
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn try_from(pkt: Icmpv4Packet) -> Result<Self, Self::Error> {
|
||||
if let Icmpv4Message::EchoReply {
|
||||
identifier,
|
||||
sequence,
|
||||
payload,
|
||||
} = pkt.message
|
||||
{
|
||||
Ok(EchoResponse {
|
||||
identifier,
|
||||
sequence,
|
||||
payload,
|
||||
})
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"Incorrect icmpv4 message: {:?}, code: {}",
|
||||
pkt.message, pkt.code
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EchoSocket<S> {
|
||||
sequence: u16,
|
||||
inner: S,
|
||||
}
|
||||
|
||||
// TODO(jwall): Make this a trait
|
||||
impl<S> EchoSocket<S>
|
||||
where
|
||||
S: IcmpSocket,
|
||||
S::PacketType: WithEchoRequest<Packet = S::PacketType>
|
||||
+ TryInto<EchoResponse, Error = std::io::Error>
|
||||
+ std::fmt::Debug,
|
||||
{
|
||||
pub fn new(sock: S) -> Self {
|
||||
EchoSocket {
|
||||
inner: sock,
|
||||
sequence: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_max_hops(&mut self, hops: u32) {
|
||||
self.inner.set_max_hops(hops);
|
||||
}
|
||||
|
||||
pub fn send_ping(
|
||||
&mut self,
|
||||
dest: S::AddrType,
|
||||
identifier: u16,
|
||||
payload: &[u8],
|
||||
) -> std::io::Result<()> {
|
||||
let packet =
|
||||
S::PacketType::with_echo_request(identifier, self.sequence, payload.to_owned())?;
|
||||
self.sequence += 1;
|
||||
self.inner.send_to(dest, packet)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_ping(&mut self) -> std::io::Result<(EchoResponse, SockAddr)> {
|
||||
let (packet, addr) = self.inner.rcv_from()?;
|
||||
Ok((packet.try_into()?, addr))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<S> for EchoSocket<S>
|
||||
where
|
||||
S: IcmpSocket,
|
||||
S::PacketType: WithEchoRequest<Packet = S::PacketType>
|
||||
+ TryInto<EchoResponse, Error = std::io::Error>
|
||||
+ std::fmt::Debug,
|
||||
{
|
||||
fn from(sock: S) -> Self {
|
||||
EchoSocket::new(sock)
|
||||
}
|
||||
}
|
@ -12,10 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod echo;
|
||||
pub mod packet;
|
||||
pub mod socket;
|
||||
|
||||
pub use echo::EchoSocket;
|
||||
pub use packet::{Icmpv4Message, Icmpv4Packet, Icmpv6Message, Icmpv6Packet};
|
||||
pub use socket::{IcmpSocket, IcmpSocket4, IcmpSocket6};
|
||||
|
@ -12,8 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::convert::{Into, TryFrom, TryInto};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::{
|
||||
convert::{Into, TryFrom, TryInto},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
|
||||
|
||||
@ -34,6 +37,11 @@ pub trait IcmpSocket {
|
||||
fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()>;
|
||||
|
||||
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 {
|
||||
@ -85,6 +93,16 @@ 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)?;
|
||||
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))
|
||||
}
|
||||
@ -149,6 +167,16 @@ 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)?;
|
||||
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))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user