|
7 | 7 | // except according to those terms.
|
8 | 8 |
|
9 | 9 | use std::cmp::min;
|
| 10 | +use std::ffi::OsStr; |
10 | 11 | #[cfg(not(target_os = "redox"))]
|
11 | 12 | use std::io::IoSlice;
|
12 | 13 | use std::marker::PhantomData;
|
@@ -569,6 +570,13 @@ impl<'a> MaybeUninitSlice<'a> {
|
569 | 570 | }
|
570 | 571 | }
|
571 | 572 |
|
| 573 | +/// Returns the offset of the `sun_path` member of the passed unix socket address. |
| 574 | +pub(crate) fn offset_of_path(storage: &libc::sockaddr_un) -> usize { |
| 575 | + let base = storage as *const _ as usize; |
| 576 | + let path = ptr::addr_of!(storage.sun_path) as usize; |
| 577 | + path - base |
| 578 | +} |
| 579 | + |
572 | 580 | #[allow(unsafe_op_in_unsafe_fn)]
|
573 | 581 | pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
|
574 | 582 | // SAFETY: a `sockaddr_storage` of all zeros is valid.
|
@@ -603,9 +611,7 @@ pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
|
603 | 611 | );
|
604 | 612 | }
|
605 | 613 |
|
606 |
| - let base = storage as *const _ as usize; |
607 |
| - let path = ptr::addr_of!(storage.sun_path) as usize; |
608 |
| - let sun_path_offset = path - base; |
| 614 | + let sun_path_offset = offset_of_path(storage); |
609 | 615 | sun_path_offset
|
610 | 616 | + bytes.len()
|
611 | 617 | + match bytes.first() {
|
@@ -659,6 +665,91 @@ impl SockAddr {
|
659 | 665 | None
|
660 | 666 | }
|
661 | 667 | }
|
| 668 | + |
| 669 | + /// Returns true if this address is an unnamed address from the `AF_UNIX` family (for local |
| 670 | + /// interprocess communication), false otherwise. |
| 671 | + pub fn is_unnamed(&self) -> bool { |
| 672 | + self.as_sockaddr_un() |
| 673 | + .map(|storage| { |
| 674 | + self.len() == offset_of_path(storage) as u32 |
| 675 | + // On some non-linux platforms a zeroed path is returned for unnamed. |
| 676 | + // Abstract addresses only exist on Linux. |
| 677 | + // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented. |
| 678 | + // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978 |
| 679 | + || (cfg!(not(any(target_os = "linux", target_os = "android"))) |
| 680 | + && storage.sun_path[0] == 0) |
| 681 | + }) |
| 682 | + .unwrap_or_default() |
| 683 | + } |
| 684 | + |
| 685 | + /// Returns the underlying `sockaddr_un` object if this addres is from the `AF_UNIX` family, |
| 686 | + /// otherwise returns `None`. |
| 687 | + pub(crate) fn as_sockaddr_un(&self) -> Option<&libc::sockaddr_un> { |
| 688 | + self.is_unix().then(|| { |
| 689 | + // SAFETY: if unix socket, i.e. the `ss_family` field is `AF_UNIX` then storage must be |
| 690 | + // a `sockaddr_un`. |
| 691 | + unsafe { &*self.as_ptr().cast::<libc::sockaddr_un>() } |
| 692 | + }) |
| 693 | + } |
| 694 | + |
| 695 | + /// Get the length of the path bytes of the address, not including the terminating or initial |
| 696 | + /// (for abstract names) null byte. |
| 697 | + /// |
| 698 | + /// Should not be called on unnamed addresses. |
| 699 | + fn path_len(&self, storage: &libc::sockaddr_un) -> usize { |
| 700 | + debug_assert!(!self.is_unnamed()); |
| 701 | + self.len() as usize - offset_of_path(storage) - 1 |
| 702 | + } |
| 703 | + |
| 704 | + /// Get a u8 slice for the bytes of the pathname or abstract name. |
| 705 | + /// |
| 706 | + /// Should not be called on unnamed addresses. |
| 707 | + fn path_bytes(&self, storage: &libc::sockaddr_un, abstract_name: bool) -> &[u8] { |
| 708 | + debug_assert!(!self.is_unnamed()); |
| 709 | + // SAFETY: the pointed objects of type `i8` have the same memory layout as `u8`. The path is |
| 710 | + // the last field in the storage and so its length is equal to |
| 711 | + // TOTAL_LENGTH - OFFSET_OF_PATH -1 |
| 712 | + // Where the 1 is either a terminating null if we have a pathname address, or the initial |
| 713 | + // null byte, if it's an abstract name address. In the latter case, the path bytes start |
| 714 | + // after the initial null byte, hence the `offset`. |
| 715 | + // There is no safe way to convert a `&[i8]` to `&[u8]` |
| 716 | + unsafe { |
| 717 | + slice::from_raw_parts( |
| 718 | + (storage.sun_path.as_ptr() as *const u8).offset(abstract_name as isize), |
| 719 | + self.path_len(storage), |
| 720 | + ) |
| 721 | + } |
| 722 | + } |
| 723 | + |
| 724 | + /// Returns this address as a `Path` reference if it is an `AF_UNIX` pathname address, otherwise |
| 725 | + /// returns `None`. |
| 726 | + pub fn as_pathname(&self) -> Option<&Path> { |
| 727 | + self.as_sockaddr_un().and_then(|storage| { |
| 728 | + (self.len() > offset_of_path(storage) as u32 && storage.sun_path[0] != 0).then(|| { |
| 729 | + let path_slice = self.path_bytes(storage, false); |
| 730 | + Path::new::<OsStr>(OsStrExt::from_bytes(path_slice)) |
| 731 | + }) |
| 732 | + }) |
| 733 | + } |
| 734 | + |
| 735 | + /// Returns this address as a slice of bytes representing an abstract address if it is an |
| 736 | + /// `AF_UNIX` abstract address, otherwise returns `None`. |
| 737 | + /// |
| 738 | + /// Abstract addresses are a Linux extension, so this method returns `None` on all non-Linux |
| 739 | + /// platforms. |
| 740 | + pub fn as_abstract_namespace(&self) -> Option<&[u8]> { |
| 741 | + // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented. |
| 742 | + // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978 |
| 743 | + #[cfg(any(target_os = "linux", target_os = "android"))] |
| 744 | + { |
| 745 | + self.as_sockaddr_un().and_then(|storage| { |
| 746 | + (self.len() > offset_of_path(storage) as u32 && storage.sun_path[0] == 0) |
| 747 | + .then(|| self.path_bytes(storage, true)) |
| 748 | + }) |
| 749 | + } |
| 750 | + #[cfg(not(any(target_os = "linux", target_os = "android")))] |
| 751 | + None |
| 752 | + } |
662 | 753 | }
|
663 | 754 |
|
664 | 755 | pub(crate) type Socket = c_int;
|
|
0 commit comments