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::ffi::OsStrExt; |
19 | use std::os::unix::io::{RawFd, FromRawFd}; |
20 | use std::path::Path; |
21 | use std::{io, mem}; |
22 | |
23 | pub(crate) mod datagram; |
24 | pub(crate) mod listener; |
25 | pub(crate) mod stream; |
26 | |
27 | pub(in crate::sys) fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { |
28 | let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); |
29 | |
30 | // This is safe to assume because a `libc::sockaddr_un` filled with `0` |
31 | // bytes is properly initialized. |
32 | // |
33 | // `0` is a valid value for `sockaddr_un::sun_family`; it is |
34 | // `libc::AF_UNSPEC`. |
35 | // |
36 | // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an |
37 | // abstract path. |
38 | let mut sockaddr = unsafe { sockaddr.assume_init() }; |
39 | |
40 | sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
41 | |
42 | let bytes = path.as_os_str().as_bytes(); |
43 | match (bytes.first(), bytes.len().cmp(&sockaddr.sun_path.len())) { |
44 | // Abstract paths don't need a null terminator |
45 | (Some(&0), Ordering::Greater) => { |
46 | return Err(io::Error::new( |
47 | io::ErrorKind::InvalidInput, |
48 | "path must be no longer than libc::sockaddr_un.sun_path" , |
49 | )); |
50 | } |
51 | (_, Ordering::Greater) | (_, Ordering::Equal) => { |
52 | return Err(io::Error::new( |
53 | io::ErrorKind::InvalidInput, |
54 | "path must be shorter than libc::sockaddr_un.sun_path" , |
55 | )); |
56 | } |
57 | _ => {} |
58 | } |
59 | |
60 | for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) { |
61 | *dst = *src as libc::c_char; |
62 | } |
63 | |
64 | let offset = path_offset(&sockaddr); |
65 | let mut socklen = offset + bytes.len(); |
66 | |
67 | match bytes.first() { |
68 | // The struct has already been zeroes so the null byte for pathname |
69 | // addresses is already there. |
70 | Some(&0) | None => {} |
71 | Some(_) => socklen += 1, |
72 | } |
73 | |
74 | Ok((sockaddr, socklen as libc::socklen_t)) |
75 | } |
76 | |
77 | fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)> |
78 | where T: FromRawFd, |
79 | { |
80 | #[cfg (not(any( |
81 | target_os = "ios" , |
82 | target_os = "macos" , |
83 | target_os = "tvos" , |
84 | target_os = "watchos" , |
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 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 = "ios" , |
100 | target_os = "macos" , |
101 | target_os = "tvos" , |
102 | target_os = "watchos" , |
103 | ))] |
104 | { |
105 | syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; |
106 | syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; |
107 | syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?; |
108 | syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?; |
109 | } |
110 | Ok(pair) |
111 | } |
112 | |
113 | // The following functions can't simply be replaced with a call to |
114 | // `net::UnixDatagram` because of our `SocketAddr` type. |
115 | |
116 | fn local_addr(socket: RawFd) -> io::Result<SocketAddr> { |
117 | SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen))) |
118 | } |
119 | |
120 | fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> { |
121 | SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen))) |
122 | } |
123 | |
124 | #[cfg (test)] |
125 | mod tests { |
126 | use super::{path_offset, socket_addr}; |
127 | use std::path::Path; |
128 | use std::str; |
129 | |
130 | #[test ] |
131 | fn pathname_address() { |
132 | const PATH: &str = "./foo/bar.txt" ; |
133 | const PATH_LEN: usize = 13; |
134 | |
135 | // Pathname addresses do have a null terminator, so `socklen` is |
136 | // expected to be `PATH_LEN` + `offset` + 1. |
137 | let path = Path::new(PATH); |
138 | let (sockaddr, actual) = socket_addr(path).unwrap(); |
139 | let offset = path_offset(&sockaddr); |
140 | let expected = PATH_LEN + offset + 1; |
141 | assert_eq!(expected as libc::socklen_t, actual) |
142 | } |
143 | |
144 | #[test ] |
145 | fn abstract_address() { |
146 | const PATH: &[u8] = &[0, 116, 111, 107, 105, 111]; |
147 | const PATH_LEN: usize = 6; |
148 | |
149 | // Abstract addresses do not have a null terminator, so `socklen` is |
150 | // expected to be `PATH_LEN` + `offset`. |
151 | let abstract_path = str::from_utf8(PATH).unwrap(); |
152 | let path = Path::new(abstract_path); |
153 | let (sockaddr, actual) = socket_addr(path).unwrap(); |
154 | let offset = path_offset(&sockaddr); |
155 | let expected = PATH_LEN + offset; |
156 | assert_eq!(expected as libc::socklen_t, actual) |
157 | } |
158 | } |
159 | } |
160 | |