// Copyright © 2017 Mozilla Foundation // // This program is made available under an ISC-style license. See the // accompanying file LICENSE for details use crate::sys::HANDLE_QUEUE_LIMIT; use bytes::{BufMut, BytesMut}; use libc::{self, cmsghdr}; use std::convert::TryInto; use std::os::unix::io::RawFd; use std::{mem, slice}; trait AsBytes { fn as_bytes(&self) -> &[u8]; } impl<'a, T: Sized> AsBytes for &'a [T] { fn as_bytes(&self) -> &[u8] { // TODO: This should account for the alignment of T let byte_count = self.len() * mem::size_of::(); unsafe { slice::from_raw_parts(self.as_ptr() as *const _, byte_count) } } } // Encode `handles` into a cmsghdr in `buf`. pub fn encode_handles(cmsg: &mut BytesMut, handles: &[RawFd]) { assert!(handles.len() <= HANDLE_QUEUE_LIMIT); let msg = handles.as_bytes(); let cmsg_space = space(msg.len()); assert!(cmsg.remaining_mut() >= cmsg_space); // Some definitions of cmsghdr contain padding. Rather // than try to keep an up-to-date #cfg list to handle // that, just use a pre-zeroed struct to fill out any // fields we don't care about. let zeroed = unsafe { mem::zeroed() }; #[allow(clippy::needless_update)] // `cmsg_len` is `usize` on some platforms, `u32` on others. #[allow(clippy::useless_conversion)] let cmsghdr = cmsghdr { cmsg_len: len(msg.len()).try_into().unwrap(), cmsg_level: libc::SOL_SOCKET, cmsg_type: libc::SCM_RIGHTS, ..zeroed }; unsafe { let cmsghdr_ptr = cmsg.chunk_mut().as_mut_ptr(); std::ptr::copy_nonoverlapping( &cmsghdr as *const _ as *const _, cmsghdr_ptr, mem::size_of::(), ); let cmsg_data_ptr = libc::CMSG_DATA(cmsghdr_ptr as _); std::ptr::copy_nonoverlapping(msg.as_ptr(), cmsg_data_ptr, msg.len()); cmsg.advance_mut(cmsg_space); } } // Decode `buf` containing a cmsghdr with one or more handle(s). pub fn decode_handles(buf: &mut BytesMut) -> arrayvec::ArrayVec { let mut fds = arrayvec::ArrayVec::::new(); let cmsghdr_len = len(0); if buf.len() < cmsghdr_len { // No more entries---not enough data in `buf` for a // complete message. return fds; } let cmsg: &cmsghdr = unsafe { &*(buf.as_ptr() as *const _) }; #[allow(clippy::unnecessary_cast)] // `cmsg_len` type is platform-dependent. let cmsg_len = cmsg.cmsg_len as usize; match (cmsg.cmsg_level, cmsg.cmsg_type) { (libc::SOL_SOCKET, libc::SCM_RIGHTS) => { trace!("Found SCM_RIGHTS..."); let slice = &buf[cmsghdr_len..cmsg_len]; let slice = unsafe { slice::from_raw_parts( slice.as_ptr() as *const _, slice.len() / mem::size_of::(), ) }; fds.try_extend_from_slice(slice).unwrap(); } (level, kind) => { trace!("Skipping cmsg level, {}, type={}...", level, kind); } } assert!(fds.len() <= HANDLE_QUEUE_LIMIT); fds } fn len(len: usize) -> usize { unsafe { libc::CMSG_LEN(len.try_into().unwrap()) as usize } } pub fn space(len: usize) -> usize { unsafe { libc::CMSG_SPACE(len.try_into().unwrap()) as usize } }