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 funtion 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.
path_offset(sockaddr: &libc::sockaddr_un) -> usize10 pub(in crate::sys) fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
11     let base = sockaddr as *const _ as usize;
12     let path = &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.get(0), 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.get(0) {
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(target_os = "ios", target_os = "macos", target_os = "solaris")))]
81         let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
82 
83         let mut fds = [-1; 2];
84         syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
85         let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) };
86 
87         // Darwin and Solaris do not have SOCK_NONBLOCK or SOCK_CLOEXEC.
88         //
89         // In order to set those flags, additional `fcntl` sys calls must be
90         // performed. If a `fnctl` fails after the sockets have been created,
91         // the file descriptors will leak. Creating `pair` above ensures that if
92         // there is an error, the file descriptors are closed.
93         #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))]
94         {
95             syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
96             syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
97             syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
98             syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
99         }
100         Ok(pair)
101     }
102 
103     // The following functions can't simply be replaced with a call to
104     // `net::UnixDatagram` because of our `SocketAddr` type.
105 
106     fn local_addr(socket: RawFd) -> io::Result<SocketAddr> {
107         SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen)))
108     }
109 
110     fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> {
111         SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen)))
112     }
113 
114     #[cfg(test)]
115     mod tests {
116         use super::{path_offset, socket_addr};
117         use std::path::Path;
118         use std::str;
119 
120         #[test]
121         fn pathname_address() {
122             const PATH: &str = "./foo/bar.txt";
123             const PATH_LEN: usize = 13;
124 
125             // Pathname addresses do have a null terminator, so `socklen` is
126             // expected to be `PATH_LEN` + `offset` + 1.
127             let path = Path::new(PATH);
128             let (sockaddr, actual) = socket_addr(path).unwrap();
129             let offset = path_offset(&sockaddr);
130             let expected = PATH_LEN + offset + 1;
131             assert_eq!(expected as libc::socklen_t, actual)
132         }
133 
134         #[test]
135         fn abstract_address() {
136             const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
137             const PATH_LEN: usize = 6;
138 
139             // Abstract addresses do not have a null terminator, so `socklen` is
140             // expected to be `PATH_LEN` + `offset`.
141             let abstract_path = str::from_utf8(PATH).unwrap();
142             let path = Path::new(abstract_path);
143             let (sockaddr, actual) = socket_addr(path).unwrap();
144             let offset = path_offset(&sockaddr);
145             let expected = PATH_LEN + offset;
146             assert_eq!(expected as libc::socklen_t, actual)
147         }
148     }
149 }
150