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.
path_offset(sockaddr: &libc::sockaddr_un) -> usize22 fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
23     let base = sockaddr as *const _ as usize;
24     let path = &sockaddr.sun_path as *const _ as usize;
25     path - base
26 }
27 
28 /// Converts a Rust `SocketAddr` into the system representation.
unix_addr(address: &SocketAddr) -> (libc::sockaddr_un, libc::socklen_t)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 
pair<T>(flags: libc::c_int) -> io::Result<(T, T)> where T: FromRawFd,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]
pathname_address()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"))]
abstract_address()160     fn abstract_address() {
161         #[cfg(target_os = "android")]
162         use std::os::android::net::SocketAddrExt;
163         #[cfg(target_os = "linux")]
164         use std::os::linux::net::SocketAddrExt;
165 
166         const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
167         const PATH_LEN: usize = 6;
168 
169         // Abstract addresses do not have a null terminator, so `socklen` is
170         // expected to be `PATH_LEN` + `offset`.
171         let address = SocketAddr::from_abstract_name(PATH).unwrap();
172         let (sockaddr, actual) = unix_addr(&address);
173         let offset = path_offset(&sockaddr);
174         let expected = PATH_LEN + offset;
175         assert_eq!(expected as libc::socklen_t, actual)
176     }
177 }
178