diff --git a/CHANGELOG.md b/CHANGELOG.md index 0106966ac6..ec74eb270d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,25 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#1002](https://github.com/nix-rust/nix/pull/1002)) ### Changed - `PollFd` event flags renamed to `PollFlags` ([#1024](https://github.com/nix-rust/nix/pull/1024/)) +- `recvmsg` now returns an Iterator over `ControlMessageOwned` objects rather + than `ControlMessage` objects. This is sadly not backwards-compatible. Fix + code like this: + ```rust + if let ControlMessage::ScmRights(&fds) = cmsg { + ``` + + By replacing it with code like this: + ```rust + if let ControlMessageOwned::ScmRights(fds) = cmsg { + ``` + ([#1020](https://github.com/nix-rust/nix/pull/1020)) +- Replaced `CmsgSpace` with the `cmsg_space` macro. + ([#1020](https://github.com/nix-rust/nix/pull/1020)) + ### Fixed +- Fixed multiple bugs when using `sendmsg` and `recvmsg` with ancillary control messages + ([#1020](https://github.com/nix-rust/nix/pull/1020)) + ### Removed ## [0.13.0] - 2019-01-15 diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 64d2fc12a0..8000991773 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::::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 { impl CmsgSpace { /// Create a CmsgSpace. 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 CmsgBuffer for CmsgSpace { + 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 as *mut u8, + mem::size_of::()) + } + } +} + +impl CmsgBuffer for Vec { + 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, 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> { - 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::()].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::())..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 { + 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), + /// 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 = 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::() - 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::()) + 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::())) + let n = len / mem::size_of::(); + 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::::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); + +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 { - 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::::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 +{ + 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>, flags: MsgFlags) -> Result> { +/// +/// # 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> +{ 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::() 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::() 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 9671f5310a..4bd3b7808d 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 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)) diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 2cc3475f75..5d55c87dc5 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -117,13 +117,31 @@ pub fn test_socketpair() { assert_eq!(&buf[..], b"hello"); } +// Test error handling of our recvmsg wrapper +#[test] +pub fn test_recvmsg_ebadf() { + use nix::Error; + use nix::errno::Errno; + use nix::sys::socket::{MsgFlags, recvmsg}; + use nix::sys::uio::IoVec; + + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let fd = -1; // Bad file descriptor + let r = recvmsg(fd, &iov, None, MsgFlags::empty()); + assert_eq!(r.err().unwrap(), Error::Sys(Errno::EBADF)); +} + +// Disable the test on emulated platforms due to a bug in QEMU versions < +// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808 +#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)] #[test] pub fn test_scm_rights() { use nix::sys::uio::IoVec; use nix::unistd::{pipe, read, write, close}; use nix::sys::socket::{socketpair, sendmsg, recvmsg, AddressFamily, SockType, SockFlag, - ControlMessage, CmsgSpace, MsgFlags}; + ControlMessage, ControlMessageOwned, MsgFlags}; let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) .unwrap(); @@ -142,11 +160,11 @@ pub fn test_scm_rights() { { let mut buf = [0u8; 5]; let iov = [IoVec::from_mut_slice(&mut buf[..])]; - let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new(); + let mut cmsgspace = cmsg_space!([RawFd; 1]); let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); for cmsg in msg.cmsgs() { - if let ControlMessage::ScmRights(fd) = cmsg { + if let ControlMessageOwned::ScmRights(fd) = cmsg { assert_eq!(received_r, None); assert_eq!(fd.len(), 1); received_r = Some(fd[0]); @@ -169,12 +187,16 @@ pub fn test_scm_rights() { } /// Tests that passing multiple fds using a single `ControlMessage` works. +// Disable the test on emulated platforms due to a bug in QEMU versions < +// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808 +#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)] #[test] fn test_scm_rights_single_cmsg_multiple_fds() { use std::os::unix::net::UnixDatagram; use std::os::unix::io::{RawFd, AsRawFd}; use std::thread; - use nix::sys::socket::{CmsgSpace, ControlMessage, MsgFlags, sendmsg, recvmsg}; + use nix::sys::socket::{ControlMessage, ControlMessageOwned, MsgFlags, + sendmsg, recvmsg}; use nix::sys::uio::IoVec; use libc; @@ -182,7 +204,7 @@ fn test_scm_rights_single_cmsg_multiple_fds() { let thread = thread::spawn(move || { let mut buf = [0u8; 8]; let iovec = [IoVec::from_mut_slice(&mut buf)]; - let mut space = CmsgSpace::<[RawFd; 2]>::new(); + let mut space = cmsg_space!([RawFd; 2]); let msg = recvmsg( receive.as_raw_fd(), &iovec, @@ -193,7 +215,7 @@ fn test_scm_rights_single_cmsg_multiple_fds() { let mut cmsgs = msg.cmsgs(); match cmsgs.next() { - Some(ControlMessage::ScmRights(fds)) => { + Some(ControlMessageOwned::ScmRights(fds)) => { assert_eq!(fds.len(), 2, "unexpected fd count (expected 2 fds, got {})", fds.len()); @@ -222,8 +244,7 @@ pub fn test_sendmsg_empty_cmsgs() { use nix::sys::uio::IoVec; use nix::unistd::close; use nix::sys::socket::{socketpair, sendmsg, recvmsg, - AddressFamily, SockType, SockFlag, - CmsgSpace, MsgFlags}; + AddressFamily, SockType, SockFlag, MsgFlags}; let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) .unwrap(); @@ -237,7 +258,7 @@ pub fn test_sendmsg_empty_cmsgs() { { let mut buf = [0u8; 5]; let iov = [IoVec::from_mut_slice(&mut buf[..])]; - let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new(); + let mut cmsgspace = cmsg_space!([RawFd; 1]); let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); for _ in msg.cmsgs() { @@ -256,7 +277,7 @@ fn test_scm_credentials() { use nix::unistd::{close, getpid, getuid, getgid}; use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, AddressFamily, SockType, SockFlag, - ControlMessage, CmsgSpace, MsgFlags}; + ControlMessage, ControlMessageOwned, MsgFlags}; use nix::sys::socket::sockopt::PassCred; let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) @@ -278,17 +299,17 @@ fn test_scm_credentials() { { let mut buf = [0u8; 5]; let iov = [IoVec::from_mut_slice(&mut buf[..])]; - let mut cmsgspace: CmsgSpace = CmsgSpace::new(); + let mut cmsgspace = cmsg_space!(libc::ucred); let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); let mut received_cred = None; for cmsg in msg.cmsgs() { - if let ControlMessage::ScmCredentials(cred) = cmsg { + if let ControlMessageOwned::ScmCredentials(cred) = cmsg { assert!(received_cred.is_none()); assert_eq!(cred.pid, getpid().as_raw()); assert_eq!(cred.uid, getuid().as_raw()); assert_eq!(cred.gid, getgid().as_raw()); - received_cred = Some(*cred); + received_cred = Some(cred); } else { panic!("unexpected cmsg"); } @@ -307,33 +328,32 @@ fn test_scm_credentials() { #[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)] #[test] fn test_scm_credentials_and_rights() { - use nix::sys::socket::CmsgSpace; use libc; - test_impl_scm_credentials_and_rights(CmsgSpace::<(libc::ucred, CmsgSpace)>::new()); + let space = cmsg_space!(libc::ucred, RawFd); + test_impl_scm_credentials_and_rights(space); } -/// Ensure that passing a `CmsgSpace` with too much space for the received -/// messages still works. +/// Ensure that passing a an oversized control message buffer to recvmsg +/// still works. #[cfg(any(target_os = "android", target_os = "linux"))] // qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86 // see https://bugs.launchpad.net/qemu/+bug/1781280 #[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)] #[test] fn test_too_large_cmsgspace() { - use nix::sys::socket::CmsgSpace; - - test_impl_scm_credentials_and_rights(CmsgSpace::<[u8; 1024]>::new()); + let space = vec![0u8; 1024]; + test_impl_scm_credentials_and_rights(space); } #[cfg(any(target_os = "android", target_os = "linux"))] -fn test_impl_scm_credentials_and_rights(mut space: ::nix::sys::socket::CmsgSpace) { +fn test_impl_scm_credentials_and_rights(mut space: Vec) { use libc; use nix::sys::uio::IoVec; use nix::unistd::{pipe, read, write, close, getpid, getuid, getgid}; use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, AddressFamily, SockType, SockFlag, - ControlMessage, MsgFlags}; + ControlMessage, ControlMessageOwned, MsgFlags}; use nix::sys::socket::sockopt::PassCred; let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) @@ -370,17 +390,17 @@ fn test_impl_scm_credentials_and_rights(mut space: ::nix::sys::socket::CmsgSp for cmsg in msg.cmsgs() { match cmsg { - ControlMessage::ScmRights(fds) => { + ControlMessageOwned::ScmRights(fds) => { assert_eq!(received_r, None, "already received fd"); assert_eq!(fds.len(), 1); received_r = Some(fds[0]); } - ControlMessage::ScmCredentials(cred) => { + ControlMessageOwned::ScmCredentials(cred) => { assert!(received_cred.is_none()); assert_eq!(cred.pid, getpid().as_raw()); assert_eq!(cred.uid, getuid().as_raw()); assert_eq!(cred.gid, getgid().as_raw()); - received_cred = Some(*cred); + received_cred = Some(cred); } _ => panic!("unexpected cmsg"), } @@ -522,7 +542,7 @@ pub fn test_recv_ipv4pktinfo() { use nix::sys::socket::sockopt::Ipv4PacketInfo; use nix::sys::socket::{bind, SockFlag, SockType}; use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags}; + use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; use nix::sys::uio::IoVec; use nix::net::if_::*; @@ -558,7 +578,7 @@ pub fn test_recv_ipv4pktinfo() { { let mut buf = [0u8; 8]; let iovec = [IoVec::from_mut_slice(&mut buf)]; - let mut space = CmsgSpace::::new(); + let mut space = cmsg_space!(libc::in_pktinfo); let msg = recvmsg( receive, &iovec, @@ -572,7 +592,7 @@ pub fn test_recv_ipv4pktinfo() { let mut cmsgs = msg.cmsgs(); match cmsgs.next() { - Some(ControlMessage::Ipv4PacketInfo(pktinfo)) => { + Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) => { let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); assert_eq!( pktinfo.ipi_ifindex as libc::c_uint, @@ -612,7 +632,7 @@ pub fn test_recvif() { use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr}; use nix::sys::socket::{bind, SockFlag, SockType}; use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr}; - use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags}; + use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; use nix::sys::uio::IoVec; let lo_ifaddr = loopback_address(AddressFamily::Inet); @@ -648,7 +668,7 @@ pub fn test_recvif() { { let mut buf = [0u8; 8]; let iovec = [IoVec::from_mut_slice(&mut buf)]; - let mut space = CmsgSpace::<(libc::sockaddr_dl, CmsgSpace)>::new(); + let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr); let msg = recvmsg( receive, &iovec, @@ -665,7 +685,7 @@ pub fn test_recvif() { let mut rx_recvdstaddr = false; for cmsg in msg.cmsgs() { match cmsg { - ControlMessage::Ipv4RecvIf(dl) => { + ControlMessageOwned::Ipv4RecvIf(dl) => { rx_recvif = true; let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); assert_eq!( @@ -676,7 +696,7 @@ pub fn test_recvif() { dl.sdl_index ); }, - ControlMessage::Ipv4RecvDstAddr(addr) => { + ControlMessageOwned::Ipv4RecvDstAddr(addr) => { rx_recvdstaddr = true; if let SockAddr::Inet(InetAddr::V4(a)) = lo { assert_eq!(a.sin_addr.s_addr, @@ -722,7 +742,7 @@ pub fn test_recv_ipv6pktinfo() { use nix::sys::socket::sockopt::Ipv6RecvPacketInfo; use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType}; use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags}; + use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; use nix::sys::uio::IoVec; let lo_ifaddr = loopback_address(AddressFamily::Inet6); @@ -757,7 +777,7 @@ pub fn test_recv_ipv6pktinfo() { { let mut buf = [0u8; 8]; let iovec = [IoVec::from_mut_slice(&mut buf)]; - let mut space = CmsgSpace::::new(); + let mut space = cmsg_space!(libc::in6_pktinfo); let msg = recvmsg( receive, &iovec, @@ -771,7 +791,7 @@ pub fn test_recv_ipv6pktinfo() { let mut cmsgs = msg.cmsgs(); match cmsgs.next() { - Some(ControlMessage::Ipv6PacketInfo(pktinfo)) => { + Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) => { let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); assert_eq!( pktinfo.ipi6_ifindex,