1//! Socket address utilities.
2//!
3//! # Safety
4//!
5//! This file uses `CStr::from_bytes_with_nul_unchecked` on a string it knows
6//! to be NUL-terminated.
7#![allow(unsafe_code)]
8
9use crate::backend::c;
10use crate::ffi::CStr;
11use crate::{io, path};
12use core::cmp::Ordering;
13use core::hash::{Hash, Hasher};
14use core::{fmt, slice};
15
16/// `struct sockaddr_un`
17#[derive(Clone)]
18#[doc(alias = "sockaddr_un")]
19pub struct SocketAddrUnix {
20 pub(crate) unix: c::sockaddr_un,
21 len: c::socklen_t,
22}
23
24impl SocketAddrUnix {
25 /// Construct a new Unix-domain address from a filesystem path.
26 #[inline]
27 pub fn new<P: path::Arg>(path: P) -> io::Result<Self> {
28 path.into_with_c_str(Self::_new)
29 }
30
31 #[inline]
32 fn _new(path: &CStr) -> io::Result<Self> {
33 let mut unix = Self::init();
34 let bytes = path.to_bytes_with_nul();
35 if bytes.len() > unix.sun_path.len() {
36 return Err(io::Errno::NAMETOOLONG);
37 }
38 for (i, b) in bytes.iter().enumerate() {
39 unix.sun_path[i] = *b as _;
40 }
41 let len = offsetof_sun_path() + bytes.len();
42 let len = len.try_into().unwrap();
43 Ok(Self { unix, len })
44 }
45
46 /// Construct a new abstract Unix-domain address from a byte slice.
47 #[inline]
48 pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
49 let mut unix = Self::init();
50 let id = &mut unix.sun_path[1..];
51
52 // SAFETY: Convert `&mut [c_char]` to `&mut [u8]`.
53 let id = unsafe { slice::from_raw_parts_mut(id.as_mut_ptr().cast::<u8>(), id.len()) };
54
55 if let Some(id) = id.get_mut(..name.len()) {
56 id.copy_from_slice(name);
57 let len = offsetof_sun_path() + 1 + name.len();
58 let len = len.try_into().unwrap();
59 Ok(Self { unix, len })
60 } else {
61 Err(io::Errno::NAMETOOLONG)
62 }
63 }
64
65 const fn init() -> c::sockaddr_un {
66 c::sockaddr_un {
67 sun_family: c::AF_UNIX as _,
68 sun_path: [0; 108],
69 }
70 }
71
72 /// For a filesystem path address, return the path.
73 #[inline]
74 pub fn path(&self) -> Option<&CStr> {
75 let len = self.len();
76 if len != 0 && self.unix.sun_path[0] as u8 != b'\0' {
77 let end = len as usize - offsetof_sun_path();
78 let bytes = &self.unix.sun_path[..end];
79
80 // SAFETY: Convert `&[c_char]` to `&[u8]`.
81 let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<u8>(), bytes.len()) };
82
83 // SAFETY: `from_bytes_with_nul_unchecked` since the string is
84 // NUL-terminated.
85 unsafe { Some(CStr::from_bytes_with_nul_unchecked(bytes)) }
86 } else {
87 None
88 }
89 }
90
91 /// For an abstract address, return the identifier.
92 #[inline]
93 pub fn abstract_name(&self) -> Option<&[u8]> {
94 let len = self.len();
95 if len != 0 && self.unix.sun_path[0] as u8 == b'\0' {
96 let end = len as usize - offsetof_sun_path();
97 let bytes = &self.unix.sun_path[1..end];
98
99 // SAFETY: Convert `&[c_char]` to `&[u8]`.
100 let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<u8>(), bytes.len()) };
101
102 Some(bytes)
103 } else {
104 None
105 }
106 }
107
108 #[inline]
109 pub(crate) fn addr_len(&self) -> c::socklen_t {
110 self.len
111 }
112
113 #[inline]
114 pub(crate) fn len(&self) -> usize {
115 self.addr_len() as usize
116 }
117}
118
119impl PartialEq for SocketAddrUnix {
120 #[inline]
121 fn eq(&self, other: &Self) -> bool {
122 let self_len: usize = self.len() - offsetof_sun_path();
123 let other_len: usize = other.len() - offsetof_sun_path();
124 self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len])
125 }
126}
127
128impl Eq for SocketAddrUnix {}
129
130impl PartialOrd for SocketAddrUnix {
131 #[inline]
132 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
133 Some(self.cmp(other))
134 }
135}
136
137impl Ord for SocketAddrUnix {
138 #[inline]
139 fn cmp(&self, other: &Self) -> Ordering {
140 let self_len: usize = self.len() - offsetof_sun_path();
141 let other_len: usize = other.len() - offsetof_sun_path();
142 self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len])
143 }
144}
145
146impl Hash for SocketAddrUnix {
147 #[inline]
148 fn hash<H: Hasher>(&self, state: &mut H) {
149 let self_len: usize = self.len() - offsetof_sun_path();
150 self.unix.sun_path[..self_len].hash(state)
151 }
152}
153
154impl fmt::Debug for SocketAddrUnix {
155 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
156 if let Some(path: &CStr) = self.path() {
157 path.fmt(fmt)
158 } else if let Some(name: &[u8]) = self.abstract_name() {
159 name.fmt(fmt)
160 } else {
161 "(unnamed)".fmt(fmt)
162 }
163 }
164}
165
166/// `struct sockaddr_storage` as a raw struct.
167pub type SocketAddrStorage = c::sockaddr;
168
169/// Return the offset of the `sun_path` field of `sockaddr_un`.
170#[inline]
171pub(crate) fn offsetof_sun_path() -> usize {
172 let z: sockaddr_un = c::sockaddr_un {
173 sun_family: 0_u16,
174 sun_path: [0; 108],
175 };
176 (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize)
177}
178