ntp_udp/
interface.rs

1//! taken from https://docs.rs/nix/latest/src/nix/ifaddrs.rs.html
2//! stripped to just the parts that we need.
3//!
4//! Query network interface addresses
5//!
6//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
7//! of interfaces and their associated addresses.
8
9use std::iter::Iterator;
10use std::net::SocketAddr;
11use std::option::Option;
12
13use crate::raw_socket::interface_iterator::InterfaceIterator;
14
15#[derive(Clone, Copy, PartialEq, Eq)]
16pub struct InterfaceName {
17    bytes: [u8; libc::IFNAMSIZ],
18}
19
20impl std::ops::Deref for InterfaceName {
21    type Target = [u8];
22
23    fn deref(&self) -> &Self::Target {
24        self.bytes.as_slice()
25    }
26}
27
28impl<'de> serde::Deserialize<'de> for InterfaceName {
29    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
30    where
31        D: serde::Deserializer<'de>,
32    {
33        use std::str::FromStr;
34        use InterfaceNameParseError::*;
35
36        let name: String = serde::Deserialize::deserialize(deserializer)?;
37
38        match Self::from_str(&name) {
39            Ok(v) => Ok(v),
40            Err(Empty) => Err(serde::de::Error::custom("interface name empty")),
41            Err(TooLong) => Err(serde::de::Error::custom("interface name too long")),
42        }
43    }
44}
45
46#[derive(Debug)]
47pub enum InterfaceNameParseError {
48    Empty,
49    TooLong,
50}
51
52impl std::str::FromStr for InterfaceName {
53    type Err = InterfaceNameParseError;
54
55    fn from_str(name: &str) -> Result<Self, Self::Err> {
56        if name.is_empty() {
57            return Err(InterfaceNameParseError::Empty);
58        }
59
60        let mut it = name.bytes();
61        let bytes = std::array::from_fn(|_| it.next().unwrap_or_default());
62
63        if it.next().is_some() {
64            Err(InterfaceNameParseError::TooLong)
65        } else {
66            Ok(InterfaceName { bytes })
67        }
68    }
69}
70
71impl InterfaceName {
72    pub const DEFAULT: Option<Self> = None;
73
74    #[cfg(test)]
75    pub const LOOPBACK: Self = Self {
76        bytes: *b"lo\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
77    };
78
79    #[cfg(test)]
80    pub const INVALID: Self = Self {
81        bytes: *b"123412341234123\0",
82    };
83
84    pub fn as_str(&self) -> &str {
85        std::str::from_utf8(self.bytes.as_slice())
86            .unwrap_or_default()
87            .trim_end_matches('\0')
88    }
89
90    pub fn as_cstr(&self) -> &std::ffi::CStr {
91        // TODO: in rust 1.69.0, use
92        // std::ffi::CStr::from_bytes_until_nul(&self.bytes[..]).unwrap()
93
94        // it is an invariant of InterfaceName that the bytes are null-terminated
95        let first_null = self.bytes.iter().position(|b| *b == 0).unwrap();
96        std::ffi::CStr::from_bytes_with_nul(&self.bytes[..=first_null]).unwrap()
97    }
98
99    pub fn to_ifr_name(self) -> [libc::c_char; libc::IFNAMSIZ] {
100        let mut it = self.bytes.iter().copied();
101        [0; libc::IFNAMSIZ].map(|_| it.next().unwrap_or(0) as libc::c_char)
102    }
103
104    pub fn from_socket_addr(local_addr: SocketAddr) -> std::io::Result<Option<Self>> {
105        let matches_inferface = |interface: &InterfaceData| match interface.socket_addr {
106            None => false,
107            Some(address) => address.ip() == local_addr.ip(),
108        };
109
110        match InterfaceIterator::new()?.find(matches_inferface) {
111            Some(interface) => Ok(Some(interface.name)),
112            None => Ok(None),
113        }
114    }
115}
116
117impl std::fmt::Debug for InterfaceName {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        f.debug_tuple("InterfaceName")
120            .field(&self.as_str())
121            .finish()
122    }
123}
124
125impl std::fmt::Display for InterfaceName {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        self.as_str().fmt(f)
128    }
129}
130
131pub fn sockaddr_storage_to_socket_addr(
132    sockaddr_storage: &libc::sockaddr_storage,
133) -> Option<SocketAddr> {
134    // Safety:
135    //
136    // sockaddr_storage always has enough space to store either a sockaddr_in or sockaddr_in6
137    unsafe { sockaddr_to_socket_addr(sockaddr_storage as *const _ as *const libc::sockaddr) }
138}
139
140/// Convert a libc::sockaddr to a rust std::net::SocketAddr
141///
142/// # Safety
143///
144/// According to the posix standard, `sockaddr` does not have a defined size: the size depends on
145/// the value of the `ss_family` field. We assume this to be correct.
146///
147/// In practice, types in rust/c need a statically-known stack size, so they pick some value. In
148/// practice it can be (and is) larger than the `sizeof<libc::sockaddr>` value.
149pub unsafe fn sockaddr_to_socket_addr(sockaddr: *const libc::sockaddr) -> Option<SocketAddr> {
150    // Most (but not all) of the fields in a socket addr are in network byte ordering.
151    // As such, when doing conversions here, we should start from the NATIVE
152    // byte representation, as this will actualy be the big-endian representation
153    // of the underlying value regardless of platform.
154    match unsafe { (*sockaddr).sa_family as libc::c_int } {
155        libc::AF_INET => {
156            // SAFETY: we cast from a libc::sockaddr (alignment 2) to a libc::sockaddr_in (alignment 4)
157            // that means that the pointer is now potentially unaligned. We must used read_unaligned!
158            let inaddr: libc::sockaddr_in =
159                unsafe { std::ptr::read_unaligned(sockaddr as *const libc::sockaddr_in) };
160
161            let socketaddr = std::net::SocketAddrV4::new(
162                std::net::Ipv4Addr::from(inaddr.sin_addr.s_addr.to_ne_bytes()),
163                u16::from_be_bytes(inaddr.sin_port.to_ne_bytes()),
164            );
165
166            Some(std::net::SocketAddr::V4(socketaddr))
167        }
168        libc::AF_INET6 => {
169            // SAFETY: we cast from a libc::sockaddr (alignment 2) to a libc::sockaddr_in6 (alignment 4)
170            // that means that the pointer is now potentially unaligned. We must used read_unaligned!
171            let inaddr: libc::sockaddr_in6 =
172                unsafe { std::ptr::read_unaligned(sockaddr as *const libc::sockaddr_in6) };
173
174            let sin_addr = inaddr.sin6_addr.s6_addr;
175            let segment_bytes: [u8; 16] =
176                unsafe { std::ptr::read_unaligned(&sin_addr as *const _ as *const _) };
177
178            let socketaddr = std::net::SocketAddrV6::new(
179                std::net::Ipv6Addr::from(segment_bytes),
180                u16::from_be_bytes(inaddr.sin6_port.to_ne_bytes()),
181                inaddr.sin6_flowinfo, // NOTE: Despite network byte order, no conversion is needed (see https://github.com/rust-lang/rust/issues/101605)
182                inaddr.sin6_scope_id,
183            );
184
185            Some(std::net::SocketAddr::V6(socketaddr))
186        }
187        _ => None,
188    }
189}
190
191pub struct InterfaceData {
192    pub name: InterfaceName,
193    pub mac: Option<[u8; 6]>,
194    pub socket_addr: Option<SocketAddr>,
195}
196
197#[cfg(test)]
198mod tests {
199    use std::net::{IpAddr, Ipv4Addr, SocketAddr};
200
201    use super::*;
202
203    #[test]
204    fn find_interface() {
205        let socket = std::net::UdpSocket::bind("127.0.0.1:8014").unwrap();
206        let name = InterfaceName::from_socket_addr(socket.local_addr().unwrap()).unwrap();
207
208        assert!(name.is_some());
209    }
210
211    #[test]
212    fn find_interface_ipv6() {
213        let socket = std::net::UdpSocket::bind("::1:8015").unwrap();
214        let name = InterfaceName::from_socket_addr(socket.local_addr().unwrap()).unwrap();
215
216        assert!(name.is_some());
217    }
218
219    #[test]
220    fn decode_socket_addr_v4() {
221        let sockaddr = libc::sockaddr {
222            sa_family: libc::AF_INET as libc::sa_family_t,
223            sa_data: [0, 0, 127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
224            #[cfg(any(target_os = "macos", target_os = "freebsd"))]
225            sa_len: 14u8,
226        };
227
228        let socket_addr = unsafe { sockaddr_to_socket_addr(&sockaddr) }.unwrap();
229
230        assert_eq!(
231            socket_addr,
232            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0)
233        );
234
235        //
236
237        let sockaddr = libc::sockaddr {
238            sa_family: libc::AF_INET as libc::sa_family_t,
239            sa_data: [0, 42, -84 as _, 23, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
240            #[cfg(any(target_os = "macos", target_os = "freebsd"))]
241            sa_len: 14u8,
242        };
243
244        let socket_addr = unsafe { sockaddr_to_socket_addr(&sockaddr) }.unwrap();
245
246        assert_eq!(
247            socket_addr,
248            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(172, 23, 0, 1)), 42)
249        );
250    }
251
252    #[test]
253    fn decode_socket_addr_v6() {
254        let raw = [
255            0x20, 0x01, 0x08, 0x88, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
256            0x00, 0x02,
257        ];
258
259        let sockaddr = libc::sockaddr_in6 {
260            sin6_family: libc::AF_INET6 as libc::sa_family_t,
261            sin6_port: u16::from_ne_bytes([0, 32]),
262            sin6_flowinfo: 0,
263            sin6_addr: libc::in6_addr { s6_addr: raw },
264            sin6_scope_id: 0,
265            #[cfg(any(target_os = "macos", target_os = "freebsd"))]
266            sin6_len: 14u8,
267        };
268
269        let socket_addr =
270            unsafe { sockaddr_to_socket_addr(&sockaddr as *const _ as *const _) }.unwrap();
271
272        assert_eq!(socket_addr, "[2001:888:0:2::2]:32".parse().unwrap());
273    }
274}