shadow_rs/utility/
sockaddr.rs

1use std::borrow::Borrow;
2use std::borrow::BorrowMut;
3use std::ffi::CStr;
4use std::mem::MaybeUninit;
5
6use linux_api::socket::AddressFamily;
7use nix::sys::socket::SockaddrLike;
8use static_assertions::{assert_eq_align, assert_eq_size};
9
10/// A container for any type of socket address.
11#[derive(Clone, Copy)]
12pub struct SockaddrStorage {
13    addr: Addr,
14    len: libc::socklen_t,
15}
16
17#[derive(Clone, Copy)]
18#[repr(C)]
19union Addr {
20    slice: [MaybeUninit<u8>; std::mem::size_of::<libc::sockaddr_storage>()],
21    storage: libc::sockaddr_storage,
22    inet: libc::sockaddr_in,
23    inet6: libc::sockaddr_in6,
24    unix: libc::sockaddr_un,
25    netlink: libc::sockaddr_nl,
26}
27
28// verify there are no larger fields larger than `libc::sockaddr_storage`
29assert_eq_size!(libc::sockaddr_storage, Addr);
30
31// NOTE: If any mutable methods are added to `SockaddrStorage` in the future, they should make sure
32// that the address family cannot be changed. Otherwise a `SockaddrStorage` containing for example a
33// `sockaddr_in` could be reinterpreted as another type such as `sockaddr_un`, the `sockaddr_in` may
34// have uninitialized padding bytes in the location of a field of `sockaddr_un`, and a read of this
35// field of `sockaddr_un` would then cause UB.
36
37impl SockaddrStorage {
38    /// # Safety
39    ///
40    /// - The address must be fully initialized, including padding fields (for example
41    ///   `sockaddr_in.sin_zero`), up until `len` bytes.
42    /// - Padding bytes do not need to be initialized.
43    /// - The address does not need to be aligned.
44    /// - If `len` is large enough for the address to hold the family field, the family must
45    ///   correctly represent the address type. For example if `addr` points to a `sockaddr_in`,
46    ///   then `addr.sin_family` must be `AF_INET`.
47    pub unsafe fn from_ptr(
48        addr: *const MaybeUninit<u8>,
49        len: libc::socklen_t,
50    ) -> Option<SockaddrStorage> {
51        if addr.is_null() {
52            return None;
53        }
54
55        const STORAGE_LEN: usize = std::mem::size_of::<libc::sockaddr_storage>();
56
57        if (len as usize) > STORAGE_LEN {
58            return None;
59        }
60
61        // 'new_addr' starts will all bytes initialized
62        let mut new_addr = [MaybeUninit::new(0u8); STORAGE_LEN];
63
64        // after the copy, 'new_addr' may have uninitialized bytes if `addr` had padding bytes
65        unsafe { std::ptr::copy_nonoverlapping(addr, new_addr.as_mut_ptr(), len as usize) };
66
67        Some(SockaddrStorage {
68            addr: Addr { slice: new_addr },
69            len,
70        })
71    }
72
73    /// # Safety
74    ///
75    /// See [`Self::from_ptr`].
76    pub unsafe fn from_bytes(address: &[MaybeUninit<u8>]) -> Option<Self> {
77        unsafe { Self::from_ptr(address.as_ptr(), address.len().try_into().ok()?) }
78    }
79
80    /// Get the socket protocol family. Will return `None` if the socket address length is too
81    /// short.
82    pub fn family(&self) -> Option<AddressFamily> {
83        if (self.len as usize) < memoffset::span_of!(libc::sockaddr_storage, ss_family).end {
84            return None;
85        }
86
87        // SAFETY: we don't know what bytes of the address have initialized/uninitialized memory,
88        // but we should be guarenteed that the `ss_family` field is initialized for any socket type.
89        Some(AddressFamily::from(unsafe { self.addr.storage }.ss_family))
90    }
91
92    /// If the socket address represents a valid ipv4 socket address (correct family and length),
93    /// returns the ipv4 socket address.
94    pub fn as_inet(&self) -> Option<&nix::sys::socket::SockaddrIn> {
95        if (self.len as usize) < std::mem::size_of::<libc::sockaddr_in>() {
96            return None;
97        }
98        if self.family() != Some(AddressFamily::AF_INET) {
99            return None;
100        }
101
102        // SAFETY: Assume that `nix::sys::socket::SockaddrIn` is a transparent wrapper around a
103        // `libc::sockaddr_in`. Verify (as best we can) that this is true.
104        assert_eq_size!(libc::sockaddr_in, nix::sys::socket::SockaddrIn);
105        assert_eq_align!(libc::sockaddr_in, nix::sys::socket::SockaddrIn);
106
107        Some(unsafe {
108            &*(std::ptr::from_ref(&self.addr.inet) as *const nix::sys::socket::SockaddrIn)
109        })
110    }
111
112    /// Get a new `SockaddrStorage` with a copy of the ipv4 socket address.
113    pub fn from_inet(addr: &nix::sys::socket::SockaddrIn) -> Self {
114        // SAFETY: Assume that `nix::sys::socket::SockaddrIn` is a transparent wrapper around a
115        // `libc::sockaddr_in`. Verify (as best we can) that this is true.
116        assert_eq_size!(libc::sockaddr_in, nix::sys::socket::SockaddrIn);
117        assert_eq_align!(libc::sockaddr_in, nix::sys::socket::SockaddrIn);
118
119        unsafe { Self::from_ptr(addr.as_ptr() as *const MaybeUninit<u8>, addr.len()) }.unwrap()
120    }
121
122    /// If the socket address represents a valid ipv6 socket address (correct family and length),
123    /// returns the ipv6 socket address.
124    pub fn as_inet6(&self) -> Option<&nix::sys::socket::SockaddrIn6> {
125        if (self.len as usize) < std::mem::size_of::<libc::sockaddr_in6>() {
126            return None;
127        }
128        if self.family() != Some(AddressFamily::AF_INET6) {
129            return None;
130        }
131
132        // SAFETY: Assume that `nix::sys::socket::SockaddrIn6` is a transparent wrapper around a
133        // `libc::sockaddr_in6`. Verify (as best we can) that this is true.
134        assert_eq_size!(libc::sockaddr_in6, nix::sys::socket::SockaddrIn6);
135        assert_eq_align!(libc::sockaddr_in6, nix::sys::socket::SockaddrIn6);
136
137        Some(unsafe {
138            &*(std::ptr::from_ref(&self.addr.inet6) as *const nix::sys::socket::SockaddrIn6)
139        })
140    }
141
142    /// Get a new `SockaddrStorage` with a copy of the ipv6 socket address.
143    pub fn from_inet6(addr: &nix::sys::socket::SockaddrIn6) -> Self {
144        // SAFETY: Assume that `nix::sys::socket::SockaddrIn6` is a transparent wrapper around a
145        // `libc::sockaddr_in6`. Verify (as best we can) that this is true.
146        assert_eq_size!(libc::sockaddr_in6, nix::sys::socket::SockaddrIn6);
147        assert_eq_align!(libc::sockaddr_in6, nix::sys::socket::SockaddrIn6);
148
149        unsafe { Self::from_ptr(addr.as_ptr() as *const MaybeUninit<u8>, addr.len()) }.unwrap()
150    }
151
152    /// If the socket address represents a valid unix socket address (correct family and length),
153    /// returns the unix socket address.
154    pub fn as_unix(&self) -> Option<SockaddrUnix<&libc::sockaddr_un>> {
155        if self.family() != Some(AddressFamily::AF_UNIX) {
156            return None;
157        }
158
159        SockaddrUnix::new(unsafe { &self.addr.unix }, self.len)
160    }
161
162    /// Get a new `SockaddrStorage` with a copy of the unix socket address.
163    pub fn from_unix(addr: &SockaddrUnix<&libc::sockaddr_un>) -> Self {
164        let (ptr, len) = addr.as_ptr();
165
166        unsafe { Self::from_ptr(ptr as *const MaybeUninit<u8>, len) }.unwrap()
167    }
168
169    /// If the socket address represents a valid netlink socket address (correct family and length),
170    /// returns the netlink socket address.
171    pub fn as_netlink(&self) -> Option<&nix::sys::socket::NetlinkAddr> {
172        if (self.len as usize) < std::mem::size_of::<libc::sockaddr_nl>() {
173            return None;
174        }
175        if self.family() != Some(AddressFamily::AF_NETLINK) {
176            return None;
177        }
178
179        // SAFETY: Assume that `nix::sys::socket::NetlinkAddr` is a transparent wrapper around a
180        // `libc::sockaddr_nl`. Verify (as best we can) that this is true.
181        assert_eq_size!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
182        assert_eq_align!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
183
184        Some(unsafe { &*(&self.addr.netlink as *const _ as *const nix::sys::socket::NetlinkAddr) })
185    }
186
187    /// Get a new `SockaddrStorage` with a copy of the netlink socket address.
188    pub fn from_netlink(addr: &nix::sys::socket::NetlinkAddr) -> Self {
189        // SAFETY: Assume that `nix::sys::socket::NetlinkAddr` is a transparent wrapper around a
190        // `libc::sockaddr_nl`. Verify (as best we can) that this is true.
191        assert_eq_size!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
192        assert_eq_align!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
193
194        unsafe { Self::from_ptr(addr.as_ptr() as *const MaybeUninit<u8>, addr.len()) }.unwrap()
195    }
196
197    /// A pointer to the socket address. Some bytes may be uninitialized.
198    pub fn as_ptr(&self) -> (*const MaybeUninit<u8>, libc::socklen_t) {
199        (unsafe { &self.addr.slice }.as_ptr(), self.len)
200    }
201
202    /// The socket address as a slice of bytes. Some bytes may be uninitialized.
203    pub fn as_slice(&self) -> &[MaybeUninit<u8>] {
204        unsafe { &self.addr.slice[..(self.len as usize)] }
205    }
206}
207
208impl std::fmt::Debug for SockaddrStorage {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        let as_inet = self.as_inet();
211        let as_inet6 = self.as_inet6();
212        let as_unix = self.as_unix();
213        let as_netlink = self.as_netlink();
214
215        let as_inet = as_inet.map(|x| x as &dyn std::fmt::Debug);
216        let as_inet6 = as_inet6.map(|x| x as &dyn std::fmt::Debug);
217        let as_unix = as_unix.as_ref().map(|x| x as &dyn std::fmt::Debug);
218        let as_netlink = as_netlink.as_ref().map(|x| x as &dyn std::fmt::Debug);
219
220        // find a representation that is not None
221        let options = [as_inet, as_inet6, as_unix, as_netlink];
222        let addr = options.into_iter().find_map(std::convert::identity);
223
224        if let Some(ref addr) = addr {
225            f.debug_struct("SockaddrStorage")
226                .field("len", &self.len)
227                .field("addr", addr)
228                .finish()
229        } else {
230            f.debug_struct("SockaddrStorage")
231                .field("len", &self.len)
232                .field("family", &self.family())
233                .finish_non_exhaustive()
234        }
235    }
236}
237
238impl std::fmt::Display for SockaddrStorage {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        let as_inet = self.as_inet();
241        let as_inet6 = self.as_inet6();
242        let as_unix = self.as_unix();
243        let as_netlink = self.as_netlink();
244
245        let as_inet = as_inet.map(|x| x as &dyn std::fmt::Display);
246        let as_inet6 = as_inet6.map(|x| x as &dyn std::fmt::Display);
247        let as_unix = as_unix.as_ref().map(|x| x as &dyn std::fmt::Display);
248        let as_netlink = as_netlink.as_ref().map(|x| x as &dyn std::fmt::Display);
249
250        // find a representation that is not None
251        let options = [as_inet, as_inet6, as_unix, as_netlink];
252        let addr = options.into_iter().find_map(std::convert::identity);
253
254        if let Some(ref addr) = addr {
255            write!(f, "{addr}")
256        } else {
257            f.debug_struct("SockaddrStorage")
258                .field("len", &self.len)
259                .field("family", &self.family())
260                .finish_non_exhaustive()
261        }
262    }
263}
264
265impl<T> From<SockaddrUnix<T>> for SockaddrStorage
266where
267    T: Borrow<libc::sockaddr_un>,
268{
269    fn from(addr: SockaddrUnix<T>) -> Self {
270        SockaddrStorage::from_unix(&addr.as_ref())
271    }
272}
273
274impl From<nix::sys::socket::SockaddrIn> for SockaddrStorage {
275    fn from(addr: nix::sys::socket::SockaddrIn) -> Self {
276        SockaddrStorage::from_inet(&addr)
277    }
278}
279
280impl From<nix::sys::socket::SockaddrIn6> for SockaddrStorage {
281    fn from(addr: nix::sys::socket::SockaddrIn6) -> Self {
282        SockaddrStorage::from_inet6(&addr)
283    }
284}
285
286impl From<std::net::SocketAddrV4> for SockaddrStorage {
287    fn from(addr: std::net::SocketAddrV4) -> Self {
288        nix::sys::socket::SockaddrIn::from(addr).into()
289    }
290}
291
292impl From<std::net::SocketAddrV6> for SockaddrStorage {
293    fn from(addr: std::net::SocketAddrV6) -> Self {
294        nix::sys::socket::SockaddrIn6::from(addr).into()
295    }
296}
297
298impl From<nix::sys::socket::NetlinkAddr> for SockaddrStorage {
299    fn from(addr: nix::sys::socket::NetlinkAddr) -> Self {
300        SockaddrStorage::from_netlink(&addr)
301    }
302}
303
304/// A Unix socket address.
305///
306/// Typically will be used as an owned address `SockaddrUnix<libc::sockaddr_un>` or a borrowed
307/// address `SockaddrUnix<&libc::sockaddr_un>`, and you can convert between them using methods such
308/// as [`as_ref`](Self::as_ref) or [`into_owned`](Self::into_owned).
309#[derive(Clone, Copy)]
310pub struct SockaddrUnix<T>
311where
312    T: Borrow<libc::sockaddr_un>,
313{
314    addr: T,
315    len: libc::socklen_t,
316}
317
318impl<T> SockaddrUnix<T>
319where
320    T: Borrow<libc::sockaddr_un>,
321{
322    /// Get a new `SockaddrUnix` for a `libc::sockaddr_un`. The `libc::sockaddr_un` must be
323    /// properly initialized. Will return `None` if the length is too short, too long, or if
324    /// `sun_family` is not `AF_UNIX`.
325    pub fn new(addr: T, len: libc::socklen_t) -> Option<Self> {
326        if (len as usize) < memoffset::span_of!(libc::sockaddr_un, sun_family).end {
327            return None;
328        }
329
330        if (len as usize) > std::mem::size_of::<libc::sockaddr_un>() {
331            return None;
332        }
333
334        if addr.borrow().sun_family as i32 != libc::AF_UNIX {
335            return None;
336        }
337
338        Some(Self { addr, len })
339    }
340
341    /// If the socket address represents a pathname address, returns the C string representing the
342    /// filesystem path.
343    pub fn as_path(&self) -> Option<&CStr> {
344        let path = self.sun_path()?;
345
346        // if the address length is too short, or it's an abstract named address
347        if path.is_empty() || path[0] == 0 {
348            return None;
349        }
350
351        // For pathname socket addresses, the path is a C-style nul-terminated string which may be
352        // shorter than the address length (`self.len`). Bytes after the nul are ignored.
353        CStr::from_bytes_until_nul(path).ok()
354    }
355
356    /// If the socket address represents an abstract address, returns the bytes representing the
357    /// name of the abstract socket address. These bytes do not include the nul byte at
358    /// `sun_path[0]`.
359    pub fn as_abstract(&self) -> Option<&[u8]> {
360        let name = self.sun_path()?;
361
362        if name.is_empty() {
363            return None;
364        }
365
366        // the first byte of `sun_path` is always 0 for abstract named socket addresses
367        if name[0] != 0 {
368            return None;
369        }
370
371        Some(&name[1..])
372    }
373
374    /// Is the unix socket address unnamed? On Linux, unnamed unix sockets are unnamed if their
375    /// length is `size_of::<libc::sa_family_t>()`.
376    pub fn is_unnamed(&self) -> bool {
377        (self.len as usize) == memoffset::span_of!(libc::sockaddr_un, sun_family).end
378    }
379
380    /// Returns a slice with the valid bytes of `sun_path`, or `None` if the address length is too
381    /// short.
382    fn sun_path(&self) -> Option<&[u8]> {
383        let path_offset = memoffset::offset_of!(libc::sockaddr_un, sun_path);
384        let path_len = (self.len as usize).checked_sub(path_offset)?;
385
386        Some(i8_to_u8_slice(&self.addr.borrow().sun_path[..path_len]))
387    }
388
389    /// Get an owned unix socket address.
390    pub fn into_owned(self) -> SockaddrUnix<libc::sockaddr_un> {
391        SockaddrUnix {
392            addr: *self.addr.borrow(),
393            len: self.len,
394        }
395    }
396
397    /// Get a borrowed unix socket address.
398    pub fn as_ref(&self) -> SockaddrUnix<&libc::sockaddr_un> {
399        SockaddrUnix {
400            addr: self.addr.borrow(),
401            len: self.len,
402        }
403    }
404
405    /// Get a pointer to the unix socket address. All fields of the `libc::sockaddr_un` will be
406    /// properly initialized.
407    pub fn as_ptr(&self) -> (*const libc::sockaddr_un, libc::socklen_t) {
408        (self.addr.borrow(), self.len)
409    }
410}
411
412impl<T> SockaddrUnix<T>
413where
414    T: BorrowMut<libc::sockaddr_un>,
415{
416    /// Get a mutably borrowed unix socket address.
417    pub fn as_mut(&mut self) -> SockaddrUnix<&mut libc::sockaddr_un> {
418        SockaddrUnix {
419            addr: self.addr.borrow_mut(),
420            len: self.len,
421        }
422    }
423}
424
425impl SockaddrUnix<libc::sockaddr_un> {
426    /// Get a new `SockaddrUnix` with the given path. Will return `None` if the path is empty or is
427    /// too large.
428    pub fn new_path(path: &CStr) -> Option<Self> {
429        let path = path.to_bytes();
430
431        // this should be guaranteed by the CStr
432        debug_assert!(!path.contains(&0));
433
434        if path.is_empty() {
435            // you cannot have a pathname unix socket address with no path
436            return None;
437        }
438
439        let mut addr: libc::sockaddr_un = unsafe { std::mem::zeroed() };
440
441        if path.len() >= std::mem::size_of_val(&addr.sun_path) {
442            // pathname unix sockets should be nul terminated, but there's no room for the nul
443            return None;
444        }
445
446        // there will be a terminating nul byte since the address was zeroed, and the path will
447        // never fill all of `sun_path`
448        addr.sun_family = libc::AF_UNIX as u16;
449        addr.sun_path[..path.len()].copy_from_slice(u8_to_i8_slice(path));
450
451        let len = memoffset::offset_of!(libc::sockaddr_un, sun_path) + path.len() + 1;
452        let len = len as libc::socklen_t;
453
454        Some(Self { addr, len })
455    }
456
457    /// Get a new `SockaddrUnix` with the given abstract address name. The name does not include the
458    /// required nul byte at `sun_path[0]`. Will return `None` if the name is too large.
459    pub fn new_abstract(name: &[u8]) -> Option<Self> {
460        let mut addr: libc::sockaddr_un = unsafe { std::mem::zeroed() };
461
462        if name.len() + 1 > std::mem::size_of_val(&addr.sun_path) {
463            return None;
464        }
465
466        addr.sun_family = libc::AF_UNIX as u16;
467        addr.sun_path[1..][..name.len()].copy_from_slice(u8_to_i8_slice(name));
468
469        let len = memoffset::offset_of!(libc::sockaddr_un, sun_path) + 1 + name.len();
470        let len = len as libc::socklen_t;
471
472        Some(Self { addr, len })
473    }
474
475    /// Get a new unnamed unix socket address.
476    pub fn new_unnamed() -> SockaddrUnix<libc::sockaddr_un> {
477        let mut addr: libc::sockaddr_un = unsafe { std::mem::zeroed() };
478        addr.sun_family = libc::AF_UNIX as u16;
479
480        let len = memoffset::span_of!(libc::sockaddr_un, sun_family).end;
481        assert_eq!(len, 2);
482        let len = len as libc::socklen_t;
483
484        Self { addr, len }
485    }
486}
487
488impl<T> std::fmt::Debug for SockaddrUnix<T>
489where
490    T: Borrow<libc::sockaddr_un>,
491{
492    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
493        f.debug_struct("SockaddrUnix")
494            .field("sun_family", &self.addr.borrow().sun_family)
495            .field("sun_path", &self.sun_path())
496            .finish()
497    }
498}
499
500impl<T> std::fmt::Display for SockaddrUnix<T>
501where
502    T: Borrow<libc::sockaddr_un>,
503{
504    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
505        if let Some(path) = self.as_path() {
506            f.debug_struct("sockaddr_un").field("path", &path).finish()
507        } else if let Some(name) = self.as_abstract() {
508            let name: Vec<u8> = name
509                .iter()
510                .flat_map(|x| std::ascii::escape_default(*x))
511                .collect();
512            let name = String::from_utf8(name).unwrap();
513            f.debug_struct("sockaddr_un")
514                .field("abstract", &name)
515                .finish()
516        } else if self.is_unnamed() {
517            write!(f, "sockaddr_un {{ unnamed }}")
518        } else {
519            f.debug_struct("sockaddr_un")
520                .field("sun_path", &self.sun_path())
521                .finish()
522        }
523    }
524}
525
526impl<T> PartialEq for SockaddrUnix<T>
527where
528    T: Borrow<libc::sockaddr_un>,
529{
530    fn eq(&self, other: &Self) -> bool {
531        // if this assertion fails, something is very wrong
532        assert_eq!(
533            self.addr.borrow().sun_family,
534            other.addr.borrow().sun_family,
535        );
536        self.len == other.len && self.sun_path() == other.sun_path()
537    }
538}
539
540impl<T> Eq for SockaddrUnix<T> where T: Borrow<libc::sockaddr_un> {}
541
542/// Convert a `&[u8]` to `&[i8]`.
543fn u8_to_i8_slice(s: &[u8]) -> &[i8] {
544    unsafe { std::slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
545}
546
547/// Convert a `&[i8]` to `&[u8]`.
548fn i8_to_u8_slice(s: &[i8]) -> &[u8] {
549    unsafe { std::slice::from_raw_parts(s.as_ptr() as *const u8, s.len()) }
550}
551
552#[cfg(test)]
553mod tests {
554    use super::*;
555
556    use std::net::Ipv4Addr;
557
558    /// Convert from a `sockaddr_in` to a `SockaddrStorage`.
559    #[test]
560    fn storage_from_inet_ptr() {
561        let mut addr: libc::sockaddr_in = unsafe { std::mem::zeroed() };
562        addr.sin_family = libc::AF_INET as u16;
563        addr.sin_port = 9000u16.to_be();
564        addr.sin_addr = libc::in_addr {
565            s_addr: libc::INADDR_LOOPBACK.to_be(),
566        };
567
568        let ptr = std::ptr::from_ref(&addr) as *const MaybeUninit<u8>;
569        let len = std::mem::size_of_val(&addr).try_into().unwrap();
570
571        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len) }.unwrap();
572
573        assert_eq!(addr.family(), Some(AddressFamily::AF_INET));
574        assert!(addr.as_inet().is_some());
575        assert!(addr.as_inet6().is_none());
576        assert!(addr.as_unix().is_none());
577        assert!(addr.as_netlink().is_none());
578    }
579
580    /// Convert from a `sockaddr_un` to a `SockaddrStorage`.
581    #[test]
582    fn storage_from_unix_ptr() {
583        // a valid unix pathname sockaddr with a path of length 3
584        let mut addr: libc::sockaddr_un = unsafe { std::mem::zeroed() };
585        addr.sun_family = libc::AF_UNIX as u16;
586        addr.sun_path = [1; 108];
587        addr.sun_path[..4].copy_from_slice(&[1, 2, 3, 0]);
588
589        let ptr = std::ptr::from_ref(&addr) as *const MaybeUninit<u8>;
590        let len = std::mem::size_of_val(&addr).try_into().unwrap();
591
592        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len) }.unwrap();
593
594        assert_eq!(addr.family(), Some(AddressFamily::AF_UNIX));
595        assert!(addr.as_unix().is_some());
596        assert!(addr.as_inet().is_none());
597        assert!(addr.as_inet6().is_none());
598        assert!(addr.as_netlink().is_none());
599    }
600
601    /// Convert from a `sockaddr_nl` to a `SockaddrStorage`.
602    #[test]
603    fn storage_from_netlink_ptr() {
604        let mut addr: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
605        addr.nl_family = libc::AF_NETLINK as u16;
606        // nl_pid and nl_groups can be arbitrary for the tests
607        addr.nl_pid = 0x38deb915;
608        addr.nl_groups = 0xf229a8ea;
609
610        let ptr = &addr as *const _ as *const MaybeUninit<u8>;
611        let len = std::mem::size_of_val(&addr).try_into().unwrap();
612
613        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len) }.unwrap();
614
615        assert_eq!(addr.family(), Some(AddressFamily::AF_NETLINK));
616        assert!(addr.as_netlink().is_some());
617        assert!(addr.as_inet().is_none());
618        assert!(addr.as_inet6().is_none());
619        assert!(addr.as_unix().is_none());
620    }
621
622    /// Convert from a `sockaddr_in` to a `SockaddrStorage` to a `SockaddrIn`.
623    #[test]
624    fn inet_addr_from_libc() {
625        let mut addr_in: libc::sockaddr_in = unsafe { std::mem::zeroed() };
626        addr_in.sin_family = libc::AF_INET as u16;
627        addr_in.sin_port = 9000u16.to_be();
628        addr_in.sin_addr = libc::in_addr {
629            s_addr: libc::INADDR_LOOPBACK.to_be(),
630        };
631
632        let ptr = std::ptr::from_ref(&addr_in) as *const MaybeUninit<u8>;
633        let len = std::mem::size_of_val(&addr_in).try_into().unwrap();
634
635        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len) }.unwrap();
636        let addr = addr.as_inet().unwrap();
637
638        assert_eq!(addr.port(), u16::from_be(addr_in.sin_port));
639        assert_eq!(
640            addr.ip(),
641            Ipv4Addr::from(u32::from_be(addr_in.sin_addr.s_addr)),
642        );
643    }
644
645    /// Convert from a `SockaddrIn` to a `SockaddrStorage` to a `sockaddr_in`.
646    #[test]
647    fn inet_addr_to_libc() {
648        let addr_original = nix::sys::socket::SockaddrIn::new(127, 0, 0, 1, 9000);
649        let addr = SockaddrStorage::from_inet(&addr_original);
650
651        let (ptr, len) = addr.as_ptr();
652        let ptr = ptr as *const libc::sockaddr_in;
653        assert_eq!(len as usize, std::mem::size_of::<libc::sockaddr_in>());
654
655        let addr = unsafe { ptr.as_ref() }.unwrap();
656
657        assert_eq!(addr.sin_family, libc::AF_INET as u16);
658        assert_eq!(u16::from_be(addr.sin_port), addr_original.port());
659        assert_eq!(
660            Ipv4Addr::from(u32::from_be(addr.sin_addr.s_addr)),
661            addr_original.ip(),
662        );
663    }
664
665    /// Convert from a `sockaddr_nl` to a `SockaddrStorage` to a `NetlinkAddr`.
666    #[test]
667    fn netlink_addr_from_libc() {
668        let mut addr_nl: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
669        addr_nl.nl_family = libc::AF_NETLINK as u16;
670        // nl_pid and nl_groups can be arbitrary for the test
671        addr_nl.nl_pid = 0x38deb915;
672        addr_nl.nl_groups = 0xf229a8ea;
673
674        let ptr = &addr_nl as *const _ as *const MaybeUninit<u8>;
675        let len = std::mem::size_of_val(&addr_nl).try_into().unwrap();
676
677        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len) }.unwrap();
678        let addr = addr.as_netlink().unwrap();
679
680        assert_eq!(addr.pid(), u32::from_le(addr_nl.nl_pid));
681        assert_eq!(addr.groups(), u32::from_le(addr_nl.nl_groups));
682    }
683
684    /// Convert from a `NetlinkAddr` to a `SockaddrStorage` to a `sockaddr_nl`.
685    #[test]
686    fn netlink_addr_to_libc() {
687        // nl_pid and nl_groups can be arbitrary for the test
688        let addr_original = nix::sys::socket::NetlinkAddr::new(0x38deb915, 0xf229a8ea);
689        let addr = SockaddrStorage::from_netlink(&addr_original);
690
691        let (ptr, len) = addr.as_ptr();
692        let ptr = ptr as *const libc::sockaddr_nl;
693        assert_eq!(len as usize, std::mem::size_of::<libc::sockaddr_nl>());
694
695        let addr = unsafe { ptr.as_ref() }.unwrap();
696
697        assert_eq!(addr.nl_family, libc::AF_NETLINK as u16);
698        assert_eq!(u32::from_le(addr.nl_pid), addr_original.pid());
699        assert_eq!(u32::from_le(addr.nl_groups), addr_original.groups());
700    }
701
702    /// Convert from a pathname `sockaddr_un` to a `SockaddrStorage` to a `SockaddrUnix`.
703    #[test]
704    fn unix_addr_from_libc_to_path() {
705        let pathname = [1, 2, 3, 0];
706        let pathname_cstr = CStr::from_bytes_with_nul(i8_to_u8_slice(&pathname)).unwrap();
707
708        // a valid unix pathname sockaddr with a path of length 3
709        let mut addr: libc::sockaddr_un = unsafe { std::mem::zeroed() };
710        addr.sun_family = libc::AF_UNIX as u16;
711        addr.sun_path = [1; 108];
712        addr.sun_path[..pathname.len()].copy_from_slice(&pathname);
713
714        let ptr = std::ptr::from_ref(&addr) as *const MaybeUninit<u8>;
715        let len_useful_info = memoffset::offset_of!(libc::sockaddr_un, sun_path) + pathname.len();
716        let len_useful_info = len_useful_info.try_into().unwrap();
717        let len_struct = std::mem::size_of_val(&addr).try_into().unwrap();
718
719        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len_useful_info) }.unwrap();
720
721        assert!(addr.as_inet().is_none());
722        assert!(addr.as_inet6().is_none());
723
724        let addr = addr.as_unix().unwrap();
725
726        assert!(addr.as_abstract().is_none());
727        assert!(!addr.is_unnamed());
728
729        assert_eq!(addr.as_path().unwrap(), pathname_cstr);
730
731        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len_struct) }.unwrap();
732
733        assert!(addr.as_inet().is_none());
734        assert!(addr.as_inet6().is_none());
735
736        let addr = addr.as_unix().unwrap();
737
738        assert!(addr.as_abstract().is_none());
739        assert!(!addr.is_unnamed());
740
741        assert_eq!(addr.as_path().unwrap(), pathname_cstr);
742    }
743
744    /// Convert from a pathname `SockaddrUnix` to a `SockaddrStorage` to a `sockaddr_un`.
745    #[test]
746    fn unix_addr_from_path_to_libc() {
747        let pathname = [1, 2, 3, 0];
748        let pathname_cstr = CStr::from_bytes_with_nul(i8_to_u8_slice(&pathname)).unwrap();
749
750        let addr = SockaddrUnix::new_path(pathname_cstr).unwrap();
751        let addr = SockaddrStorage::from_unix(&addr.as_ref());
752
753        let (ptr, len) = addr.as_ptr();
754        let ptr = ptr as *const libc::sockaddr_un;
755        assert_eq!(len as usize, 2 + pathname.len());
756
757        let addr = unsafe { ptr.as_ref() }.unwrap();
758        let path_len = len as usize - memoffset::offset_of!(libc::sockaddr_un, sun_path);
759
760        assert_eq!(addr.sun_family, libc::AF_UNIX as u16);
761        assert_eq!(addr.sun_path[..path_len], pathname);
762    }
763
764    /// Convert from a abstract-named `sockaddr_un` to a `SockaddrStorage` to a `SockaddrUnix`.
765    #[test]
766    fn unix_addr_from_libc_to_abstract() {
767        let name = [1, 2, 3, 0, 5, 6];
768
769        // a valid unix abstract sockaddr
770        let mut addr: libc::sockaddr_un = unsafe { std::mem::zeroed() };
771        addr.sun_family = libc::AF_UNIX as u16;
772        addr.sun_path = [1; 108];
773        addr.sun_path[0] = 0;
774        addr.sun_path[1..][..name.len()].copy_from_slice(u8_to_i8_slice(&name));
775
776        let ptr = std::ptr::from_ref(&addr) as *const MaybeUninit<u8>;
777
778        // the correct sockaddr length for this abstract unix socket address
779        let len_real = memoffset::offset_of!(libc::sockaddr_un, sun_path) + 1 + name.len();
780        let len_real = len_real.try_into().unwrap();
781
782        // an incorrect sockaddr length (will result in the wrong abstract address name)
783        let len_struct = std::mem::size_of_val(&addr).try_into().unwrap();
784
785        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len_real) }.unwrap();
786
787        assert!(addr.as_inet().is_none());
788        assert!(addr.as_inet6().is_none());
789
790        let addr = addr.as_unix().unwrap();
791
792        assert!(addr.as_path().is_none());
793        assert!(!addr.is_unnamed());
794
795        assert_eq!(addr.as_abstract().unwrap(), &name);
796
797        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len_struct) }.unwrap();
798
799        assert!(addr.as_inet().is_none());
800        assert!(addr.as_inet6().is_none());
801
802        let addr = addr.as_unix().unwrap();
803
804        assert!(addr.as_path().is_none());
805        assert!(!addr.is_unnamed());
806
807        assert_eq!(addr.as_abstract().unwrap().len(), 107);
808    }
809
810    /// Convert from an abstract-named `SockaddrUnix` to a `SockaddrStorage` to a `sockaddr_un`.
811    #[test]
812    fn unix_addr_from_abstract_to_libc() {
813        let name = [1, 2, 3, 0, 5, 6];
814
815        let addr = SockaddrUnix::new_abstract(&name).unwrap();
816        let addr = SockaddrStorage::from_unix(&addr.as_ref());
817
818        let (ptr, len) = addr.as_ptr();
819        let ptr = ptr as *const libc::sockaddr_un;
820        assert_eq!(len as usize, 2 + 1 + name.len());
821
822        let addr = unsafe { ptr.as_ref() }.unwrap();
823        let path_len = len as usize - memoffset::offset_of!(libc::sockaddr_un, sun_path);
824
825        assert_eq!(addr.sun_family, libc::AF_UNIX as u16);
826        assert_eq!(addr.sun_path[0], 0);
827        assert_eq!(&addr.sun_path[1..path_len], u8_to_i8_slice(&name));
828    }
829
830    /// Convert from an unnamed `sockaddr_un` to a `SockaddrStorage` to a `SockaddrUnix`.
831    #[test]
832    fn unix_addr_from_libc_to_unnamed() {
833        // a valid unix abstract sockaddr
834        let mut addr: libc::sockaddr_un = unsafe { std::mem::zeroed() };
835        addr.sun_family = libc::AF_UNIX as u16;
836        addr.sun_path = [1; 108];
837
838        let ptr = std::ptr::from_ref(&addr) as *const MaybeUninit<u8>;
839
840        // the correct sockaddr length for this unnamed unix socket address
841        let len_real = memoffset::offset_of!(libc::sockaddr_un, sun_path);
842        let len_real = len_real.try_into().unwrap();
843
844        // an incorrect sockaddr length (will result in the wrong address)
845        let len_struct = std::mem::size_of_val(&addr).try_into().unwrap();
846
847        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len_real) }.unwrap();
848
849        assert!(addr.as_inet().is_none());
850        assert!(addr.as_inet6().is_none());
851
852        let addr = addr.as_unix().unwrap();
853
854        assert!(addr.is_unnamed());
855        assert!(addr.as_path().is_none());
856        assert!(addr.as_abstract().is_none());
857
858        let addr = unsafe { SockaddrStorage::from_ptr(ptr, len_struct) }.unwrap();
859
860        assert!(addr.as_inet().is_none());
861        assert!(addr.as_inet6().is_none());
862
863        let addr = addr.as_unix().unwrap();
864
865        // the sun_path value isn't a valid pathname or abstract address, and it's not unnamed
866        // because of the length (len_struct > 2)
867        assert!(!addr.is_unnamed());
868        assert!(addr.as_abstract().is_none());
869        assert!(addr.as_path().is_none());
870    }
871
872    /// Convert from an unnamed `SockaddrUnix` to a `SockaddrStorage` to a `sockaddr_un`.
873    #[test]
874    fn unix_addr_from_unnamed_to_libc() {
875        let addr = SockaddrUnix::new_unnamed();
876        let addr = SockaddrStorage::from_unix(&addr.as_ref());
877
878        let (ptr, len) = addr.as_ptr();
879        let ptr = ptr as *const libc::sockaddr_un;
880        assert_eq!(len, 2);
881
882        let addr = unsafe { ptr.as_ref() }.unwrap();
883
884        assert_eq!(addr.sun_family, libc::AF_UNIX as u16);
885    }
886}