1 | mod socketaddr; |
2 | pub 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. |
10 | pub(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 | |
16 | cfg_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 | |