Refactor API for more ergonomic usage

This commit is contained in:
Jeremy Wall 2021-01-25 20:55:25 -05:00
parent d3e0dfa344
commit 5a7287f10c
6 changed files with 87 additions and 61 deletions

View File

@ -8,5 +8,4 @@ edition = "2018"
[dependencies] [dependencies]
socket2 = "0.3.19" socket2 = "0.3.19"
packet = "0.1.4"
byteorder = "1.3.4" byteorder = "1.3.4"

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
use icmp_socket::socket::IcmpSocket;
use icmp_socket::*; use icmp_socket::*;
pub fn main() { pub fn main() {

View File

@ -14,15 +14,10 @@
use std::convert::{From, TryFrom, TryInto}; use std::convert::{From, TryFrom, TryInto};
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use packet::icmp::echo::Packet; use crate::{
use packet::{Builder, Packet as P}; packet::{Icmpv4Message, Icmpv4Packet, Icmpv6Message, Icmpv6Packet},
socket::{IcmpSocket, IcmpSocket4, IcmpSocket6},
use crate::packet::{Icmpv6Message::EchoReply, Icmpv6Packet}; };
// TODO(jwall): It turns out that the ICMPv6 packets are sufficiently
// different from the ICMPv4 packets. In order to handle them appropriately
// It is going to take some consideration.
use crate::{IcmpSocket4, IcmpSocket6};
#[derive(Debug)] #[derive(Debug)]
pub struct EchoResponse { pub struct EchoResponse {
@ -35,7 +30,7 @@ impl TryFrom<Icmpv6Packet> for EchoResponse {
type Error = std::io::Error; type Error = std::io::Error;
fn try_from(pkt: Icmpv6Packet) -> Result<Self, Self::Error> { fn try_from(pkt: Icmpv6Packet) -> Result<Self, Self::Error> {
if let EchoReply { if let Icmpv6Message::EchoReply {
identifier, identifier,
sequence, sequence,
payload, payload,
@ -58,18 +53,44 @@ impl TryFrom<Icmpv6Packet> for EchoResponse {
} }
} }
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 EchoSocket4 { pub struct EchoSocket4 {
sequence: u16, sequence: u16,
buf: Vec<u8>,
inner: IcmpSocket4, inner: IcmpSocket4,
} }
// TODO(jwall): Make this a trait
impl EchoSocket4 { impl EchoSocket4 {
pub fn new(sock: IcmpSocket4) -> Self { pub fn new(sock: IcmpSocket4) -> Self {
EchoSocket4 { EchoSocket4 {
inner: sock, inner: sock,
sequence: 0, sequence: 0,
buf: Vec::with_capacity(512),
} }
} }
@ -83,41 +104,16 @@ impl EchoSocket4 {
identifier: u16, identifier: u16,
payload: &[u8], payload: &[u8],
) -> std::io::Result<()> { ) -> std::io::Result<()> {
let packet = packet::icmp::Builder::default() let packet =
.echo() Icmpv4Packet::with_echo_request(identifier, self.sequence, payload.to_owned())?;
.unwrap()
.request()
.unwrap()
.identifier(identifier)
.unwrap()
.sequence(self.sequence)
.unwrap()
.payload(payload)
.unwrap()
.build()
.unwrap();
self.sequence += 1; self.sequence += 1;
self.inner.send_to(dest, &packet)?; self.inner.send_to(dest, packet)?;
Ok(()) Ok(())
} }
pub fn recv_ping(&mut self) -> std::io::Result<EchoResponse> { pub fn recv_ping(&mut self) -> std::io::Result<EchoResponse> {
let bytes_read = self.inner.rcv_from(&mut self.buf)?; let packet = self.inner.rcv_from()?;
match Packet::new(&self.buf[0..bytes_read]) { Ok(packet.try_into()?)
Ok(p) => {
return Ok(EchoResponse {
sequence: p.sequence(),
identifier: p.identifier(),
payload: p.payload().to_owned(),
})
}
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Malformed ICMP Response: {:?}", e),
))
}
};
} }
} }

View File

@ -16,5 +16,6 @@ pub mod echo;
pub mod packet; pub mod packet;
pub mod socket; pub mod socket;
pub use crate::packet::{Icmpv6Message, Icmpv6Packet}; pub use echo::{EchoSocket4, EchoSocket6};
pub use socket::{IcmpSocket4, IcmpSocket6}; pub use packet::{Icmpv4Message, Icmpv4Packet, Icmpv6Message, Icmpv6Packet};
pub use socket::{IcmpSocket, IcmpSocket4, IcmpSocket6};

View File

@ -406,6 +406,7 @@ impl From<PacketParseError> for std::io::Error {
} }
} }
#[derive(Debug)]
pub enum Icmpv4Message { pub enum Icmpv4Message {
Unreachable { Unreachable {
// type 3 // type 3
@ -585,6 +586,7 @@ impl Icmpv4Message {
} }
} }
#[derive(Debug)]
pub struct Icmpv4Packet { pub struct Icmpv4Packet {
pub typ: u8, pub typ: u8,
pub code: u8, pub code: u8,

View File

@ -12,17 +12,30 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::convert::{Into, TryFrom}; use std::convert::{Into, TryFrom, TryInto};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use socket2::{Domain, Protocol, Socket, Type}; use socket2::{Domain, Protocol, Socket, Type};
use crate::packet::Icmpv6Packet; use crate::packet::{Icmpv4Packet, Icmpv6Packet};
fn ip_to_socket(ip: &IpAddr) -> SocketAddr { fn ip_to_socket(ip: &IpAddr) -> SocketAddr {
SocketAddr::new(*ip, 0) SocketAddr::new(*ip, 0)
} }
pub trait IcmpSocket {
type AddrType;
type PacketType;
fn set_max_hops(&mut self, hops: u32);
fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()>;
fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()>;
fn rcv_from(&self) -> std::io::Result<Self::PacketType>;
}
pub struct Opts { pub struct Opts {
hops: u32, hops: u32,
} }
@ -41,12 +54,17 @@ impl IcmpSocket4 {
opts: Opts { hops: 50 }, opts: Opts { hops: 50 },
}) })
} }
}
pub fn set_max_hops(&mut self, hops: u32) { impl IcmpSocket for IcmpSocket4 {
type AddrType = Ipv4Addr;
type PacketType = Icmpv4Packet;
fn set_max_hops(&mut self, hops: u32) {
self.opts.hops = hops; self.opts.hops = hops;
} }
pub fn bind<A: Into<Ipv4Addr>>(&mut self, addr: A) -> std::io::Result<()> { fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()> {
let addr = addr.into(); let addr = addr.into();
self.bound_to = Some(addr.clone()); self.bound_to = Some(addr.clone());
let sock = ip_to_socket(&IpAddr::V4(addr)); let sock = ip_to_socket(&IpAddr::V4(addr));
@ -54,17 +72,18 @@ impl IcmpSocket4 {
Ok(()) Ok(())
} }
// TODO(jwall): This should take an actual packet not the payload. fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()> {
pub fn send_to(&mut self, dest: Ipv4Addr, payload: &[u8]) -> std::io::Result<()> {
let dest = ip_to_socket(&IpAddr::V4(dest)); let dest = ip_to_socket(&IpAddr::V4(dest));
self.inner.set_ttl(self.opts.hops)?; self.inner.set_ttl(self.opts.hops)?;
self.inner.send_to(payload, &(dest.into()))?; self.inner
.send_to(&packet.get_bytes(true), &(dest.into()))?;
Ok(()) Ok(())
} }
pub fn rcv_from(&self, buf: &mut [u8]) -> std::io::Result<usize> { fn rcv_from(&self) -> std::io::Result<Self::PacketType> {
let (read_count, _addr) = self.inner.recv_from(buf)?; let mut buf = vec![0; 512];
Ok(read_count) let (read_count, _addr) = self.inner.recv_from(&mut buf)?;
Ok(buf[0..read_count].try_into()?)
} }
} }
@ -82,12 +101,17 @@ impl IcmpSocket6 {
opts: Opts { hops: 50 }, opts: Opts { hops: 50 },
}) })
} }
}
pub fn set_max_hops(&mut self, hops: u32) { impl IcmpSocket for IcmpSocket6 {
type AddrType = Ipv6Addr;
type PacketType = Icmpv6Packet;
fn set_max_hops(&mut self, hops: u32) {
self.opts.hops = hops; self.opts.hops = hops;
} }
pub fn bind<A: Into<Ipv6Addr>>(&mut self, addr: A) -> std::io::Result<()> { fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()> {
let addr = addr.into(); let addr = addr.into();
self.bound_to = Some(addr.clone()); self.bound_to = Some(addr.clone());
let sock = ip_to_socket(&IpAddr::V6(addr)); let sock = ip_to_socket(&IpAddr::V6(addr));
@ -95,7 +119,11 @@ impl IcmpSocket6 {
Ok(()) Ok(())
} }
pub fn send_to(&mut self, dest: Ipv6Addr, mut packet: Icmpv6Packet) -> std::io::Result<()> { fn send_to(
&mut self,
dest: Self::AddrType,
mut packet: Self::PacketType,
) -> std::io::Result<()> {
let source = match self.bound_to { let source = match self.bound_to {
Some(ref addr) => addr, Some(ref addr) => addr,
None => { None => {
@ -113,11 +141,10 @@ impl IcmpSocket6 {
Ok(()) Ok(())
} }
pub fn rcv_from(&self) -> std::io::Result<Icmpv6Packet> { fn rcv_from(&self) -> std::io::Result<Self::PacketType> {
let mut buf = vec![0; 512]; let mut buf = vec![0; 512];
let (read_count, _addr) = self.inner.recv_from(&mut buf)?; let (read_count, _addr) = self.inner.recv_from(&mut buf)?;
let pkt = Icmpv6Packet::parse(&buf[0..read_count])?; Ok(buf[0..read_count].try_into()?)
Ok(pkt)
} }
} }