libarp/
client.rs

1use crate::arp;
2use crate::interfaces::MacAddr;
3use crate::{arp::ArpMessage, interfaces::Interface};
4use pnet::{
5    datalink::DataLinkReceiver,
6    packet::{
7        arp::ArpPacket,
8        ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket},
9    },
10};
11use std::convert::TryInto;
12use std::time::Duration;
13use std::{
14    io::{Error, ErrorKind},
15    net::Ipv4Addr,
16    time::Instant,
17};
18
19/// Struct that encapsulates interaction with (R)ARP messages, such as sending and receiving.
20pub struct ArpClient {
21    rx_channel: Box<dyn DataLinkReceiver>,
22    interface: Interface,
23}
24
25impl ArpClient {
26    /// Create an ARP client on a guessed, "best-suited" interface.
27    pub fn new() -> Result<Self, Error> {
28        ArpClient::new_with_iface(&Interface::new()?)
29    }
30
31    /// Create an ARP client on the interface with the name `iface_name`.
32    pub fn new_with_iface_name(iface_name: &str) -> Result<Self, Error> {
33        let iface = Interface::new_by_name(iface_name);
34
35        match iface {
36            Some(iface) => ArpClient::new_with_iface(&iface),
37            None => Err(Error::new(ErrorKind::NotFound, "No such interface.")),
38        }
39    }
40
41    /// Create an ARP client on the `interface` given.
42    pub fn new_with_iface(interface: &Interface) -> Result<Self, Error> {
43        let result = interface.create_tx_rx_channels();
44
45        match result {
46            Ok((_, rx)) => Ok(ArpClient {
47                rx_channel: rx,
48                interface: interface.clone(),
49            }),
50            Err(err) => Err(err),
51        }
52    }
53
54    /// Send an ARP `message` with the given `timeout`.
55    /// Returns the next ARP message received. (must not necessarily be related to your message sent)
56    #[maybe_async::maybe_async]
57    pub async fn send_message(
58        &mut self,
59        timeout: Option<Duration>,
60        message: ArpMessage,
61    ) -> Result<ArpMessage, Error> {
62        self.send_message_with_check(timeout, message, |arp_message| Some(arp_message))
63            .await
64    }
65
66    /// Send an ARP `message` with the given `timeout`, and perform an arbitrary check `check_answer` on the answer.
67    /// Using `check_answer`, you can check if the received tmessage is related to your previously sent message if needed.
68    /// Returns the first ARP message received that satisfies `check_answer`.
69    #[maybe_async::maybe_async]
70    pub async fn send_message_with_check<T>(
71        &mut self,
72        timeout: Option<Duration>,
73        message: ArpMessage,
74        check_answer: impl Fn(ArpMessage) -> Option<T>,
75    ) -> Result<T, Error> {
76        let unpacked_timeout = match timeout {
77            Some(t) => t,
78            // use Duration::MAX after integrated into Rust stable
79            None => Duration::from_secs(u64::MAX),
80        };
81
82        match message.send(&self.interface) {
83            Err(e) => return Err(e),
84            _ => {}
85        }
86
87        let start_time = Instant::now();
88        while Instant::now() - start_time < unpacked_timeout {
89            match self.receive_next().await {
90                Some(arp_message) => match check_answer(arp_message) {
91                    Some(result) => return Ok(result),
92                    None => {}
93                },
94                _ => {}
95            }
96        }
97
98        return Err(Error::new(ErrorKind::TimedOut, "Timeout"));
99    }
100
101    /// Resolves a given `ip_addr` to a MAC address.
102    /// To achieve this, sends an ARP request with a `timeout`.
103    #[maybe_async::maybe_async]
104    pub async fn ip_to_mac(
105        &mut self,
106        ip_addr: Ipv4Addr,
107        timeout: Option<Duration>,
108    ) -> Result<MacAddr, Error> {
109        let message = ArpMessage::new_arp_request(
110            self.interface.get_mac()?,
111            self.interface.get_ip()?,
112            ip_addr,
113        );
114
115        self.send_message_with_check(timeout, message, |arp_message| {
116            return if arp_message.source_protocol_address == ip_addr
117                && arp_message.operation == arp::Operation::ArpResponse
118            {
119                Some(arp_message.source_hardware_address.into())
120            } else {
121                None
122            };
123        })
124        .await
125    }
126
127    /// Resolves a given `mac_addr` to an IPv4 address.
128    /// To achieve this, sends an RARP request with a `timeout`.
129    #[maybe_async::maybe_async]
130    pub async fn mac_to_ip(
131        &mut self,
132        mac_addr: MacAddr,
133        timeout: Option<Duration>,
134    ) -> Result<Ipv4Addr, Error> {
135        let message =
136            ArpMessage::new_rarp_request(self.interface.get_mac()?.into(), mac_addr.into());
137
138        self.send_message_with_check(timeout, message, |arp_message| {
139            let source_mac: MacAddr = arp_message.source_hardware_address.into();
140
141            if source_mac == mac_addr && arp_message.operation == arp::Operation::RarpResponse {
142                Some(arp_message.target_protocol_address)
143            } else {
144                None
145            }
146        })
147        .await
148    }
149
150    /// Sends `arp_message` on the interface belonging to this client.
151    #[maybe_async::maybe_async]
152    pub async fn send(&self, arp_message: &ArpMessage) -> Result<(), Error> {
153        arp_message.send(&self.interface)
154    }
155
156    /// Returns when the next Ethernet frame has been received. If this frame contains an ARP message,
157    /// returns this message, else returns None.
158    #[maybe_async::maybe_async]
159    pub async fn receive_next(&mut self) -> Option<ArpMessage> {
160        let rx_ethernet_packet = self.next_ethernet_frame().await;
161
162        match rx_ethernet_packet {
163            Some((packet, bytes))
164                if packet.get_ethertype() == EtherTypes::Arp
165                    || packet.get_ethertype() == EtherTypes::Rarp =>
166            {
167                let arp_packet =
168                    ArpPacket::new(&bytes[MutableEthernetPacket::minimum_packet_size()..]).unwrap();
169
170                match arp_packet.try_into() {
171                    Ok(arp_packet) => return Some(arp_packet),
172                    Err(_) => return None,
173                }
174            }
175            _ => return None,
176        }
177    }
178
179    #[maybe_async::maybe_async]
180    async fn next_ethernet_frame(&mut self) -> Option<(EthernetPacket<'_>, &[u8])> {
181        let rx_bytes = match self.rx_channel.next() {
182            Ok(rx_bytes) => rx_bytes,
183            Err(_) => return None,
184        };
185
186        match EthernetPacket::new(&rx_bytes) {
187            Some(frame) => Some((frame, rx_bytes)),
188            None => None,
189        }
190    }
191}