use libc::{c_int, size_t, ssize_t, c_uint, c_void};
use std::io;
use std::marker::PhantomData;
use std::mem::{size_of, size_of_val};
use std::mem::transmute;
use std::os::unix::io::AsRawFd;
use std::ptr;
pub mod raw {
use libc::{c_int, ssize_t, c_void};
#[cfg(target_arch="x86_64")]
pub const MSG_CMSG_CLOEXEC: c_int = 0x40000000;
extern {
pub fn recvmsg(sockfd: c_int, msg: *mut c_void, flags: c_int) -> ssize_t;
pub fn sendmsg(sockfd: c_int, msg: *const c_void, flags: c_int) -> ssize_t;
}
}
#[cfg(target_arch="x86_64")]
pub struct Iovec {
pub iov_base: *const c_void,
pub iov_len: size_t,
}
#[cfg(target_arch="x86_64")]
pub type Socklen = c_uint;
#[cfg(target_arch="x86_64")]
#[repr(C)]
pub struct Msghdr<'a, 'b, 'c, T:'c> {
msg_name: *mut u8,
_msg_name: PhantomData<&'a mut [u8]>,
msg_namelen: Socklen,
msg_iov: *mut Iovec,
_msg_iov: PhantomData<&'b mut Iovec>,
msg_iovlen: size_t,
msg_control: *mut Cmsghdr<T>,
_msg_control: PhantomData<&'c mut Cmsghdr<T>>,
msg_controllen: size_t,
msg_flags: c_int,
}
impl<'a, 'b, 'c, T> Msghdr<'a, 'b,'c, T> {
pub fn new(addr: Option<&'a mut [u8]>, iovv: &'b mut Vec<Iovec>, ctrl: &'c mut Cmsghdr<T>, flags: Option<c_int>) -> Msghdr<'a, 'b, 'c, T> {
let msg_controllen = size_of_val(ctrl) as size_t;
let (msg_name, msg_namelen) = match addr {
Some(a) => (a.as_mut_ptr(), a.len() as Socklen),
None => (ptr::null_mut(), 0),
};
Msghdr {
msg_name: msg_name,
_msg_name: PhantomData,
msg_namelen: msg_namelen,
msg_iov: iovv.as_mut_ptr(),
_msg_iov: PhantomData,
msg_iovlen: iovv.len() as size_t,
msg_control: unsafe { transmute(ctrl) },
_msg_control: PhantomData,
msg_controllen: msg_controllen,
msg_flags: match flags {
Some(f) => f,
None => 0,
},
}
}
}
#[allow(dead_code)]
#[repr(C)]
pub enum Scm {
Rights = 0x01,
Credentials = 0x02,
Security = 0x03
}
#[cfg(target_arch="x86_64")]
#[repr(C)]
pub struct Cmsghdr<T> {
cmsg_len: size_t,
cmsg_level: c_int,
cmsg_type: Scm,
__cmsg_data: T,
}
pub static SOL_SOCKET: c_int = 1;
impl<T> Cmsghdr<T> {
pub fn new(level: c_int, scm: Scm, data: T) -> Cmsghdr<T> {
assert_eq!(size_of::<T>() % size_of::<size_t>(), 0);
assert_eq!((size_of::<Cmsghdr<T>>() - size_of::<T>()) % size_of::<size_t>(), 0);
Cmsghdr {
cmsg_len: size_of::<Cmsghdr<T>>() as size_t,
cmsg_level: level,
cmsg_type: scm,
__cmsg_data: data,
}
}
}
#[allow(unused_mut)]
pub fn recvmsg<T>(sockfd: &mut AsRawFd, iov_len: usize, mut cmsg_data: T) -> io::Result<(ssize_t, Vec<u8>, T)> {
let mut iov_data = Vec::with_capacity(iov_len);
let mut iov_data_ptr = iov_data.as_mut_ptr();
let mut iovv = vec!(Iovec {
iov_base: unsafe { transmute(iov_data_ptr) },
iov_len: iov_len as size_t,
});
let mut ctrl = Cmsghdr::new(SOL_SOCKET, Scm::Rights, cmsg_data);
let size = {
let mut msg = Msghdr::new(None, &mut iovv, &mut ctrl, None);
let size = match unsafe { raw::recvmsg(sockfd.as_raw_fd(), transmute(&mut msg), raw::MSG_CMSG_CLOEXEC) } {
-1 => return Err(io::Error::last_os_error()),
s => s,
};
unsafe { iov_data.set_len(msg.msg_iovlen as usize) };
if msg.msg_controllen != size_of::<Cmsghdr<T>>() as size_t {
return Err(io::Error::new(io::ErrorKind::Other, format!("Short write: {}", msg.msg_controllen).as_str()));
}
size
};
if ctrl.cmsg_len != size_of::<Cmsghdr<T>>() as size_t {
return Err(io::Error::new(io::ErrorKind::Other, format!("Short write: {}", ctrl.cmsg_len).as_str()));
}
Ok((size, iov_data, ctrl.__cmsg_data))
}
pub fn sendmsg<T>(sockfd: &mut AsRawFd, msg: &Msghdr<T>) -> io::Result<ssize_t> {
match unsafe { raw::sendmsg(sockfd.as_raw_fd(), transmute(msg), 0) } {
-1 => Err(io::Error::last_os_error()),
s => Ok(s),
}
}