tokio-raw 0.0.3

Raw sockets for tokio
Documentation
// Copyright (C) 2020 - Will Glozer. All rights reserved.

use std::convert::TryInto;
use std::net::SocketAddr;
use anyhow::Result;
use tokio_raw::{RawSocket, Domain, Type, Protocol};

#[tokio::main]
async fn main() -> Result<()>  {
    let ip4   = Domain::ipv4();
    let dgram = Type::dgram();
    let icmp4 = Protocol::icmpv4();

    let mut sock = RawSocket::new(ip4, dgram, Some(icmp4))?;

    let ping = IcmpPacket::echo_request(1, 2, b"asdf");
    let dst  = SocketAddr::new("1.1.1.1".parse()?, 0);

    let mut buf = [0u8; 64];
    let pkt = ping.encode(&mut buf);
    sock.send_to(pkt, dst).await?;

    let mut buf = [0u8; 64];
    let (n, from) = sock.recv_from(&mut buf).await?;
    let pong = IcmpPacket::decode(&pkt[..n]);

    println!("{:?}: {:?}", from, pong);

    Ok(())
}

#[derive(Debug)]
enum IcmpPacket<'a> {
    EchoRequest(Echo<'a>),
    EchoReply(Echo<'a>),
}

#[derive(Debug)]
struct Echo<'a> {
    ident: u16,
    seq:   u16,
    body:  &'a [u8],
}

impl<'a> IcmpPacket<'a> {
    const HEADER_SIZE: usize = 8;

    const ECHO_REPLY:   u8 = 0;
    const ECHO_REQUEST: u8 = 8;

    fn echo_request(ident: u16, seq: u16, body: &'a [u8]) -> Self {
        Self::EchoRequest(Echo::new(ident, seq, body))
    }

    fn encode<'b>(&self, pkt: &'b mut [u8]) -> &'b [u8] {
        match self {
            Self::EchoRequest(e) => e.encode(Self::ECHO_REQUEST, pkt),
            Self::EchoReply(e)   => e.encode(Self::ECHO_REPLY,   pkt),
        }
    }

    fn decode(pkt: &'a [u8]) -> Self {
        let kind = pkt[0];
        let code = pkt[1];
        match (kind, code) {
            (Self::ECHO_REPLY,   0) => Self::EchoReply(Echo::decode(pkt)),
            (Self::ECHO_REQUEST, 0) => Self::EchoRequest(Echo::decode(pkt)),
            other                   => panic!("unexpected ICMP msg {:?}", other),
        }
    }
}

impl<'a> Echo<'a> {
    fn new(ident: u16, seq: u16, body: &'a [u8]) -> Self {
        Self { ident, seq, body }
    }

    fn encode<'b>(&self, kind: u8, pkt: &'b mut [u8]) -> &'b [u8] {
        let code:  u8  = 0;
        let cksum: u16 = 0;

        let n = IcmpPacket::HEADER_SIZE + self.body.len();

        pkt[0..2].copy_from_slice(&[kind, code]);
        pkt[2..4].copy_from_slice(&cksum.to_be_bytes());
        pkt[4..6].copy_from_slice(&self.ident.to_be_bytes());
        pkt[6..8].copy_from_slice(&self.seq.to_be_bytes());
        pkt[8..n].copy_from_slice(&self.body);

        &pkt[..n]
    }

    fn decode(pkt: &'a [u8]) -> Self {
        let ident = pkt[4..6].try_into().unwrap();
        let seq   = pkt[6..8].try_into().unwrap();

        Self {
            ident: u16::from_be_bytes(ident),
            seq:   u16::from_be_bytes(seq),
            body:  &pkt[8..],
        }
    }
}