1mod socketaddr;
2pub use self::socketaddr::SocketAddr;
3
4/// Get the `sun_path` field offset of `sockaddr_un` for the target OS.
5///
6/// On Linux, this function equates to the same value as
7/// `size_of::<sa_family_t>()`, but some other implementations include
8/// other fields before `sun_path`, so the expression more portably
9/// describes the size of the address structure.
10pub(in crate::sys) fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
11 let base: usize = sockaddr as *const _ as usize;
12 let path: usize = &sockaddr.sun_path as *const _ as usize;
13 path - base
14}
15
16cfg_os_poll! {
17 use std::cmp::Ordering;
18 use std::os::unix::io::{RawFd, FromRawFd};
19 use std::{io, mem};
20
21 pub(crate) mod datagram;
22 pub(crate) mod listener;
23 pub(crate) mod stream;
24
25 pub(in crate::sys) fn socket_addr(bytes: &[u8]) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
26 let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
27
28 // This is safe to assume because a `libc::sockaddr_un` filled with `0`
29 // bytes is properly initialized.
30 //
31 // `0` is a valid value for `sockaddr_un::sun_family`; it is
32 // `libc::AF_UNSPEC`.
33 //
34 // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
35 // abstract path.
36 let mut sockaddr = unsafe { sockaddr.assume_init() };
37
38 sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
39
40 match (bytes.first(), bytes.len().cmp(&sockaddr.sun_path.len())) {
41 // Abstract paths don't need a null terminator
42 (Some(&0), Ordering::Greater) => {
43 return Err(io::Error::new(
44 io::ErrorKind::InvalidInput,
45 "path must be no longer than libc::sockaddr_un.sun_path",
46 ));
47 }
48 (_, Ordering::Greater) | (_, Ordering::Equal) => {
49 return Err(io::Error::new(
50 io::ErrorKind::InvalidInput,
51 "path must be shorter than libc::sockaddr_un.sun_path",
52 ));
53 }
54 _ => {}
55 }
56
57 for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) {
58 *dst = *src as libc::c_char;
59 }
60
61 let offset = path_offset(&sockaddr);
62 let mut socklen = offset + bytes.len();
63
64 match bytes.first() {
65 // The struct has already been zeroes so the null byte for pathname
66 // addresses is already there.
67 Some(&0) | None => {}
68 Some(_) => socklen += 1,
69 }
70
71 Ok((sockaddr, socklen as libc::socklen_t))
72 }
73
74 fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
75 where T: FromRawFd,
76 {
77 #[cfg(not(any(
78 target_os = "aix",
79 target_os = "ios",
80 target_os = "macos",
81 target_os = "tvos",
82 target_os = "watchos",
83 target_os = "espidf",
84 target_os = "vita",
85 )))]
86 let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
87
88 let mut fds = [-1; 2];
89 syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
90 let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) };
91
92 // Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
93 //
94 // In order to set those flags, additional `fcntl` sys calls must be
95 // performed. If a `fnctl` fails after the sockets have been created,
96 // the file descriptors will leak. Creating `pair` above ensures that if
97 // there is an error, the file descriptors are closed.
98 #[cfg(any(
99 target_os = "aix",
100 target_os = "ios",
101 target_os = "macos",
102 target_os = "tvos",
103 target_os = "watchos",
104 target_os = "espidf",
105 target_os = "vita",
106 ))]
107 {
108 syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
109 #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
110 syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
111 syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
112 #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
113 syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
114 }
115
116 Ok(pair)
117 }
118
119 // The following functions can't simply be replaced with a call to
120 // `net::UnixDatagram` because of our `SocketAddr` type.
121
122 fn local_addr(socket: RawFd) -> io::Result<SocketAddr> {
123 SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen)))
124 }
125
126 fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> {
127 SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen)))
128 }
129
130 #[cfg(test)]
131 mod tests {
132 use super::{path_offset, socket_addr};
133 use std::os::unix::ffi::OsStrExt;
134 use std::path::Path;
135 use std::str;
136
137 #[test]
138 fn pathname_address() {
139 const PATH: &str = "./foo/bar.txt";
140 const PATH_LEN: usize = 13;
141
142 // Pathname addresses do have a null terminator, so `socklen` is
143 // expected to be `PATH_LEN` + `offset` + 1.
144 let path = Path::new(PATH);
145 let (sockaddr, actual) = socket_addr(path.as_os_str().as_bytes()).unwrap();
146 let offset = path_offset(&sockaddr);
147 let expected = PATH_LEN + offset + 1;
148 assert_eq!(expected as libc::socklen_t, actual)
149 }
150
151 #[test]
152 fn abstract_address() {
153 const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
154 const PATH_LEN: usize = 6;
155
156 // Abstract addresses do not have a null terminator, so `socklen` is
157 // expected to be `PATH_LEN` + `offset`.
158 let (sockaddr, actual) = socket_addr(PATH).unwrap();
159 let offset = path_offset(&sockaddr);
160 let expected = PATH_LEN + offset;
161 assert_eq!(expected as libc::socklen_t, actual)
162 }
163 }
164}
165