1 //! Socket address utilities.
2 
3 use crate::backend::c;
4 #[cfg(unix)]
5 use {
6     crate::ffi::CStr,
7     crate::io,
8     crate::path,
9     core::cmp::Ordering,
10     core::fmt,
11     core::hash::{Hash, Hasher},
12     core::slice,
13 };
14 
15 /// `struct sockaddr_un`
16 #[cfg(unix)]
17 #[derive(Clone)]
18 #[doc(alias = "sockaddr_un")]
19 pub struct SocketAddrUnix {
20     pub(crate) unix: c::sockaddr_un,
21     #[cfg(not(any(bsd, target_os = "haiku")))]
22     len: c::socklen_t,
23 }
24 
25 #[cfg(unix)]
26 impl SocketAddrUnix {
27     /// Construct a new Unix-domain address from a filesystem path.
28     #[inline]
new<P: path::Arg>(path: P) -> io::Result<Self>29     pub fn new<P: path::Arg>(path: P) -> io::Result<Self> {
30         path.into_with_c_str(Self::_new)
31     }
32 
33     #[inline]
_new(path: &CStr) -> io::Result<Self>34     fn _new(path: &CStr) -> io::Result<Self> {
35         let mut unix = Self::init();
36         let bytes = path.to_bytes_with_nul();
37         if bytes.len() > unix.sun_path.len() {
38             return Err(io::Errno::NAMETOOLONG);
39         }
40         for (i, b) in bytes.iter().enumerate() {
41             unix.sun_path[i] = *b as c::c_char;
42         }
43 
44         #[cfg(any(bsd, target_os = "haiku"))]
45         {
46             unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap();
47         }
48 
49         Ok(Self {
50             unix,
51             #[cfg(not(any(bsd, target_os = "haiku")))]
52             len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(),
53         })
54     }
55 
56     /// Construct a new abstract Unix-domain address from a byte slice.
57     #[cfg(linux_kernel)]
58     #[inline]
new_abstract_name(name: &[u8]) -> io::Result<Self>59     pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
60         let mut unix = Self::init();
61         if 1 + name.len() > unix.sun_path.len() {
62             return Err(io::Errno::NAMETOOLONG);
63         }
64         unix.sun_path[0] = 0;
65         for (i, b) in name.iter().enumerate() {
66             unix.sun_path[1 + i] = *b as c::c_char;
67         }
68         let len = offsetof_sun_path() + 1 + name.len();
69         let len = len.try_into().unwrap();
70         Ok(Self {
71             unix,
72             #[cfg(not(any(bsd, target_os = "haiku")))]
73             len,
74         })
75     }
76 
init() -> c::sockaddr_un77     fn init() -> c::sockaddr_un {
78         c::sockaddr_un {
79             #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))]
80             sun_len: 0,
81             #[cfg(target_os = "vita")]
82             ss_len: 0,
83             sun_family: c::AF_UNIX as _,
84             #[cfg(any(bsd, target_os = "nto"))]
85             sun_path: [0; 104],
86             #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto")))]
87             sun_path: [0; 108],
88             #[cfg(target_os = "haiku")]
89             sun_path: [0; 126],
90             #[cfg(target_os = "aix")]
91             sun_path: [0; 1023],
92         }
93     }
94 
95     /// For a filesystem path address, return the path.
96     #[inline]
path(&self) -> Option<&CStr>97     pub fn path(&self) -> Option<&CStr> {
98         let len = self.len();
99         if len != 0 && self.unix.sun_path[0] != 0 {
100             let end = len as usize - offsetof_sun_path();
101             let bytes = &self.unix.sun_path[..end];
102             // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
103             // And `from_bytes_with_nul_unchecked` since the string is
104             // NUL-terminated.
105             unsafe {
106                 Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
107                     bytes.as_ptr().cast(),
108                     bytes.len(),
109                 )))
110             }
111         } else {
112             None
113         }
114     }
115 
116     /// For an abstract address, return the identifier.
117     #[cfg(linux_kernel)]
118     #[inline]
abstract_name(&self) -> Option<&[u8]>119     pub fn abstract_name(&self) -> Option<&[u8]> {
120         let len = self.len();
121         if len != 0 && self.unix.sun_path[0] == 0 {
122             let end = len as usize - offsetof_sun_path();
123             let bytes = &self.unix.sun_path[1..end];
124             // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
125             unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) }
126         } else {
127             None
128         }
129     }
130 
131     #[inline]
addr_len(&self) -> c::socklen_t132     pub(crate) fn addr_len(&self) -> c::socklen_t {
133         #[cfg(not(any(bsd, target_os = "haiku")))]
134         {
135             self.len
136         }
137         #[cfg(any(bsd, target_os = "haiku"))]
138         {
139             c::socklen_t::from(self.unix.sun_len)
140         }
141     }
142 
143     #[inline]
len(&self) -> usize144     pub(crate) fn len(&self) -> usize {
145         self.addr_len() as usize
146     }
147 }
148 
149 #[cfg(unix)]
150 impl PartialEq for SocketAddrUnix {
151     #[inline]
eq(&self, other: &Self) -> bool152     fn eq(&self, other: &Self) -> bool {
153         let self_len = self.len() - offsetof_sun_path();
154         let other_len = other.len() - offsetof_sun_path();
155         self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len])
156     }
157 }
158 
159 #[cfg(unix)]
160 impl Eq for SocketAddrUnix {}
161 
162 #[cfg(unix)]
163 impl PartialOrd for SocketAddrUnix {
164     #[inline]
partial_cmp(&self, other: &Self) -> Option<Ordering>165     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
166         Some(self.cmp(other))
167     }
168 }
169 
170 #[cfg(unix)]
171 impl Ord for SocketAddrUnix {
172     #[inline]
cmp(&self, other: &Self) -> Ordering173     fn cmp(&self, other: &Self) -> Ordering {
174         let self_len = self.len() - offsetof_sun_path();
175         let other_len = other.len() - offsetof_sun_path();
176         self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len])
177     }
178 }
179 
180 #[cfg(unix)]
181 impl Hash for SocketAddrUnix {
182     #[inline]
hash<H: Hasher>(&self, state: &mut H)183     fn hash<H: Hasher>(&self, state: &mut H) {
184         let self_len = self.len() - offsetof_sun_path();
185         self.unix.sun_path[..self_len].hash(state)
186     }
187 }
188 
189 #[cfg(unix)]
190 impl fmt::Debug for SocketAddrUnix {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result191     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
192         if let Some(path) = self.path() {
193             path.fmt(fmt)
194         } else {
195             #[cfg(linux_kernel)]
196             if let Some(name) = self.abstract_name() {
197                 return name.fmt(fmt);
198             }
199 
200             "(unnamed)".fmt(fmt)
201         }
202     }
203 }
204 
205 /// `struct sockaddr_storage` as a raw struct.
206 pub type SocketAddrStorage = c::sockaddr_storage;
207 
208 /// Return the offset of the `sun_path` field of `sockaddr_un`.
209 #[cfg(not(windows))]
210 #[inline]
offsetof_sun_path() -> usize211 pub(crate) fn offsetof_sun_path() -> usize {
212     let z = c::sockaddr_un {
213         #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))]
214         sun_len: 0_u8,
215         #[cfg(target_os = "vita")]
216         ss_len: 0,
217         #[cfg(any(
218             bsd,
219             target_os = "aix",
220             target_os = "espidf",
221             target_os = "haiku",
222             target_os = "nto",
223             target_os = "vita"
224         ))]
225         sun_family: 0_u8,
226         #[cfg(not(any(
227             bsd,
228             target_os = "aix",
229             target_os = "espidf",
230             target_os = "haiku",
231             target_os = "nto",
232             target_os = "vita"
233         )))]
234         sun_family: 0_u16,
235         #[cfg(any(bsd, target_os = "nto"))]
236         sun_path: [0; 104],
237         #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto")))]
238         sun_path: [0; 108],
239         #[cfg(target_os = "haiku")]
240         sun_path: [0; 126],
241         #[cfg(target_os = "aix")]
242         sun_path: [0; 1023],
243     };
244     (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize)
245 }
246