| 1 | #[cfg (target_os = "android" )] |
| 2 | use std::os::android::net::SocketAddrExt; |
| 3 | #[cfg (target_os = "linux" )] |
| 4 | use std::os::linux::net::SocketAddrExt; |
| 5 | use std::os::unix::ffi::OsStrExt; |
| 6 | use std::os::unix::io::FromRawFd; |
| 7 | use std::os::unix::net::SocketAddr; |
| 8 | use std::{io, mem, ptr}; |
| 9 | |
| 10 | pub(crate) mod datagram; |
| 11 | pub(crate) mod listener; |
| 12 | pub(crate) mod stream; |
| 13 | |
| 14 | const UNNAMED_ADDRESS: &[u8] = &[]; |
| 15 | |
| 16 | /// Get the `sun_path` field offset of `sockaddr_un` for the target OS. |
| 17 | /// |
| 18 | /// On Linux, this function equates to the same value as |
| 19 | /// `size_of::<sa_family_t>()`, but some other implementations include |
| 20 | /// other fields before `sun_path`, so the expression more portably |
| 21 | /// describes the size of the address structure. |
| 22 | fn path_offset(sockaddr: &libc::sockaddr_un) -> usize { |
| 23 | let base: usize = sockaddr as *const _ as usize; |
| 24 | let path: usize = &sockaddr.sun_path as *const _ as usize; |
| 25 | path - base |
| 26 | } |
| 27 | |
| 28 | /// Converts a Rust `SocketAddr` into the system representation. |
| 29 | fn unix_addr(address: &SocketAddr) -> (libc::sockaddr_un, libc::socklen_t) { |
| 30 | // SAFETY: `libc::sockaddr_un` zero filled is properly initialized. |
| 31 | // |
| 32 | // `0` is a valid value for `sockaddr_un::sun_family`; it is |
| 33 | // `libc::AF_UNSPEC`. |
| 34 | // |
| 35 | // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an |
| 36 | // abstract path. |
| 37 | let mut sockaddr = unsafe { mem::zeroed::<libc::sockaddr_un>() }; |
| 38 | |
| 39 | sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
| 40 | |
| 41 | #[allow (unused_mut)] // Only used with abstract namespaces. |
| 42 | let mut offset = 0; |
| 43 | let addr = match address.as_pathname() { |
| 44 | Some(path) => path.as_os_str().as_bytes(), |
| 45 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
| 46 | None => match address.as_abstract_name() { |
| 47 | Some(name) => { |
| 48 | offset += 1; |
| 49 | name |
| 50 | } |
| 51 | None => UNNAMED_ADDRESS, |
| 52 | }, |
| 53 | #[cfg (not(any(target_os = "android" , target_os = "linux" )))] |
| 54 | None => UNNAMED_ADDRESS, |
| 55 | }; |
| 56 | |
| 57 | // SAFETY: `addr` and `sockaddr.sun_path` are not overlapping and both point |
| 58 | // to valid memory. |
| 59 | // SAFETY: since `addr` is a valid Unix address, it must not be larger than |
| 60 | // `SUN_LEN` bytes, thus we won't overwrite the size of sockaddr.sun_path. |
| 61 | // SAFETY: null byte is already written because we zeroed the address above. |
| 62 | debug_assert!(offset + addr.len() <= sockaddr.sun_path.len()); |
| 63 | unsafe { |
| 64 | ptr::copy_nonoverlapping( |
| 65 | addr.as_ptr(), |
| 66 | sockaddr.sun_path.as_mut_ptr().add(offset).cast(), |
| 67 | addr.len(), |
| 68 | ) |
| 69 | }; |
| 70 | |
| 71 | let mut addrlen = path_offset(&sockaddr) + addr.len(); |
| 72 | // +1 for null byte at the end of the path, not needed for abstract |
| 73 | // namespaces (which start with a null byte). |
| 74 | match addr.first() { |
| 75 | Some(&0) | None => {} |
| 76 | Some(_) => addrlen += 1, |
| 77 | } |
| 78 | |
| 79 | // SAFETY: the length is fine to cast to `socklen_t` as it's 32 bits and the |
| 80 | // address can be at most `SUN_LEN` bytes. |
| 81 | (sockaddr, addrlen as _) |
| 82 | } |
| 83 | |
| 84 | fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)> |
| 85 | where |
| 86 | T: FromRawFd, |
| 87 | { |
| 88 | #[cfg (not(any( |
| 89 | target_os = "aix" , |
| 90 | target_os = "haiku" , |
| 91 | target_os = "ios" , |
| 92 | target_os = "macos" , |
| 93 | target_os = "nto" , |
| 94 | target_os = "tvos" , |
| 95 | target_os = "visionos" , |
| 96 | target_os = "watchos" , |
| 97 | target_os = "espidf" , |
| 98 | target_os = "vita" , |
| 99 | )))] |
| 100 | let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; |
| 101 | |
| 102 | let mut fds = [-1; 2]; |
| 103 | syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?; |
| 104 | let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) }; |
| 105 | |
| 106 | // Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. |
| 107 | // |
| 108 | // In order to set those flags, additional `fcntl` sys calls must be |
| 109 | // performed. If a `fnctl` fails after the sockets have been created, |
| 110 | // the file descriptors will leak. Creating `pair` above ensures that if |
| 111 | // there is an error, the file descriptors are closed. |
| 112 | #[cfg (any( |
| 113 | target_os = "aix" , |
| 114 | target_os = "haiku" , |
| 115 | target_os = "ios" , |
| 116 | target_os = "macos" , |
| 117 | target_os = "nto" , |
| 118 | target_os = "tvos" , |
| 119 | target_os = "visionos" , |
| 120 | target_os = "watchos" , |
| 121 | target_os = "espidf" , |
| 122 | target_os = "vita" , |
| 123 | ))] |
| 124 | { |
| 125 | syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; |
| 126 | #[cfg (not(any(target_os = "espidf" , target_os = "vita" , target_os = "nto" )))] |
| 127 | syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; |
| 128 | syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?; |
| 129 | #[cfg (not(any(target_os = "espidf" , target_os = "vita" , target_os = "nto" )))] |
| 130 | syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?; |
| 131 | } |
| 132 | |
| 133 | Ok(pair) |
| 134 | } |
| 135 | |
| 136 | #[cfg (test)] |
| 137 | mod tests { |
| 138 | use std::os::unix::net::SocketAddr; |
| 139 | use std::path::Path; |
| 140 | use std::str; |
| 141 | |
| 142 | use super::{path_offset, unix_addr}; |
| 143 | |
| 144 | #[test ] |
| 145 | fn pathname_address() { |
| 146 | const PATH: &str = "./foo/bar.txt" ; |
| 147 | const PATH_LEN: usize = 13; |
| 148 | |
| 149 | // Pathname addresses do have a null terminator, so `socklen` is |
| 150 | // expected to be `PATH_LEN` + `offset` + 1. |
| 151 | let address = SocketAddr::from_pathname(Path::new(PATH)).unwrap(); |
| 152 | let (sockaddr, actual) = unix_addr(&address); |
| 153 | let offset = path_offset(&sockaddr); |
| 154 | let expected = PATH_LEN + offset + 1; |
| 155 | assert_eq!(expected as libc::socklen_t, actual) |
| 156 | } |
| 157 | |
| 158 | #[test ] |
| 159 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
| 160 | fn abstract_address() { |
| 161 | use std::os::linux::net::SocketAddrExt; |
| 162 | |
| 163 | const PATH: &[u8] = &[0, 116, 111, 107, 105, 111]; |
| 164 | const PATH_LEN: usize = 6; |
| 165 | |
| 166 | // Abstract addresses do not have a null terminator, so `socklen` is |
| 167 | // expected to be `PATH_LEN` + `offset`. |
| 168 | let address = SocketAddr::from_abstract_name(PATH).unwrap(); |
| 169 | let (sockaddr, actual) = unix_addr(&address); |
| 170 | let offset = path_offset(&sockaddr); |
| 171 | let expected = PATH_LEN + offset; |
| 172 | assert_eq!(expected as libc::socklen_t, actual) |
| 173 | } |
| 174 | } |
| 175 | |