Skip to content

Commit

Permalink
Fix struct msghdr types on non-Linux platforms.
Browse files Browse the repository at this point in the history
  • Loading branch information
kinetiknz committed Jul 11, 2017
1 parent 17f87d4 commit bc98b99
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#623](https://github.com/nix-rust/nix/pull/623))
- Multiple constants related to the termios API have now been properly defined for
all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527))
- Fixed field types of `sys::socket::ffi::{msghdr, cmsghdr}` on
non-Linux platforms. ([#](https://...))

## [0.8.1] 2017-04-16

Expand Down
4 changes: 2 additions & 2 deletions nix-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub fn assert_size_of<T>(name: &str) {
assert!(expect > 0, "undefined type {}", name);

if mem::size_of::<T>() != expect {
panic!("incorrectly sized type; expect={}; actual={}",
expect, mem::size_of::<T>());
panic!("incorrectly sized type; type={} expect={}; actual={}",
name, expect, mem::size_of::<T>());
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions nix-test/src/sizes.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ size_of(const char* type) {

// sys/socket
SIZE_OF_S(sockaddr_storage);
SIZE_OF_S(msghdr);
SIZE_OF_S(cmsghdr);

// sys/uio
SIZE_OF_S(iovec);
Expand Down
26 changes: 20 additions & 6 deletions src/sys/socket/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

pub use libc::{socket, listen, bind, accept, connect, setsockopt, sendto, recvfrom, getsockname, getpeername, recv, send};

use libc::{c_int, c_void, socklen_t, size_t, ssize_t};
use libc::{c_int, c_void, socklen_t, ssize_t};

#[cfg(not(target_os = "macos"))]
use libc::size_t;

#[cfg(target_os = "macos")]
use libc::c_uint;
Expand All @@ -18,10 +21,22 @@ pub type type_of_cmsg_len = socklen_t;

// OSX always aligns struct cmsghdr as if it were a 32-bit OS
#[cfg(target_os = "macos")]
pub type type_of_cmsg_data = c_uint;
pub type align_of_cmsg_data = c_uint;

#[cfg(not(target_os = "macos"))]
pub type type_of_cmsg_data = size_t;
pub type align_of_cmsg_data = size_t;

#[cfg(target_os = "linux")]
pub type type_of_msgiov_len = size_t;

#[cfg(not(target_os = "linux"))]
pub type type_of_msgiov_len = c_int;

#[cfg(target_os = "linux")]
pub type type_of_msg_controllen = size_t;

#[cfg(not(target_os = "linux"))]
pub type type_of_msg_controllen = socklen_t;

// Private because we don't expose any external functions that operate
// directly on this type; we just use it internally at FFI boundaries.
Expand All @@ -33,9 +48,9 @@ pub struct msghdr<'a> {
pub msg_name: *const c_void,
pub msg_namelen: socklen_t,
pub msg_iov: *const IoVec<&'a [u8]>,
pub msg_iovlen: size_t,
pub msg_iovlen: type_of_msgiov_len,
pub msg_control: *const c_void,
pub msg_controllen: size_t,
pub msg_controllen: type_of_msg_controllen,
pub msg_flags: c_int,
}

Expand All @@ -47,7 +62,6 @@ pub struct cmsghdr {
pub cmsg_len: type_of_cmsg_len,
pub cmsg_level: c_int,
pub cmsg_type: c_int,
pub cmsg_data: [type_of_cmsg_data; 0]
}

extern {
Expand Down
32 changes: 19 additions & 13 deletions src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ unsafe fn copy_bytes<'a, 'b, T: ?Sized>(src: &T, dst: &'a mut &'b mut [u8]) {
}


use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len, type_of_cmsg_data};
use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len, align_of_cmsg_data,
type_of_msgiov_len, type_of_msg_controllen};

/// 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
Expand All @@ -95,8 +96,11 @@ use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len, type_of_cmsg_data};
/// To make room for multiple messages, nest the type parameter with
/// tuples, e.g.
/// `let cmsg: CmsgSpace<([RawFd; 3], CmsgSpace<[RawFd; 2]>)> = CmsgSpace::new();`
#[repr(C)]
pub struct CmsgSpace<T> {
_hdr: cmsghdr,
#[cfg(not(target_os = "macos"))]
_pad: [align_of_cmsg_data; 0],
_data: T,
}

Expand Down Expand Up @@ -156,17 +160,18 @@ impl<'a> Iterator for CmsgIterator<'a> {
}
self.0 = &buf[cmsg_align(cmsg_len)..];

let cmsg_data = &buf[cmsg_align(sizeof_cmsghdr)..];
match (cmsg.cmsg_level, cmsg.cmsg_type) {
(SOL_SOCKET, SCM_RIGHTS) => unsafe {
Some(ControlMessage::ScmRights(
slice::from_raw_parts(
&cmsg.cmsg_data as *const _ as *const _, 1)))
cmsg_data.as_ptr() as *const _, 1)))
},
(_, _) => unsafe {
Some(ControlMessage::Unknown(UnknownCmsg(
&cmsg,
slice::from_raw_parts(
&cmsg.cmsg_data as *const _ as *const _,
cmsg_data.as_ptr() as *const _,
len))))
}
}
Expand All @@ -191,7 +196,7 @@ pub enum ControlMessage<'a> {
pub struct UnknownCmsg<'a>(&'a cmsghdr, &'a [u8]);

fn cmsg_align(len: usize) -> usize {
let align_bytes = mem::size_of::<type_of_cmsg_data>() - 1;
let align_bytes = mem::size_of::<align_of_cmsg_data>() - 1;
(len + align_bytes) & !align_bytes
}

Expand All @@ -213,8 +218,8 @@ impl<'a> ControlMessage<'a> {
}
}

// Unsafe: start and end of buffer must be size_t-aligned (that is,
// cmsg_align'd). Updates the provided slice; panics if the buffer
// 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<'b>(&self, buf: &mut &'b mut [u8]) {
match *self {
Expand All @@ -223,7 +228,6 @@ impl<'a> ControlMessage<'a> {
cmsg_len: self.len() as type_of_cmsg_len,
cmsg_level: SOL_SOCKET,
cmsg_type: SCM_RIGHTS,
cmsg_data: [],
};
copy_bytes(&cmsg, buf);

Expand Down Expand Up @@ -287,9 +291,9 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
msg_name: name as *const c_void,
msg_namelen: namelen,
msg_iov: iov.as_ptr(),
msg_iovlen: iov.len() as size_t,
msg_iovlen: iov.len() as type_of_msgiov_len,
msg_control: cmsg_ptr,
msg_controllen: capacity as size_t,
msg_controllen: capacity as type_of_msg_controllen,
msg_flags: 0,
};
let ret = unsafe { ffi::sendmsg(fd, &mhdr, flags.bits()) };
Expand All @@ -310,9 +314,9 @@ pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>], cmsg_buffer: Option<&
msg_name: &mut address as *const _ as *const c_void,
msg_namelen: mem::size_of::<sockaddr_storage>() as socklen_t,
msg_iov: iov.as_ptr() as *const IoVec<&[u8]>, // safe cast to add const-ness
msg_iovlen: iov.len() as size_t,
msg_iovlen: iov.len() as type_of_msgiov_len,
msg_control: msg_control as *const c_void,
msg_controllen: msg_controllen as size_t,
msg_controllen: msg_controllen as type_of_msg_controllen,
msg_flags: 0,
};
let ret = unsafe { ffi::recvmsg(fd, &mut mhdr, flags.bits()) };
Expand Down Expand Up @@ -692,6 +696,8 @@ pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {

#[test]
pub fn test_struct_sizes() {
use nixtest;
nixtest::assert_size_of::<sockaddr_storage>("sockaddr_storage");
use nixtest::assert_size_of;
assert_size_of::<sockaddr_storage>("sockaddr_storage");
assert_size_of::<msghdr>("msghdr");
assert_size_of::<cmsghdr>("cmsghdr");
}

0 comments on commit bc98b99

Please sign in to comment.