1use std::ffi::{CStr, 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.is_empty() || 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 #[inline]
158 pub fn from_bytes_until_nul(bytes: &[u8]) -> io::Result<Self> {
171 match bytes {
172 [b'\0', rest @ ..] => {
173 let addr = CStr::from_bytes_until_nul(rest)
174 .map(|rest| rest.to_bytes())
175 .unwrap_or(rest);
176
177 Self::new_abstract_strict(addr)
178 }
179 _ => {
180 let addr = CStr::from_bytes_until_nul(bytes)
181 .map(|rest| rest.to_bytes())
182 .unwrap_or(bytes);
183
184 Self::new_pathname(OsStr::from_bytes(addr))
185 }
186 }
187 }
188
189 pub fn to_os_string(&self) -> OsString {
197 self.to_os_string_impl("", "\0")
198 }
199
200 pub fn to_string_lossy(&self) -> String {
209 self.to_os_string_impl("", "@")
210 .to_string_lossy()
211 .into_owned()
212 }
213
214 pub(crate) fn to_os_string_impl(&self, prefix: &str, abstract_identifier: &str) -> OsString {
215 let mut os_string = OsString::from(prefix);
216
217 if let Some(pathname) = self.as_pathname() {
218 os_string.push(pathname);
220
221 return os_string;
222 }
223
224 #[cfg(any(target_os = "android", target_os = "linux"))]
225 {
226 use std::os::linux::net::SocketAddrExt;
227
228 if let Some(abstract_name) = self.as_abstract_name() {
229 os_string.push(abstract_identifier);
230 os_string.push(OsStr::from_bytes(abstract_name));
231
232 return os_string;
233 }
234 }
235
236 os_string
238 }
239}
240
241impl fmt::Debug for SocketAddr {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 self.as_inner().fmt(f)
244 }
245}
246
247impl PartialEq for SocketAddr {
248 fn eq(&self, other: &Self) -> bool {
249 if let Some((l, r)) = self.as_pathname().zip(other.as_pathname()) {
250 return l == r;
251 }
252
253 #[cfg(any(target_os = "android", target_os = "linux"))]
254 {
255 use std::os::linux::net::SocketAddrExt;
256
257 if let Some((l, r)) = self.as_abstract_name().zip(other.as_abstract_name()) {
258 return l == r;
259 }
260 }
261
262 if self.is_unnamed() && other.is_unnamed() {
263 return true;
264 }
265
266 false
267 }
268}
269
270impl Eq for SocketAddr {}
271
272impl Hash for SocketAddr {
273 fn hash<H: Hasher>(&self, state: &mut H) {
274 if let Some(pathname) = self.as_pathname() {
275 pathname.hash(state);
276
277 return;
278 }
279
280 #[cfg(any(target_os = "android", target_os = "linux"))]
281 {
282 use std::os::linux::net::SocketAddrExt;
283
284 if let Some(abstract_name) = self.as_abstract_name() {
285 b'\0'.hash(state);
286 abstract_name.hash(state);
287
288 return;
289 }
290 }
291
292 debug_assert!(self.is_unnamed(), "SocketAddr is not unnamed one");
293
294 b"(unnamed)\0".hash(state);
297 }
298}
299
300#[cfg(feature = "feat-serde")]
301impl serde::Serialize for SocketAddr {
302 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303 where
304 S: serde::Serializer,
305 {
306 serializer.serialize_str(&self.to_string_lossy())
307 }
308}
309
310#[cfg(feature = "feat-serde")]
311impl<'de> serde::Deserialize<'de> for SocketAddr {
312 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
313 where
314 D: serde::Deserializer<'de>,
315 {
316 Self::new(<&str>::deserialize(deserializer)?).map_err(serde::de::Error::custom)
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use core::hash::{Hash, Hasher};
323 use std::hash::DefaultHasher;
324
325 use super::*;
326
327 #[test]
328 fn test_unnamed() {
329 const TEST_CASE: &str = "";
330
331 let addr = SocketAddr::new(TEST_CASE).unwrap();
332
333 assert!(addr.as_ref().is_unnamed());
334 }
335
336 #[test]
337 fn test_pathname() {
338 const TEST_CASE: &str = "/tmp/test_pathname.socket";
339
340 let addr = SocketAddr::new(TEST_CASE).unwrap();
341
342 assert_eq!(addr.to_os_string().to_str().unwrap(), TEST_CASE);
343 assert_eq!(addr.to_string_lossy(), TEST_CASE);
344 assert_eq!(addr.as_pathname().unwrap().to_str().unwrap(), TEST_CASE);
345 }
346
347 #[test]
348 #[cfg(any(target_os = "android", target_os = "linux"))]
349 fn test_abstract() {
350 use std::os::linux::net::SocketAddrExt;
351
352 const TEST_CASE_1: &[u8] = b"@abstract.socket";
353 const TEST_CASE_2: &[u8] = b"\0abstract.socket";
354 const TEST_CASE_3: &[u8] = b"@";
355 const TEST_CASE_4: &[u8] = b"\0";
356
357 assert_eq!(
358 SocketAddr::new(OsStr::from_bytes(TEST_CASE_1))
359 .unwrap()
360 .as_abstract_name()
361 .unwrap(),
362 &TEST_CASE_1[1..]
363 );
364
365 assert_eq!(
366 SocketAddr::new(OsStr::from_bytes(TEST_CASE_2))
367 .unwrap()
368 .as_abstract_name()
369 .unwrap(),
370 &TEST_CASE_2[1..]
371 );
372
373 assert_eq!(
374 SocketAddr::new(OsStr::from_bytes(TEST_CASE_3))
375 .unwrap()
376 .as_abstract_name()
377 .unwrap(),
378 &TEST_CASE_3[1..]
379 );
380
381 assert_eq!(
382 SocketAddr::new(OsStr::from_bytes(TEST_CASE_4))
383 .unwrap()
384 .as_abstract_name()
385 .unwrap(),
386 &TEST_CASE_4[1..]
387 );
388 }
389
390 #[test]
391 #[should_panic]
392 fn test_pathname_with_null_byte() {
393 let _addr = SocketAddr::new_pathname("(unamed)\0").unwrap();
394 }
395
396 #[test]
397 fn test_partial_eq_hash() {
398 let addr_pathname_1 = SocketAddr::new("/tmp/test_pathname_1.socket").unwrap();
399 let addr_pathname_2 = SocketAddr::new("/tmp/test_pathname_2.socket").unwrap();
400 let addr_unnamed = SocketAddr::new_unnamed();
401
402 assert_eq!(addr_pathname_1, addr_pathname_1);
403 assert_ne!(addr_pathname_1, addr_pathname_2);
404 assert_ne!(addr_pathname_2, addr_pathname_1);
405
406 assert_eq!(addr_unnamed, addr_unnamed);
407 assert_ne!(addr_pathname_1, addr_unnamed);
408 assert_ne!(addr_unnamed, addr_pathname_1);
409 assert_ne!(addr_pathname_2, addr_unnamed);
410 assert_ne!(addr_unnamed, addr_pathname_2);
411
412 #[cfg(any(target_os = "android", target_os = "linux"))]
413 {
414 let addr_abstract_1 = SocketAddr::new_abstract(b"/tmp/test_pathname_1.socket").unwrap();
415 let addr_abstract_2 = SocketAddr::new_abstract(b"/tmp/test_pathname_2.socket").unwrap();
416 let addr_abstract_empty = SocketAddr::new_abstract(&[]).unwrap();
417 let addr_abstract_unnamed_hash = SocketAddr::new_abstract(b"(unamed)\0").unwrap();
418
419 assert_eq!(addr_abstract_1, addr_abstract_1);
420 assert_ne!(addr_abstract_1, addr_abstract_2);
421 assert_ne!(addr_abstract_2, addr_abstract_1);
422
423 assert_ne!(addr_unnamed, addr_abstract_empty);
425
426 assert_ne!(addr_pathname_1, addr_abstract_1);
428
429 let addr_unnamed_hash = {
432 let mut state = DefaultHasher::new();
433 addr_unnamed.hash(&mut state);
434 state.finish()
435 };
436 let addr_abstract_unnamed_hash = {
437 let mut state = DefaultHasher::new();
438 addr_abstract_unnamed_hash.hash(&mut state);
439 state.finish()
440 };
441 assert_ne!(addr_unnamed_hash, addr_abstract_unnamed_hash);
442 }
443 }
444}