1//! IPv4, IPv6, and Socket addresses.
2
3use super::super::c;
4#[cfg(unix)]
5use crate::ffi::CStr;
6#[cfg(unix)]
7use crate::io;
8#[cfg(unix)]
9use crate::path;
10#[cfg(not(windows))]
11use core::convert::TryInto;
12#[cfg(unix)]
13use core::fmt;
14#[cfg(unix)]
15use core::slice;
16
17/// `struct sockaddr_un`
18#[cfg(unix)]
19#[derive(Clone)]
20#[doc(alias = "sockaddr_un")]
21pub struct SocketAddrUnix {
22    pub(crate) unix: c::sockaddr_un,
23    #[cfg(not(any(
24        target_os = "dragonfly",
25        target_os = "freebsd",
26        target_os = "ios",
27        target_os = "macos",
28        target_os = "netbsd",
29        target_os = "openbsd",
30    )))]
31    len: c::socklen_t,
32}
33
34#[cfg(unix)]
35impl SocketAddrUnix {
36    /// Construct a new Unix-domain address from a filesystem path.
37    #[inline]
38    pub fn new<P: path::Arg>(path: P) -> io::Result<Self> {
39        path.into_with_c_str(Self::_new)
40    }
41
42    #[inline]
43    fn _new(path: &CStr) -> io::Result<Self> {
44        let mut unix = Self::init();
45        let bytes = path.to_bytes_with_nul();
46        if bytes.len() > unix.sun_path.len() {
47            return Err(io::Errno::NAMETOOLONG);
48        }
49        for (i, b) in bytes.iter().enumerate() {
50            unix.sun_path[i] = *b as c::c_char;
51        }
52
53        #[cfg(any(
54            target_os = "dragonfly",
55            target_os = "freebsd",
56            target_os = "ios",
57            target_os = "macos",
58            target_os = "netbsd",
59            target_os = "openbsd",
60        ))]
61        {
62            unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap();
63        }
64
65        Ok(Self {
66            unix,
67            #[cfg(not(any(
68                target_os = "dragonfly",
69                target_os = "freebsd",
70                target_os = "ios",
71                target_os = "macos",
72                target_os = "netbsd",
73                target_os = "openbsd",
74            )))]
75            len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(),
76        })
77    }
78
79    /// Construct a new abstract Unix-domain address from a byte slice.
80    #[cfg(any(target_os = "android", target_os = "linux"))]
81    #[inline]
82    pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
83        let mut unix = Self::init();
84        if 1 + name.len() > unix.sun_path.len() {
85            return Err(io::Errno::NAMETOOLONG);
86        }
87        unix.sun_path[0] = b'\0' as c::c_char;
88        for (i, b) in name.iter().enumerate() {
89            unix.sun_path[1 + i] = *b as c::c_char;
90        }
91        let len = offsetof_sun_path() + 1 + name.len();
92        let len = len.try_into().unwrap();
93        Ok(Self {
94            unix,
95            #[cfg(not(any(
96                target_os = "dragonfly",
97                target_os = "freebsd",
98                target_os = "ios",
99                target_os = "macos",
100                target_os = "netbsd",
101                target_os = "openbsd",
102            )))]
103            len,
104        })
105    }
106
107    fn init() -> c::sockaddr_un {
108        c::sockaddr_un {
109            #[cfg(any(
110                target_os = "dragonfly",
111                target_os = "freebsd",
112                target_os = "haiku",
113                target_os = "ios",
114                target_os = "macos",
115                target_os = "netbsd",
116                target_os = "openbsd",
117            ))]
118            sun_len: 0,
119            sun_family: c::AF_UNIX as _,
120            #[cfg(any(
121                target_os = "dragonfly",
122                target_os = "freebsd",
123                target_os = "ios",
124                target_os = "macos",
125                target_os = "netbsd",
126                target_os = "openbsd",
127            ))]
128            sun_path: [0; 104],
129            #[cfg(not(any(
130                target_os = "dragonfly",
131                target_os = "freebsd",
132                target_os = "haiku",
133                target_os = "ios",
134                target_os = "macos",
135                target_os = "netbsd",
136                target_os = "openbsd",
137            )))]
138            sun_path: [0; 108],
139            #[cfg(target_os = "haiku")]
140            sun_path: [0; 126],
141        }
142    }
143
144    /// For a filesystem path address, return the path.
145    #[inline]
146    pub fn path(&self) -> Option<&CStr> {
147        let len = self.len();
148        if len != 0 && self.unix.sun_path[0] != b'\0' as c::c_char {
149            let end = len as usize - offsetof_sun_path();
150            let bytes = &self.unix.sun_path[..end];
151            // Safety: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. And
152            // `from_bytes_with_nul_unchecked` since the string is NUL-terminated.
153            unsafe {
154                Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
155                    bytes.as_ptr().cast(),
156                    bytes.len(),
157                )))
158            }
159        } else {
160            None
161        }
162    }
163
164    /// For an abstract address, return the identifier.
165    #[cfg(any(target_os = "android", target_os = "linux"))]
166    #[inline]
167    pub fn abstract_name(&self) -> Option<&[u8]> {
168        let len = self.len();
169        if len != 0 && self.unix.sun_path[0] == b'\0' as c::c_char {
170            let end = len as usize - offsetof_sun_path();
171            let bytes = &self.unix.sun_path[1..end];
172            // Safety: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
173            unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) }
174        } else {
175            None
176        }
177    }
178
179    #[inline]
180    pub(crate) fn addr_len(&self) -> c::socklen_t {
181        #[cfg(not(any(
182            target_os = "dragonfly",
183            target_os = "freebsd",
184            target_os = "ios",
185            target_os = "macos",
186            target_os = "netbsd",
187            target_os = "openbsd",
188        )))]
189        {
190            self.len
191        }
192        #[cfg(any(
193            target_os = "dragonfly",
194            target_os = "freebsd",
195            target_os = "ios",
196            target_os = "macos",
197            target_os = "netbsd",
198            target_os = "openbsd",
199        ))]
200        {
201            c::socklen_t::from(self.unix.sun_len)
202        }
203    }
204
205    #[inline]
206    pub(crate) fn len(&self) -> usize {
207        self.addr_len() as usize
208    }
209}
210
211#[cfg(unix)]
212impl PartialEq for SocketAddrUnix {
213    #[inline]
214    fn eq(&self, other: &Self) -> bool {
215        let self_len = self.len() - offsetof_sun_path();
216        let other_len = other.len() - offsetof_sun_path();
217        self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len])
218    }
219}
220
221#[cfg(unix)]
222impl Eq for SocketAddrUnix {}
223
224#[cfg(unix)]
225impl PartialOrd for SocketAddrUnix {
226    #[inline]
227    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
228        let self_len = self.len() - offsetof_sun_path();
229        let other_len = other.len() - offsetof_sun_path();
230        self.unix.sun_path[..self_len].partial_cmp(&other.unix.sun_path[..other_len])
231    }
232}
233
234#[cfg(unix)]
235impl Ord for SocketAddrUnix {
236    #[inline]
237    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
238        let self_len = self.len() - offsetof_sun_path();
239        let other_len = other.len() - offsetof_sun_path();
240        self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len])
241    }
242}
243
244#[cfg(unix)]
245impl core::hash::Hash for SocketAddrUnix {
246    #[inline]
247    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
248        let self_len = self.len() - offsetof_sun_path();
249        self.unix.sun_path[..self_len].hash(state)
250    }
251}
252
253#[cfg(unix)]
254impl fmt::Debug for SocketAddrUnix {
255    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
256        if let Some(path) = self.path() {
257            path.fmt(fmt)
258        } else {
259            #[cfg(any(target_os = "android", target_os = "linux"))]
260            if let Some(name) = self.abstract_name() {
261                return name.fmt(fmt);
262            }
263
264            "(unnamed)".fmt(fmt)
265        }
266    }
267}
268
269/// `struct sockaddr_storage` as a raw struct.
270pub type SocketAddrStorage = c::sockaddr_storage;
271
272/// Return the offset of the `sun_path` field of `sockaddr_un`.
273#[cfg(not(windows))]
274#[inline]
275pub(crate) fn offsetof_sun_path() -> usize {
276    let z = c::sockaddr_un {
277        #[cfg(any(
278            target_os = "dragonfly",
279            target_os = "freebsd",
280            target_os = "haiku",
281            target_os = "ios",
282            target_os = "macos",
283            target_os = "netbsd",
284            target_os = "openbsd",
285        ))]
286        sun_len: 0_u8,
287        #[cfg(any(
288            target_os = "dragonfly",
289            target_os = "freebsd",
290            target_os = "haiku",
291            target_os = "ios",
292            target_os = "macos",
293            target_os = "netbsd",
294            target_os = "openbsd",
295        ))]
296        sun_family: 0_u8,
297        #[cfg(not(any(
298            target_os = "dragonfly",
299            target_os = "freebsd",
300            target_os = "haiku",
301            target_os = "ios",
302            target_os = "macos",
303            target_os = "netbsd",
304            target_os = "openbsd",
305        )))]
306        sun_family: 0_u16,
307        #[cfg(any(
308            target_os = "dragonfly",
309            target_os = "freebsd",
310            target_os = "ios",
311            target_os = "macos",
312            target_os = "netbsd",
313            target_os = "openbsd",
314        ))]
315        sun_path: [0; 104],
316        #[cfg(not(any(
317            target_os = "dragonfly",
318            target_os = "freebsd",
319            target_os = "haiku",
320            target_os = "ios",
321            target_os = "macos",
322            target_os = "netbsd",
323            target_os = "openbsd",
324        )))]
325        sun_path: [0; 108],
326        #[cfg(target_os = "haiku")]
327        sun_path: [0; 126],
328    };
329    (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize)
330}
331