1use std::ffi::{OsStr, OsString};
4use std::hash::{Hash, Hasher};
5use std::os::unix::ffi::OsStrExt;
6use std::path::Path;
7use std::{fmt, fs, io};
8
9wrapper_lite::general_wrapper! {
10 #[wrapper_impl(Deref)]
11 #[derive(Clone)]
12 pub struct SocketAddr(std::os::unix::net::SocketAddr);
16}
17
18impl SocketAddr {
19 pub fn new<S: AsRef<OsStr> + ?Sized>(addr: &S) -> io::Result<Self> {
58 let addr = addr.as_ref();
59
60 match addr.as_bytes() {
61 #[cfg(any(target_os = "android", target_os = "linux"))]
62 [b'@', rest @ ..] | [b'\0', rest @ ..] => Self::new_abstract(rest),
63 #[cfg(not(any(target_os = "android", target_os = "linux")))]
64 [b'@', ..] | [b'\0', ..] => Err(io::Error::new(
65 io::ErrorKind::Unsupported,
66 "abstract unix socket address is not supported",
67 )),
68 _ => Self::new_pathname(addr),
69 }
70 }
71
72 pub fn new_strict<S: AsRef<OsStr> + ?Sized>(addr: &S) -> io::Result<Self> {
74 let addr = addr.as_ref();
75
76 match addr.as_bytes() {
77 #[cfg(any(target_os = "android", target_os = "linux"))]
78 [b'@', rest @ ..] | [b'\0', rest @ ..] => Self::new_abstract_strict(rest),
79 #[cfg(not(any(target_os = "android", target_os = "linux")))]
80 [b'@', ..] | [b'\0', ..] => Err(io::Error::new(
81 io::ErrorKind::Unsupported,
82 "abstract unix socket address is not supported",
83 )),
84 _ => Self::new_pathname(addr),
85 }
86 }
87
88 #[cfg(any(target_os = "android", target_os = "linux"))]
89 pub fn new_abstract(bytes: &[u8]) -> io::Result<Self> {
105 use std::os::linux::net::SocketAddrExt;
106
107 std::os::unix::net::SocketAddr::from_abstract_name(bytes).map(Self::const_from)
108 }
109
110 #[cfg(any(target_os = "android", target_os = "linux"))]
111 pub fn new_abstract_strict(bytes: &[u8]) -> io::Result<Self> {
113 use std::os::linux::net::SocketAddrExt;
114
115 if bytes.contains(&b'\0') {
116 return Err(io::Error::new(
117 io::ErrorKind::InvalidInput,
118 "parse abstract socket name in strict mode: reject NULL bytes",
119 ));
120 }
121
122 std::os::unix::net::SocketAddr::from_abstract_name(bytes).map(Self::const_from)
123 }
124
125 pub fn new_pathname<P: AsRef<Path>>(pathname: P) -> io::Result<Self> {
133 let _ = fs::remove_file(pathname.as_ref());
134
135 std::os::unix::net::SocketAddr::from_pathname(pathname).map(Self::const_from)
136 }
137
138 #[allow(clippy::missing_panics_doc)]
139 pub fn new_unnamed() -> Self {
141 std::os::unix::net::SocketAddr::from_pathname("")
143 .map(Self::const_from)
144 .unwrap()
145 }
146
147 #[inline]
148 pub fn from_bytes(bytes: &[u8]) -> io::Result<Self> {
154 Self::new(OsStr::from_bytes(bytes))
155 }
156
157 pub fn to_os_string(&self) -> OsString {
165 self.to_os_string_impl("", "\0")
166 }
167
168 pub fn to_string_lossy(&self) -> String {
177 self.to_os_string_impl("", "@")
178 .to_string_lossy()
179 .into_owned()
180 }
181
182 pub(crate) fn to_os_string_impl(&self, prefix: &str, abstract_identifier: &str) -> OsString {
183 let mut os_string = OsString::from(prefix);
184
185 if let Some(pathname) = self.as_pathname() {
186 os_string.push(pathname);
188
189 return os_string;
190 }
191
192 #[cfg(any(target_os = "android", target_os = "linux"))]
193 {
194 use std::os::linux::net::SocketAddrExt;
195
196 if let Some(abstract_name) = self.as_abstract_name() {
197 os_string.push(abstract_identifier);
198 os_string.push(OsStr::from_bytes(abstract_name));
199
200 return os_string;
201 }
202 }
203
204 os_string
206 }
207}
208
209impl fmt::Debug for SocketAddr {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 self.as_inner().fmt(f)
212 }
213}
214
215impl PartialEq for SocketAddr {
216 fn eq(&self, other: &Self) -> bool {
217 if let Some((l, r)) = self.as_pathname().zip(other.as_pathname()) {
218 return l == r;
219 }
220
221 #[cfg(any(target_os = "android", target_os = "linux"))]
222 {
223 use std::os::linux::net::SocketAddrExt;
224
225 if let Some((l, r)) = self.as_abstract_name().zip(other.as_abstract_name()) {
226 return l == r;
227 }
228 }
229
230 if self.is_unnamed() && other.is_unnamed() {
231 return true;
232 }
233
234 false
235 }
236}
237
238impl Eq for SocketAddr {}
239
240impl Hash for SocketAddr {
241 fn hash<H: Hasher>(&self, state: &mut H) {
242 if let Some(pathname) = self.as_pathname() {
243 pathname.hash(state);
244
245 return;
246 }
247
248 #[cfg(any(target_os = "android", target_os = "linux"))]
249 {
250 use std::os::linux::net::SocketAddrExt;
251
252 if let Some(abstract_name) = self.as_abstract_name() {
253 b'\0'.hash(state);
254 abstract_name.hash(state);
255
256 return;
257 }
258 }
259
260 debug_assert!(self.is_unnamed(), "SocketAddr is not unnamed one");
261
262 b"(unnamed)\0".hash(state);
265 }
266}
267
268#[cfg(feature = "feat-serde")]
269impl serde::Serialize for SocketAddr {
270 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
271 where
272 S: serde::Serializer,
273 {
274 serializer.serialize_str(&self.to_string_lossy())
275 }
276}
277
278#[cfg(feature = "feat-serde")]
279impl<'de> serde::Deserialize<'de> for SocketAddr {
280 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
281 where
282 D: serde::Deserializer<'de>,
283 {
284 Self::new(<&str>::deserialize(deserializer)?).map_err(serde::de::Error::custom)
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use core::hash::{Hash, Hasher};
291 use std::hash::DefaultHasher;
292
293 use super::*;
294
295 #[test]
296 fn test_unnamed() {
297 const TEST_CASE: &str = "";
298
299 let addr = SocketAddr::new(TEST_CASE).unwrap();
300
301 assert!(addr.as_ref().is_unnamed());
302 }
303
304 #[test]
305 fn test_pathname() {
306 const TEST_CASE: &str = "/tmp/test_pathname.socket";
307
308 let addr = SocketAddr::new(TEST_CASE).unwrap();
309
310 assert_eq!(addr.to_os_string().to_str().unwrap(), TEST_CASE);
311 assert_eq!(addr.to_string_lossy(), TEST_CASE);
312 assert_eq!(addr.as_pathname().unwrap().to_str().unwrap(), TEST_CASE);
313 }
314
315 #[test]
316 #[cfg(any(target_os = "android", target_os = "linux"))]
317 fn test_abstract() {
318 use std::os::linux::net::SocketAddrExt;
319
320 const TEST_CASE_1: &[u8] = b"@abstract.socket";
321 const TEST_CASE_2: &[u8] = b"\0abstract.socket";
322 const TEST_CASE_3: &[u8] = b"@";
323 const TEST_CASE_4: &[u8] = b"\0";
324
325 assert_eq!(
326 SocketAddr::new(OsStr::from_bytes(TEST_CASE_1))
327 .unwrap()
328 .as_abstract_name()
329 .unwrap(),
330 &TEST_CASE_1[1..]
331 );
332
333 assert_eq!(
334 SocketAddr::new(OsStr::from_bytes(TEST_CASE_2))
335 .unwrap()
336 .as_abstract_name()
337 .unwrap(),
338 &TEST_CASE_2[1..]
339 );
340
341 assert_eq!(
342 SocketAddr::new(OsStr::from_bytes(TEST_CASE_3))
343 .unwrap()
344 .as_abstract_name()
345 .unwrap(),
346 &TEST_CASE_3[1..]
347 );
348
349 assert_eq!(
350 SocketAddr::new(OsStr::from_bytes(TEST_CASE_4))
351 .unwrap()
352 .as_abstract_name()
353 .unwrap(),
354 &TEST_CASE_4[1..]
355 );
356 }
357
358 #[test]
359 #[should_panic]
360 fn test_pathname_with_null_byte() {
361 let _addr = SocketAddr::new_pathname("(unamed)\0").unwrap();
362 }
363
364 #[test]
365 fn test_partial_eq_hash() {
366 let addr_pathname_1 = SocketAddr::new("/tmp/test_pathname_1.socket").unwrap();
367 let addr_pathname_2 = SocketAddr::new("/tmp/test_pathname_2.socket").unwrap();
368 let addr_unnamed = SocketAddr::new_unnamed();
369
370 assert_eq!(addr_pathname_1, addr_pathname_1);
371 assert_ne!(addr_pathname_1, addr_pathname_2);
372 assert_ne!(addr_pathname_2, addr_pathname_1);
373
374 assert_eq!(addr_unnamed, addr_unnamed);
375 assert_ne!(addr_pathname_1, addr_unnamed);
376 assert_ne!(addr_unnamed, addr_pathname_1);
377 assert_ne!(addr_pathname_2, addr_unnamed);
378 assert_ne!(addr_unnamed, addr_pathname_2);
379
380 #[cfg(any(target_os = "android", target_os = "linux"))]
381 {
382 let addr_abstract_1 = SocketAddr::new_abstract(b"/tmp/test_pathname_1.socket").unwrap();
383 let addr_abstract_2 = SocketAddr::new_abstract(b"/tmp/test_pathname_2.socket").unwrap();
384 let addr_abstract_empty = SocketAddr::new_abstract(&[]).unwrap();
385 let addr_abstract_unnamed_hash = SocketAddr::new_abstract(b"(unamed)\0").unwrap();
386
387 assert_eq!(addr_abstract_1, addr_abstract_1);
388 assert_ne!(addr_abstract_1, addr_abstract_2);
389 assert_ne!(addr_abstract_2, addr_abstract_1);
390
391 assert_ne!(addr_unnamed, addr_abstract_empty);
393
394 assert_ne!(addr_pathname_1, addr_abstract_1);
396
397 let addr_unnamed_hash = {
400 let mut state = DefaultHasher::new();
401 addr_unnamed.hash(&mut state);
402 state.finish()
403 };
404 let addr_abstract_unnamed_hash = {
405 let mut state = DefaultHasher::new();
406 addr_abstract_unnamed_hash.hash(&mut state);
407 state.finish()
408 };
409 assert_ne!(addr_unnamed_hash, addr_abstract_unnamed_hash);
410 }
411 }
412}