diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-14 18:13:34 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-14 18:13:34 +0000 |
commit | a2fa2828e0663de420ce02a063c9ea66e7f00c36 (patch) | |
tree | 6c8cea3595d7f4be9aeaa90e980f692c99d4a21c /src | |
parent | 842f5acf3e003e5600746c9f4ee0a3c02fbf3dd0 (diff) | |
parent | b5c4c7a9c043afc97ced704c1edd482e12903234 (diff) | |
download | nix-a2fa2828e0663de420ce02a063c9ea66e7f00c36.zip |
Merge #1020
1020: Major cmsg cleanup r=asomers a=asomers
This PR fixes multiple bugs in the cmsg code.
Fixes #994
Fixes #999
Fixes #1013
Co-authored-by: Alan Somers <asomers@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/sys/socket/mod.rs | 823 | ||||
-rw-r--r-- | src/sys/time.rs | 7 |
2 files changed, 384 insertions, 446 deletions
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 64d2fc12..80009917 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -3,7 +3,8 @@ //! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html) use {Error, Result}; use errno::Errno; -use libc::{self, c_void, c_int, socklen_t, size_t}; +use libc::{self, c_void, c_int, iovec, socklen_t, size_t, + CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN}; use std::{fmt, mem, ptr, slice}; use std::os::unix::io::RawFd; use sys::time::TimeVal; @@ -42,6 +43,10 @@ pub use libc::{ sockaddr_un, }; +// Needed by the cmsg_space macro +#[doc(hidden)] +pub use libc::{c_uint, CMSG_SPACE}; + /// These constants are used to specify the communication semantics /// when creating a socket with [`socket()`](fn.socket.html) #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -303,39 +308,6 @@ impl fmt::Debug for Ipv6MembershipRequest { } } -/// Copy the in-memory representation of `src` into the byte slice `dst`. -/// -/// Returns the remainder of `dst`. -/// -/// Panics when `dst` is too small for `src` (more precisely, panics if -/// `mem::size_of_val(src) >= dst.len()`). -/// -/// Unsafe because it transmutes `src` to raw bytes, which is only safe for some -/// types `T`. Refer to the [Rustonomicon] for details. -/// -/// [Rustonomicon]: https://doc.rust-lang.org/nomicon/transmutes.html -unsafe fn copy_bytes<'a, T: ?Sized>(src: &T, dst: &'a mut [u8]) -> &'a mut [u8] { - let srclen = mem::size_of_val(src); - ptr::copy_nonoverlapping( - src as *const T as *const u8, - dst[..srclen].as_mut_ptr(), - srclen - ); - - &mut dst[srclen..] -} - -/// Fills `dst` with `len` zero bytes and returns the remainder of the slice. -/// -/// Panics when `len >= dst.len()`. -fn pad_bytes(len: usize, dst: &mut [u8]) -> &mut [u8] { - for pad in &mut dst[..len] { - *pad = 0; - } - - &mut dst[len..] -} - cfg_if! { // Darwin and DragonFly BSD always align struct cmsghdr to 32-bit only. if #[cfg(any(target_os = "dragonfly", target_os = "ios", target_os = "macos"))] { @@ -345,6 +317,55 @@ cfg_if! { } } +/// A type that can be used to store ancillary data received by +/// [`recvmsg`](fn.recvmsg.html) +pub trait CmsgBuffer { + fn as_bytes_mut(&mut self) -> &mut [u8]; +} + +/// Create a buffer large enough for storing some control messages as returned +/// by [`recvmsg`](fn.recvmsg.html). +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// # use nix::sys::time::TimeVal; +/// # use std::os::unix::io::RawFd; +/// # fn main() { +/// // Create a buffer for a `ControlMessageOwned::ScmTimestamp` message +/// let _ = cmsg_space!(TimeVal); +/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message +/// // with two file descriptors +/// let _ = cmsg_space!([RawFd; 2]); +/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message +/// // and a `ControlMessageOwned::ScmTimestamp` message +/// let _ = cmsg_space!(RawFd, TimeVal); +/// # } +/// ``` +// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a +// stack-allocated array. +#[macro_export] +macro_rules! cmsg_space { + ( $( $x:ty ),* ) => { + { + use nix::sys::socket::{c_uint, CMSG_SPACE}; + use std::mem; + let mut space = 0; + $( + // CMSG_SPACE is always safe + space += unsafe { + CMSG_SPACE(mem::size_of::<$x>() as c_uint) + } as usize; + )* + let mut v = Vec::<u8>::with_capacity(space); + // safe because any bit pattern is a valid u8 + unsafe {v.set_len(space)}; + v + } + } +} + /// A structure used to make room in a cmsghdr passed to recvmsg. The /// size and alignment match that of a cmsghdr followed by a T, but the /// fields are not accessible, as the actual types will change on a call @@ -369,19 +390,35 @@ pub struct CmsgSpace<T> { impl<T> CmsgSpace<T> { /// Create a CmsgSpace<T>. The structure is used only for space, so /// the fields are uninitialized. + #[deprecated( since="0.14.0", note="Use the cmsg_space! macro instead")] pub fn new() -> Self { // Safe because the fields themselves aren't accessible. unsafe { mem::uninitialized() } } } -#[derive(Debug)] +impl<T> CmsgBuffer for CmsgSpace<T> { + fn as_bytes_mut(&mut self) -> &mut [u8] { + // Safe because nothing ever attempts to access CmsgSpace's fields + unsafe { + slice::from_raw_parts_mut(self as *mut CmsgSpace<T> as *mut u8, + mem::size_of::<Self>()) + } + } +} + +impl CmsgBuffer for Vec<u8> { + fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self[..] + } +} + +#[allow(missing_debug_implementations)] // msghdr isn't Debug pub struct RecvMsg<'a> { - // The number of bytes received. - pub bytes: usize, - cmsg_buffer: &'a [u8], + cmsghdr: Option<&'a cmsghdr>, pub address: Option<SockAddr>, pub flags: MsgFlags, + mhdr: msghdr, } impl<'a> RecvMsg<'a> { @@ -389,79 +426,60 @@ impl<'a> RecvMsg<'a> { /// msghdr. pub fn cmsgs(&self) -> CmsgIterator { CmsgIterator { - buf: self.cmsg_buffer, + cmsghdr: self.cmsghdr, + mhdr: &self.mhdr } } } -#[derive(Debug)] +#[allow(missing_debug_implementations)] // msghdr isn't Debug pub struct CmsgIterator<'a> { /// Control message buffer to decode from. Must adhere to cmsg alignment. - buf: &'a [u8], + cmsghdr: Option<&'a cmsghdr>, + mhdr: &'a msghdr } impl<'a> Iterator for CmsgIterator<'a> { - type Item = ControlMessage<'a>; - - // The implementation loosely follows CMSG_FIRSTHDR / CMSG_NXTHDR, - // although we handle the invariants in slightly different places to - // get a better iterator interface. - fn next(&mut self) -> Option<ControlMessage<'a>> { - if self.buf.len() == 0 { - // The iterator assumes that `self.buf` always contains exactly the - // bytes we need, so we're at the end when the buffer is empty. - return None; - } - - // Safe if: `self.buf` is `cmsghdr`-aligned. - let cmsg: &'a cmsghdr = unsafe { - &*(self.buf[..mem::size_of::<cmsghdr>()].as_ptr() as *const cmsghdr) - }; - - let cmsg_len = cmsg.cmsg_len as usize; - - // Advance our internal pointer. - let cmsg_data = &self.buf[cmsg_align(mem::size_of::<cmsghdr>())..cmsg_len]; - self.buf = &self.buf[cmsg_align(cmsg_len)..]; - - // Safe if: `cmsg_data` contains the expected (amount of) content. This - // is verified by the kernel. - unsafe { - Some(ControlMessage::decode_from(cmsg, cmsg_data)) + type Item = ControlMessageOwned; + + fn next(&mut self) -> Option<ControlMessageOwned> { + match self.cmsghdr { + None => None, // No more messages + Some(hdr) => { + // Get the data. + // Safe if cmsghdr points to valid data returned by recvmsg(2) + let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))}; + // Advance the internal pointer. Safe if mhdr and cmsghdr point + // to valid data returned by recvmsg(2) + self.cmsghdr = unsafe { + let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _); + p.as_ref() + }; + cm + } } } } -/// A type-safe wrapper around a single control message. More types may -/// be added to this enum; do not exhaustively pattern-match it. +/// A type-safe wrapper around a single control message, as used with +/// [`recvmsg`](#fn.recvmsg). +/// /// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html) +// Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and +// sendmsg. However, on some platforms the messages returned by recvmsg may be +// unaligned. ControlMessageOwned takes those messages by copy, obviating any +// alignment issues. +// +// See https://github.com/nix-rust/nix/issues/999 #[allow(missing_debug_implementations)] -pub enum ControlMessage<'a> { - /// A message of type `SCM_RIGHTS`, containing an array of file - /// descriptors passed between processes. - /// - /// See the description in the "Ancillary messages" section of the - /// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html). - /// - /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't recommended since it - /// causes platform-dependent behaviour: It might swallow all but the first `ScmRights` message - /// or fail with `EINVAL`. Instead, you can put all fds to be passed into a single `ScmRights` - /// message. - ScmRights(&'a [RawFd]), - /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of - /// a process connected to the socket. - /// - /// This is similar to the socket option `SO_PEERCRED`, but requires a - /// process to explicitly send its credentials. A process running as root is - /// allowed to specify any credentials, while credentials sent by other - /// processes are verified by the kernel. - /// - /// For further information, please refer to the - /// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page. - // FIXME: When `#[repr(transparent)]` is stable, use it on `UnixCredentials` - // and put that in here instead of a raw ucred. +pub enum ControlMessageOwned { + /// Received version of + /// [`ControlMessage::ScmRights`][#enum.ControlMessage.html#variant.ScmRights] + ScmRights(Vec<RawFd>), + /// Received version of + /// [`ControlMessage::ScmCredentials`][#enum.ControlMessage.html#variant.ScmCredentials] #[cfg(any(target_os = "android", target_os = "linux"))] - ScmCredentials(&'a libc::ucred), + ScmCredentials(libc::ucred), /// A message of type `SCM_TIMESTAMP`, containing the time the /// packet was received by the kernel. /// @@ -474,11 +492,12 @@ pub enum ControlMessage<'a> { // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222039 #[cfg_attr(not(all(target_os = "freebsd", target_arch = "x86")), doc = " ```")] #[cfg_attr(all(target_os = "freebsd", target_arch = "x86"), doc = " ```no_run")] - /// use nix::sys::socket::*; - /// use nix::sys::uio::IoVec; - /// use nix::sys::time::*; - /// use std::time::*; - /// + /// # #[macro_use] extern crate nix; + /// # use nix::sys::socket::*; + /// # use nix::sys::uio::IoVec; + /// # use nix::sys::time::*; + /// # use std::time::*; + /// # fn main() { /// // Set up /// let message = "OhayĆ!".as_bytes(); /// let in_socket = socket( @@ -499,11 +518,11 @@ pub enum ControlMessage<'a> { /// assert_eq!(message.len(), l); /// // Receive the message /// let mut buffer = vec![0u8; message.len()]; - /// let mut cmsgspace: CmsgSpace<TimeVal> = CmsgSpace::new(); + /// let mut cmsgspace = cmsg_space!(TimeVal); /// let iov = [IoVec::from_mut_slice(&mut buffer)]; /// let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap(); /// let rtime = match r.cmsgs().next() { - /// Some(ControlMessage::ScmTimestamp(&rtime)) => rtime, + /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime, /// Some(_) => panic!("Unexpected control message"), /// None => panic!("No control message") /// }; @@ -517,24 +536,28 @@ pub enum ControlMessage<'a> { /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); /// // Close socket /// nix::unistd::close(in_socket).unwrap(); + /// # } /// ``` - ScmTimestamp(&'a TimeVal), - + ScmTimestamp(TimeVal), #[cfg(any( target_os = "android", target_os = "ios", target_os = "linux", - target_os = "macos" + target_os = "macos", + target_os = "netbsd", ))] - Ipv4PacketInfo(&'a libc::in_pktinfo), + Ipv4PacketInfo(libc::in_pktinfo), #[cfg(any( target_os = "android", + target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "linux", - target_os = "macos" + target_os = "macos", + target_os = "openbsd", + target_os = "netbsd", ))] - Ipv6PacketInfo(&'a libc::in6_pktinfo), + Ipv6PacketInfo(libc::in6_pktinfo), #[cfg(any( target_os = "freebsd", target_os = "ios", @@ -542,7 +565,7 @@ pub enum ControlMessage<'a> { target_os = "netbsd", target_os = "openbsd", ))] - Ipv4RecvIf(&'a libc::sockaddr_dl), + Ipv4RecvIf(libc::sockaddr_dl), #[cfg(any( target_os = "freebsd", target_os = "ios", @@ -550,282 +573,46 @@ pub enum ControlMessage<'a> { target_os = "netbsd", target_os = "openbsd", ))] - Ipv4RecvDstAddr(&'a libc::in_addr), - + Ipv4RecvDstAddr(libc::in_addr), /// Catch-all variant for unimplemented cmsg types. #[doc(hidden)] - Unknown(UnknownCmsg<'a>), + Unknown(UnknownCmsg), } -// An opaque structure used to prevent cmsghdr from being a public type -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct UnknownCmsg<'a>(&'a cmsghdr, &'a [u8]); - -// Round `len` up to meet the platform's required alignment for -// `cmsghdr`s and trailing `cmsghdr` data. This should match the -// behaviour of CMSG_ALIGN from the Linux headers and do the correct -// thing on other platforms that don't usually provide CMSG_ALIGN. -#[inline] -fn cmsg_align(len: usize) -> usize { - let align_bytes = mem::size_of::<align_of_cmsg_data>() - 1; - (len + align_bytes) & !align_bytes -} - -impl<'a> ControlMessage<'a> { - /// The value of CMSG_SPACE on this message. - fn space(&self) -> usize { - cmsg_align(self.len()) - } - - /// The value of CMSG_LEN on this message. - fn len(&self) -> usize { - cmsg_align(mem::size_of::<cmsghdr>()) + match *self { - ControlMessage::ScmRights(fds) => { - mem::size_of_val(fds) - }, - #[cfg(any(target_os = "android", target_os = "linux"))] - ControlMessage::ScmCredentials(creds) => { - mem::size_of_val(creds) - } - ControlMessage::ScmTimestamp(t) => { - mem::size_of_val(t) - }, - #[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv4PacketInfo(pktinfo) => { - mem::size_of_val(pktinfo) - }, - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv6PacketInfo(pktinfo) => { - mem::size_of_val(pktinfo) - }, - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvIf(dl) => { - mem::size_of_val(dl) - }, - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvDstAddr(inaddr) => { - mem::size_of_val(inaddr) - }, - ControlMessage::Unknown(UnknownCmsg(_, bytes)) => { - mem::size_of_val(bytes) - } - } - } - - /// Returns the value to put into the `cmsg_level` field of the header. - fn cmsg_level(&self) -> libc::c_int { - match *self { - ControlMessage::ScmRights(_) => libc::SOL_SOCKET, - #[cfg(any(target_os = "android", target_os = "linux"))] - ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET, - ControlMessage::ScmTimestamp(_) => libc::SOL_SOCKET, - #[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP, - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6, - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvIf(_) => libc::IPPROTO_IP, - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvDstAddr(_) => libc::IPPROTO_IP, - ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_level, - } - } - - /// Returns the value to put into the `cmsg_type` field of the header. - fn cmsg_type(&self) -> libc::c_int { - match *self { - ControlMessage::ScmRights(_) => libc::SCM_RIGHTS, - #[cfg(any(target_os = "android", target_os = "linux"))] - ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS, - ControlMessage::ScmTimestamp(_) => libc::SCM_TIMESTAMP, - #[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO, - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO, - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvIf(_) => libc::IP_RECVIF, - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvDstAddr(_) => libc::IP_RECVDSTADDR, - ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_type, - } - } - - // Unsafe: start and end of buffer must be cmsg_align'd. Updates - // the provided slice; panics if the buffer is too small. - unsafe fn encode_into(&self, buf: &mut [u8]) { - let final_buf = if let ControlMessage::Unknown(ref cmsg) = *self { - let &UnknownCmsg(orig_cmsg, bytes) = cmsg; - - let buf = copy_bytes(orig_cmsg, buf); - - let padlen = cmsg_align(mem::size_of_val(&orig_cmsg)) - - mem::size_of_val(&orig_cmsg); - let buf = pad_bytes(padlen, buf); - - copy_bytes(bytes, buf) - } else { - let cmsg = cmsghdr { - cmsg_len: self.len() as _, - cmsg_level: self.cmsg_level(), - cmsg_type: self.cmsg_type(), - ..mem::zeroed() // zero out platform-dependent padding fields - }; - let buf = copy_bytes(&cmsg, buf); - - let padlen = cmsg_align(mem::size_of_val(&cmsg)) - - mem::size_of_val(&cmsg); - let buf = pad_bytes(padlen, buf); - - match *self { - ControlMessage::ScmRights(fds) => { - copy_bytes(fds, buf) - }, - #[cfg(any(target_os = "android", target_os = "linux"))] - ControlMessage::ScmCredentials(creds) => { - copy_bytes(creds, buf) - }, - ControlMessage::ScmTimestamp(t) => { - copy_bytes(t, buf) - }, - #[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv4PacketInfo(pktinfo) => { - copy_bytes(pktinfo, buf) - }, - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos" - ))] - ControlMessage::Ipv6PacketInfo(pktinfo) => { - copy_bytes(pktinfo, buf) - } - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvIf(dl) => { - copy_bytes(dl, buf) - }, - #[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - ))] - ControlMessage::Ipv4RecvDstAddr(inaddr) => { - copy_bytes(inaddr, buf) - }, - ControlMessage::Unknown(_) => unreachable!(), - } - }; - - let padlen = self.space() - self.len(); - pad_bytes(padlen, final_buf); - } - - /// Decodes a `ControlMessage` from raw bytes. +impl ControlMessageOwned { + /// Decodes a `ControlMessageOwned` from raw bytes. /// /// This is only safe to call if the data is correct for the message type /// specified in the header. Normally, the kernel ensures that this is the /// case. "Correct" in this case includes correct length, alignment and /// actual content. - unsafe fn decode_from(header: &'a cmsghdr, data: &'a [u8]) -> ControlMessage<'a> { + /// + /// Returns `None` if the data may be unaligned. In that case use + /// `ControlMessageOwned::decode_from`. + unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned + { + let p = CMSG_DATA(header); + let len = header as *const _ as usize + header.cmsg_len as usize + - p as usize; match (header.cmsg_level, header.cmsg_type) { (libc::SOL_SOCKET, libc::SCM_RIGHTS) => { - ControlMessage::ScmRights( - slice::from_raw_parts(data.as_ptr() as *const _, - data.len() / mem::size_of::<RawFd>())) + let n = len / mem::size_of::<RawFd>(); + let mut fds = Vec::with_capacity(n); + for i in 0..n { + let fdp = (p as *const RawFd).offset(i as isize); + fds.push(ptr::read_unaligned(fdp)); + } + let cmo = ControlMessageOwned::ScmRights(fds); + cmo }, #[cfg(any(target_os = "android", target_os = "linux"))] (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => { - ControlMessage::ScmCredentials( - &*(data.as_ptr() as *const _) - ) + let cred: libc::ucred = ptr::read_unaligned(p as *const _); + ControlMessageOwned::ScmCredentials(cred) } (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => { - ControlMessage::ScmTimestamp( - &*(data.as_ptr() as *const _)) + let tv: libc::timeval = ptr::read_unaligned(p as *const _); + ControlMessageOwned::ScmTimestamp(TimeVal::from(tv)) }, #[cfg(any( target_os = "android", @@ -835,18 +622,19 @@ impl<'a> ControlMessage<'a> { target_os = "macos" ))] (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => { - ControlMessage::Ipv6PacketInfo( - &*(data.as_ptr() as *const _)) + let info = ptr::read_unaligned(p as *const libc::in6_pktinfo); + ControlMessageOwned::Ipv6PacketInfo(info) } #[cfg(any( target_os = "android", target_os = "ios", target_os = "linux", - target_os = "macos" + target_os = "macos", + target_os = "netbsd", ))] (libc::IPPROTO_IP, libc::IP_PKTINFO) => { - ControlMessage::Ipv4PacketInfo( - &*(data.as_ptr() as *const _)) + let info = ptr::read_unaligned(p as *const libc::in_pktinfo); + ControlMessageOwned::Ipv4PacketInfo(info) } #[cfg(any( target_os = "freebsd", @@ -856,9 +644,9 @@ impl<'a> ControlMessage<'a> { target_os = "openbsd", ))] (libc::IPPROTO_IP, libc::IP_RECVIF) => { - ControlMessage::Ipv4RecvIf( - &*(data.as_ptr() as *const _)) - } + let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl); + ControlMessageOwned::Ipv4RecvIf(dl) + }, #[cfg(any( target_os = "freebsd", target_os = "ios", @@ -867,15 +655,136 @@ impl<'a> ControlMessage<'a> { target_os = "openbsd", ))] (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => { - ControlMessage::Ipv4RecvDstAddr( - &*(data.as_ptr() as *const _)) + let dl = ptr::read_unaligned(p as *const libc::in_addr); + ControlMessageOwned::Ipv4RecvDstAddr(dl) + }, + (_, _) => { + let sl = slice::from_raw_parts(p, len); + let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(&sl[..])); + ControlMessageOwned::Unknown(ucmsg) } + } + } +} - (_, _) => { - ControlMessage::Unknown(UnknownCmsg(header, data)) +/// A type-safe zero-copy wrapper around a single control message, as used wih +/// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not +/// exhaustively pattern-match it. +/// +/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html) +#[allow(missing_debug_implementations)] +pub enum ControlMessage<'a> { + /// A message of type `SCM_RIGHTS`, containing an array of file + /// descriptors passed between processes. + /// + /// See the description in the "Ancillary messages" section of the + /// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html). + /// + /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't + /// recommended since it causes platform-dependent behaviour: It might + /// swallow all but the first `ScmRights` message or fail with `EINVAL`. + /// Instead, you can put all fds to be passed into a single `ScmRights` + /// message. + ScmRights(&'a [RawFd]), + /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of + /// a process connected to the socket. + /// + /// This is similar to the socket option `SO_PEERCRED`, but requires a + /// process to explicitly send its credentials. A process running as root is + /// allowed to specify any credentials, while credentials sent by other + /// processes are verified by the kernel. + /// + /// For further information, please refer to the + /// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page. + // FIXME: When `#[repr(transparent)]` is stable, use it on `UnixCredentials` + // and put that in here instead of a raw ucred. + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmCredentials(&'a libc::ucred), +} + +// An opaque structure used to prevent cmsghdr from being a public type +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct UnknownCmsg(cmsghdr, Vec<u8>); + +impl<'a> ControlMessage<'a> { + /// The value of CMSG_SPACE on this message. + /// Safe because CMSG_SPACE is always safe + fn space(&self) -> usize { + unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize} + } + + /// The value of CMSG_LEN on this message. + /// Safe because CMSG_LEN is always safe + #[cfg(any(target_os = "android", + all(target_os = "linux", not(target_env = "musl"))))] + fn cmsg_len(&self) -> usize { + unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize} + } + + #[cfg(not(any(target_os = "android", + all(target_os = "linux", not(target_env = "musl")))))] + fn cmsg_len(&self) -> libc::c_uint { + unsafe{CMSG_LEN(self.len() as libc::c_uint)} + } + + /// Return a reference to the payload data as a byte pointer + fn data(&self) -> *const u8 { + match self { + &ControlMessage::ScmRights(fds) => { + fds as *const _ as *const u8 + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::ScmCredentials(creds) => { + creds as *const libc::ucred as *const u8 + } + } + } + + /// The size of the payload, excluding its cmsghdr + fn len(&self) -> usize { + match self { + &ControlMessage::ScmRights(fds) => { + mem::size_of_val(fds) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::ScmCredentials(creds) => { + mem::size_of_val(creds) } } } + + /// Returns the value to put into the `cmsg_level` field of the header. + fn cmsg_level(&self) -> libc::c_int { + match self { + &ControlMessage::ScmRights(_) => libc::SOL_SOCKET, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET, + } + } + + /// Returns the value to put into the `cmsg_type` field of the header. + fn cmsg_type(&self) -> libc::c_int { + match self { + &ControlMessage::ScmRights(_) => libc::SCM_RIGHTS, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS, + } + } + + // Unsafe: cmsg must point to a valid cmsghdr with enough space to + // encode self. + unsafe fn encode_into(&self, cmsg: *mut cmsghdr) { + (*cmsg).cmsg_level = self.cmsg_level(); + (*cmsg).cmsg_type = self.cmsg_type(); + (*cmsg).cmsg_len = self.cmsg_len(); + let data = self.data(); + ptr::copy_nonoverlapping( + data, + CMSG_DATA(cmsg), + self.len() + ); + } } @@ -884,51 +793,60 @@ impl<'a> ControlMessage<'a> { /// as with sendto. /// /// Allocates if cmsgs is nonempty. -pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'a>], flags: MsgFlags, addr: Option<&'a SockAddr>) -> Result<usize> { - let mut capacity = 0; - for cmsg in cmsgs { - capacity += cmsg.space(); - } - // Note that the resulting vector claims to have length == capacity, - // so it's presently uninitialized. - let mut cmsg_buffer = unsafe { - let mut vec = Vec::<u8>::with_capacity(capacity); - vec.set_len(capacity); - vec - }; - { - let mut ofs = 0; - for cmsg in cmsgs { - let ptr = &mut cmsg_buffer[ofs..]; - unsafe { - cmsg.encode_into(ptr); - } - ofs += cmsg.space(); - } - } +pub fn sendmsg(fd: RawFd, iov: &[IoVec<&[u8]>], cmsgs: &[ControlMessage], + flags: MsgFlags, addr: Option<&SockAddr>) -> Result<usize> +{ + let capacity = cmsgs.iter().map(|c| c.space()).sum(); + + // First size the buffer needed to hold the cmsgs. It must be zeroed, + // because subsequent code will not clear the padding bytes. + let cmsg_buffer = vec![0u8; capacity]; + // Next encode the sending address, if provided let (name, namelen) = match addr { - Some(addr) => { let (x, y) = unsafe { addr.as_ffi_pair() }; (x as *const _, y) } + Some(addr) => { + let (x, y) = unsafe { addr.as_ffi_pair() }; + (x as *const _, y) + }, None => (ptr::null(), 0), }; + // The message header must be initialized before the individual cmsgs. let cmsg_ptr = if capacity > 0 { - cmsg_buffer.as_ptr() as *const c_void + cmsg_buffer.as_ptr() as *mut c_void } else { - ptr::null() + ptr::null_mut() }; - let mhdr = unsafe { - let mut mhdr: msghdr = mem::uninitialized(); - mhdr.msg_name = name as *mut _; - mhdr.msg_namelen = namelen; - mhdr.msg_iov = iov.as_ptr() as *mut _; - mhdr.msg_iovlen = iov.len() as _; - mhdr.msg_control = cmsg_ptr as *mut _; - mhdr.msg_controllen = capacity as _; - mhdr.msg_flags = 0; + let mhdr = { + // Musl's msghdr has private fields, so this is the only way to + // initialize it. + let mut mhdr: msghdr = unsafe{mem::uninitialized()}; + mhdr.msg_name = name as *mut _; + mhdr.msg_namelen = namelen; + // transmute iov into a mutable pointer. sendmsg doesn't really mutate + // the buffer, but the standard says that it takes a mutable pointer + mhdr.msg_iov = iov.as_ptr() as *mut _; + mhdr.msg_iovlen = iov.len() as _; + mhdr.msg_control = cmsg_ptr; + mhdr.msg_controllen = capacity as _; + mhdr.msg_flags = 0; mhdr }; + + // Encode each cmsg. This must happen after initializing the header because + // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields. + // CMSG_FIRSTHDR is always safe + let mut pmhdr: *mut cmsghdr = unsafe{CMSG_FIRSTHDR(&mhdr as *const msghdr)}; + for cmsg in cmsgs { + assert_ne!(pmhdr, ptr::null_mut()); + // Safe because we know that pmhdr is valid, and we initialized it with + // sufficient space + unsafe { cmsg.encode_into(pmhdr) }; + // Safe because mhdr is valid + pmhdr = unsafe{CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr)}; + } + let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) }; Errno::result(ret).map(|r| r as usize) @@ -937,46 +855,59 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<' /// Receive message in scatter-gather vectors from a socket, and /// optionally receive ancillary data into the provided buffer. /// If no ancillary data is desired, use () as the type parameter. -pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>], cmsg_buffer: Option<&'a mut CmsgSpace<T>>, flags: MsgFlags) -> Result<RecvMsg<'a>> { +/// +/// # References +/// [recvmsg(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html) +pub fn recvmsg<'a>(fd: RawFd, iov: &[IoVec<&mut [u8]>], + cmsg_buffer: Option<&'a mut CmsgBuffer>, + flags: MsgFlags) -> Result<RecvMsg<'a>> +{ let mut address: sockaddr_storage = unsafe { mem::uninitialized() }; let (msg_control, msg_controllen) = match cmsg_buffer { - Some(cmsg_buffer) => (cmsg_buffer as *mut _, mem::size_of_val(cmsg_buffer)), + Some(cmsgspace) => { + let msg_buf = cmsgspace.as_bytes_mut(); + (msg_buf.as_mut_ptr(), msg_buf.len()) + }, None => (ptr::null_mut(), 0), }; - let mut mhdr = unsafe { - let mut mhdr: msghdr = mem::uninitialized(); - mhdr.msg_name = &mut address as *mut _ as *mut _; - mhdr.msg_namelen = mem::size_of::<sockaddr_storage>() as socklen_t; - mhdr.msg_iov = iov.as_ptr() as *mut _; - mhdr.msg_iovlen = iov.len() as _; - mhdr.msg_control = msg_control as *mut _; - mhdr.msg_controllen = msg_controllen as _; - mhdr.msg_flags = 0; + let mut mhdr = { + // Musl's msghdr has private fields, so this is the only way to + // initialize it. + let mut mhdr: msghdr = unsafe{mem::uninitialized()}; + mhdr.msg_name = &mut address as *mut sockaddr_storage as *mut c_void; + mhdr.msg_namelen = mem::size_of::<sockaddr_storage>() as socklen_t; + mhdr.msg_iov = iov.as_ptr() as *mut iovec; + mhdr.msg_iovlen = iov.len() as _; + mhdr.msg_control = msg_control as *mut c_void; + mhdr.msg_controllen = msg_controllen as _; + mhdr.msg_flags = 0; mhdr }; + let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) }; - let cmsg_buffer = if msg_controllen > 0 { - // got control message(s) - debug_assert!(!mhdr.msg_control.is_null()); - unsafe { - // Safe: The pointer is not null and the length is correct as part of `recvmsg`s - // contract. - slice::from_raw_parts(mhdr.msg_control as *const u8, - mhdr.msg_controllen as usize) - } - } else { - // No control message, create an empty buffer to avoid creating a slice from a null pointer - &[] - }; + Errno::result(ret).map(|_| { + let cmsghdr = unsafe { + if mhdr.msg_controllen > 0 { + // got control message(s) + debug_assert!(!mhdr.msg_control.is_null()); + debug_assert!(msg_controllen >= mhdr.msg_controllen as usize); + CMSG_FIRSTHDR(&mhdr as *const msghdr) + } else { + ptr::null() + }.as_ref() + }; - Ok(unsafe { RecvMsg { - bytes: Errno::result(ret)? as usize, - cmsg_buffer, - address: sockaddr_storage_to_addr(&address, - mhdr.msg_namelen as usize).ok(), - flags: MsgFlags::from_bits_truncate(mhdr.msg_flags), - } }) + let address = unsafe { + sockaddr_storage_to_addr(&address, mhdr.msg_namelen as usize).ok() + }; + RecvMsg { + cmsghdr, + address, + flags: MsgFlags::from_bits_truncate(mhdr.msg_flags), + mhdr, + } + }) } diff --git a/src/sys/time.rs b/src/sys/time.rs index 9671f531..4bd3b780 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,4 +1,5 @@ use std::{cmp, fmt, ops}; +use std::convert::From; use libc::{c_long, timespec, timeval}; pub use libc::{time_t, suseconds_t}; @@ -467,6 +468,12 @@ impl fmt::Display for TimeVal { } } +impl From<timeval> for TimeVal { + fn from(tv: timeval) -> Self { + TimeVal(tv) + } +} + #[inline] fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { (div_floor_64(this, other), mod_floor_64(this, other)) |