• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! socket helpers.
2 
3 use std::io::{Error, Result};
4 use std::mem::{size_of, zeroed};
5 use std::os::unix::ffi::OsStrExt;
6 use std::os::unix::net::UnixDatagram;
7 use std::os::unix::prelude::{AsRawFd, RawFd};
8 use std::path::Path;
9 use std::ptr;
10 
11 use libc::*;
12 
13 const CMSG_BUFSIZE: usize = 64;
14 
15 #[repr(C)]
16 union AlignedBuffer<T: Copy + Clone> {
17     buffer: T,
18     align: cmsghdr,
19 }
20 
assert_cmsg_bufsize()21 fn assert_cmsg_bufsize() {
22     let space_one_fd = unsafe { CMSG_SPACE(size_of::<RawFd>() as u32) };
23     assert!(
24         space_one_fd <= CMSG_BUFSIZE as u32,
25         "cmsghdr buffer too small (< {}) to hold a single fd",
26         space_one_fd
27     );
28 }
29 
30 #[cfg(test)]
31 #[test]
cmsg_buffer_size_for_one_fd()32 fn cmsg_buffer_size_for_one_fd() {
33     assert_cmsg_bufsize()
34 }
35 
send_one_fd_to<P: AsRef<Path>>(socket: &UnixDatagram, fd: RawFd, path: P) -> Result<usize>36 pub fn send_one_fd_to<P: AsRef<Path>>(socket: &UnixDatagram, fd: RawFd, path: P) -> Result<usize> {
37     assert_cmsg_bufsize();
38 
39     let mut addr: sockaddr_un = unsafe { zeroed() };
40     let path_bytes = path.as_ref().as_os_str().as_bytes();
41     // path_bytes may have at most sun_path + 1 bytes, to account for the trailing NUL byte.
42     if addr.sun_path.len() <= path_bytes.len() {
43         return Err(Error::from_raw_os_error(ENAMETOOLONG));
44     }
45 
46     addr.sun_family = AF_UNIX as _;
47     unsafe {
48         std::ptr::copy_nonoverlapping(
49             path_bytes.as_ptr(),
50             addr.sun_path.as_mut_ptr() as *mut u8,
51             path_bytes.len(),
52         )
53     };
54 
55     let mut msg: msghdr = unsafe { zeroed() };
56     // Set the target address.
57     msg.msg_name = &mut addr as *mut _ as *mut c_void;
58     msg.msg_namelen = size_of::<sockaddr_un>() as socklen_t;
59 
60     // We send no data body with this message.
61     msg.msg_iov = ptr::null_mut();
62     msg.msg_iovlen = 0;
63 
64     // Create and fill the control message buffer with our file descriptor
65     let mut cmsg_buffer = AlignedBuffer {
66         buffer: ([0u8; CMSG_BUFSIZE]),
67     };
68     msg.msg_control = unsafe { cmsg_buffer.buffer.as_mut_ptr() as _ };
69     msg.msg_controllen = unsafe { CMSG_SPACE(size_of::<RawFd>() as _) as _ };
70 
71     let mut cmsg: &mut cmsghdr =
72         unsafe { CMSG_FIRSTHDR(&msg).as_mut() }.expect("Control message buffer exhausted");
73 
74     cmsg.cmsg_level = SOL_SOCKET;
75     cmsg.cmsg_type = SCM_RIGHTS;
76     cmsg.cmsg_len = unsafe { CMSG_LEN(size_of::<RawFd>() as _) as _ };
77 
78     unsafe { ptr::write(CMSG_DATA(cmsg) as *mut RawFd, fd) };
79 
80     let result = unsafe { sendmsg(socket.as_raw_fd(), &msg, libc::MSG_NOSIGNAL) };
81 
82     if result < 0 {
83         Err(Error::last_os_error())
84     } else {
85         // sendmsg returns the number of bytes written
86         Ok(result as usize)
87     }
88 }
89