use cfg_if::cfg_if;
use super::{GetSockOpt, SetSockOpt};
use crate::Result;
use crate::errno::Errno;
use crate::sys::time::TimeVal;
use libc::{self, c_int, c_void, socklen_t};
use std::convert::TryFrom;
use std::mem::{
self,
MaybeUninit
};
use std::os::unix::io::RawFd;
use std::ffi::{OsStr, OsString};
#[cfg(target_family = "unix")]
use std::os::unix::ffi::OsStrExt;
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
const TCP_CA_NAME_MAX: usize = 16;
macro_rules! setsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
impl SetSockOpt for $name {
type Val = $ty;
fn set(&self, fd: RawFd, val: &$ty) -> Result<()> {
unsafe {
let setter: $setter = Set::new(val);
let res = libc::setsockopt(fd, $level, $flag,
setter.ffi_ptr(),
setter.ffi_len());
Errno::result(res).map(drop)
}
}
}
}
}
macro_rules! getsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
impl GetSockOpt for $name {
type Val = $ty;
fn get(&self, fd: RawFd) -> Result<$ty> {
unsafe {
let mut getter: $getter = Get::uninit();
let res = libc::getsockopt(fd, $level, $flag,
getter.ffi_ptr(),
getter.ffi_len());
Errno::result(res)?;
match <$ty>::try_from(getter.assume_init()) {
Err(_) => Err(Errno::EINVAL),
Ok(r) => Ok(r)
}
}
}
}
}
}
#[allow(unknown_lints)]
#[allow(unused_macro_rules)]
macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, bool, GetBool);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, usize, GetUsize);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, bool, SetBool);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, usize, SetUsize);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, bool, GetBool, SetBool);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, u8, GetU8, SetU8);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, usize, GetUsize, SetUsize);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
OsString<$array:ty>) =>
{
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, OsString, GetOsString<$array>,
SetOsString);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
$getter:ty) =>
{
$(#[$attr])*
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
getsockopt_impl!($name, $level, $flag, $ty, $getter);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
$setter:ty) =>
{
$(#[$attr])*
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
setsockopt_impl!($name, $level, $flag, $ty, $setter);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty,
$getter:ty, $setter:ty) =>
{
$(#[$attr])*
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
setsockopt_impl!($name, $level, $flag, $ty, $setter);
getsockopt_impl!($name, $level, $flag, $ty, $getter);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, $ty, GetStruct<$ty>,
SetStruct<$ty>);
};
}
sockopt_impl!(
ReuseAddr, Both, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool
);
#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
sockopt_impl!(
ReusePort, Both, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
TcpNoDelay, Both, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool);
sockopt_impl!(
Linger, Both, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
IpAddMembership, SetOnly, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP,
super::IpMembershipRequest);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
IpDropMembership, SetOnly, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP,
super::IpMembershipRequest);
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
} else if #[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"))] {
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
}
}
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
IpMulticastTtl, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
IpMulticastLoop, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool);
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
IpFreebind, Both, libc::IPPROTO_IP, libc::IP_FREEBIND, bool);
sockopt_impl!(
ReceiveTimeout, Both, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal);
sockopt_impl!(
SendTimeout, Both, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal);
sockopt_impl!(
Broadcast, Both, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
sockopt_impl!(
OobInline, Both, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
sockopt_impl!(
SocketError, GetOnly, libc::SOL_SOCKET, libc::SO_ERROR, i32);
sockopt_impl!(
DontRoute, Both, libc::SOL_SOCKET, libc::SO_DONTROUTE, bool);
sockopt_impl!(
KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "ios"
))]
sockopt_impl!(
LocalPeerCred, GetOnly, 0, libc::LOCAL_PEERCRED, super::XuCred);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
PeerCredentials, GetOnly, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
#[cfg(any(target_os = "ios",
target_os = "macos"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
TcpKeepAlive, Both, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32);
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
TcpKeepIdle, Both, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32);
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
sockopt_impl!(
TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
} else {
sockopt_impl!(
TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
}
}
#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
TcpKeepCount, Both, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32);
#[cfg(any(target_os = "android",
target_os = "fuchsia",
target_os = "linux"))]
sockopt_impl!(
#[allow(missing_docs)]
TcpRepair, Both, libc::IPPROTO_TCP, libc::TCP_REPAIR, u32);
#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
TcpKeepInterval, Both, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32);
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
TcpUserTimeout, Both, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT, u32);
sockopt_impl!(
RcvBuf, Both, libc::SOL_SOCKET, libc::SO_RCVBUF, usize);
sockopt_impl!(
SndBuf, Both, libc::SOL_SOCKET, libc::SO_SNDBUF, usize);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
RcvBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
SndBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize);
sockopt_impl!(
SockType, GetOnly, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType, GetStruct<i32>);
sockopt_impl!(
AcceptConn, GetOnly, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
BindToDevice, Both, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
#[allow(missing_docs)]
OriginalDst, GetOnly, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
#[allow(missing_docs)]
Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6);
#[cfg(any(target_os = "linux"))]
sockopt_impl!(
Timestamping, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPING, super::TimestampingFlag);
#[cfg(not(target_os = "haiku"))]
sockopt_impl!(
ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
#[cfg(all(target_os = "linux"))]
sockopt_impl!(
ReceiveTimestampns, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
IpTransparent, Both, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
#[cfg(target_os = "openbsd")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
BindAny, Both, libc::SOL_SOCKET, libc::SO_BINDANY, bool);
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
BindAny, Both, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
#[cfg(target_os = "linux")]
sockopt_impl!(
Mark, Both, libc::SOL_SOCKET, libc::SO_MARK, u32);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
PassCred, Both, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
TcpCongestion, Both, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>);
#[cfg(any(
target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4PacketInfo, Both, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6RecvPacketInfo, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvIf, Both, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4OrigDstAddr, Both, libc::IPPROTO_IP, libc::IP_ORIGDSTADDR, bool);
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
#[allow(missing_docs)]
UdpGsoSegment, Both, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
#[allow(missing_docs)]
UdpGroSegment, Both, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
#[cfg(target_os = "linux")]
sockopt_impl!(
TxTime, Both, libc::SOL_SOCKET, libc::SO_TXTIME, libc::sock_txtime);
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
sockopt_impl!(
RxqOvfl, Both, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6V6Only, Both, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, bool);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
Ipv4RecvErr, Both, libc::IPPROTO_IP, libc::IP_RECVERR, bool);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(
Ipv6RecvErr, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVERR, bool);
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
sockopt_impl!(
Ipv4Ttl, Both, libc::IPPROTO_IP, libc::IP_TTL, libc::c_int);
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
sockopt_impl!(
Ipv6Ttl, Both, libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS, libc::c_int);
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6OrigDstAddr, Both, libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR, bool);
#[cfg(any(target_os = "ios", target_os = "macos"))]
sockopt_impl!(
IpDontFrag, Both, libc::IPPROTO_IP, libc::IP_DONTFRAG, bool);
#[cfg(any(
target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
))]
sockopt_impl!(
Ipv6DontFrag, Both, libc::IPPROTO_IPV6, libc::IPV6_DONTFRAG, bool);
#[allow(missing_docs)]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[derive(Copy, Clone, Debug)]
pub struct AlgSetAeadAuthSize;
#[cfg(any(target_os = "android", target_os = "linux"))]
impl SetSockOpt for AlgSetAeadAuthSize {
type Val = usize;
fn set(&self, fd: RawFd, val: &usize) -> Result<()> {
unsafe {
let res = libc::setsockopt(fd,
libc::SOL_ALG,
libc::ALG_SET_AEAD_AUTHSIZE,
::std::ptr::null(),
*val as libc::socklen_t);
Errno::result(res).map(drop)
}
}
}
#[allow(missing_docs)]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[derive(Clone, Debug)]
pub struct AlgSetKey<T>(::std::marker::PhantomData<T>);
#[cfg(any(target_os = "android", target_os = "linux"))]
impl<T> Default for AlgSetKey<T> {
fn default() -> Self {
AlgSetKey(Default::default())
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl<T> SetSockOpt for AlgSetKey<T> where T: AsRef<[u8]> + Clone {
type Val = T;
fn set(&self, fd: RawFd, val: &T) -> Result<()> {
unsafe {
let res = libc::setsockopt(fd,
libc::SOL_ALG,
libc::ALG_SET_KEY,
val.as_ref().as_ptr() as *const _,
val.as_ref().len() as libc::socklen_t);
Errno::result(res).map(drop)
}
}
}
trait Get<T> {
fn uninit() -> Self;
fn ffi_ptr(&mut self) -> *mut c_void;
fn ffi_len(&mut self) -> *mut socklen_t;
unsafe fn assume_init(self) -> T;
}
trait Set<'a, T> {
fn new(val: &'a T) -> Self;
fn ffi_ptr(&self) -> *const c_void;
fn ffi_len(&self) -> socklen_t;
}
struct GetStruct<T> {
len: socklen_t,
val: MaybeUninit<T>,
}
impl<T> Get<T> for GetStruct<T> {
fn uninit() -> Self {
GetStruct {
len: mem::size_of::<T>() as socklen_t,
val: MaybeUninit::uninit(),
}
}
fn ffi_ptr(&mut self) -> *mut c_void {
self.val.as_mut_ptr() as *mut c_void
}
fn ffi_len(&mut self) -> *mut socklen_t {
&mut self.len
}
unsafe fn assume_init(self) -> T {
assert_eq!(self.len as usize, mem::size_of::<T>(), "invalid getsockopt implementation");
self.val.assume_init()
}
}
struct SetStruct<'a, T: 'static> {
ptr: &'a T,
}
impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
fn new(ptr: &'a T) -> SetStruct<'a, T> {
SetStruct { ptr }
}
fn ffi_ptr(&self) -> *const c_void {
self.ptr as *const T as *const c_void
}
fn ffi_len(&self) -> socklen_t {
mem::size_of::<T>() as socklen_t
}
}
struct GetBool {
len: socklen_t,
val: MaybeUninit<c_int>,
}
impl Get<bool> for GetBool {
fn uninit() -> Self {
GetBool {
len: mem::size_of::<c_int>() as socklen_t,
val: MaybeUninit::uninit(),
}
}
fn ffi_ptr(&mut self) -> *mut c_void {
self.val.as_mut_ptr() as *mut c_void
}
fn ffi_len(&mut self) -> *mut socklen_t {
&mut self.len
}
unsafe fn assume_init(self) -> bool {
assert_eq!(self.len as usize, mem::size_of::<c_int>(), "invalid getsockopt implementation");
self.val.assume_init() != 0
}
}
struct SetBool {
val: c_int,
}
impl<'a> Set<'a, bool> for SetBool {
fn new(val: &'a bool) -> SetBool {
SetBool { val: i32::from(*val) }
}
fn ffi_ptr(&self) -> *const c_void {
&self.val as *const c_int as *const c_void
}
fn ffi_len(&self) -> socklen_t {
mem::size_of::<c_int>() as socklen_t
}
}
struct GetU8 {
len: socklen_t,
val: MaybeUninit<u8>,
}
impl Get<u8> for GetU8 {
fn uninit() -> Self {
GetU8 {
len: mem::size_of::<u8>() as socklen_t,
val: MaybeUninit::uninit(),
}
}
fn ffi_ptr(&mut self) -> *mut c_void {
self.val.as_mut_ptr() as *mut c_void
}
fn ffi_len(&mut self) -> *mut socklen_t {
&mut self.len
}
unsafe fn assume_init(self) -> u8 {
assert_eq!(self.len as usize, mem::size_of::<u8>(), "invalid getsockopt implementation");
self.val.assume_init()
}
}
struct SetU8 {
val: u8,
}
impl<'a> Set<'a, u8> for SetU8 {
fn new(val: &'a u8) -> SetU8 {
SetU8 { val: *val }
}
fn ffi_ptr(&self) -> *const c_void {
&self.val as *const u8 as *const c_void
}
fn ffi_len(&self) -> socklen_t {
mem::size_of::<c_int>() as socklen_t
}
}
struct GetUsize {
len: socklen_t,
val: MaybeUninit<c_int>,
}
impl Get<usize> for GetUsize {
fn uninit() -> Self {
GetUsize {
len: mem::size_of::<c_int>() as socklen_t,
val: MaybeUninit::uninit(),
}
}
fn ffi_ptr(&mut self) -> *mut c_void {
self.val.as_mut_ptr() as *mut c_void
}
fn ffi_len(&mut self) -> *mut socklen_t {
&mut self.len
}
unsafe fn assume_init(self) -> usize {
assert_eq!(self.len as usize, mem::size_of::<c_int>(), "invalid getsockopt implementation");
self.val.assume_init() as usize
}
}
struct SetUsize {
val: c_int,
}
impl<'a> Set<'a, usize> for SetUsize {
fn new(val: &'a usize) -> SetUsize {
SetUsize { val: *val as c_int }
}
fn ffi_ptr(&self) -> *const c_void {
&self.val as *const c_int as *const c_void
}
fn ffi_len(&self) -> socklen_t {
mem::size_of::<c_int>() as socklen_t
}
}
struct GetOsString<T: AsMut<[u8]>> {
len: socklen_t,
val: MaybeUninit<T>,
}
impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
fn uninit() -> Self {
GetOsString {
len: mem::size_of::<T>() as socklen_t,
val: MaybeUninit::uninit(),
}
}
fn ffi_ptr(&mut self) -> *mut c_void {
self.val.as_mut_ptr() as *mut c_void
}
fn ffi_len(&mut self) -> *mut socklen_t {
&mut self.len
}
unsafe fn assume_init(self) -> OsString {
let len = self.len as usize;
let mut v = self.val.assume_init();
OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
}
}
struct SetOsString<'a> {
val: &'a OsStr,
}
impl<'a> Set<'a, OsString> for SetOsString<'a> {
fn new(val: &'a OsString) -> SetOsString {
SetOsString { val: val.as_os_str() }
}
fn ffi_ptr(&self) -> *const c_void {
self.val.as_bytes().as_ptr() as *const c_void
}
fn ffi_len(&self) -> socklen_t {
self.val.len() as socklen_t
}
}
#[cfg(test)]
mod test {
#[cfg(any(target_os = "android", target_os = "linux"))]
#[test]
fn can_get_peercred_on_unix_socket() {
use super::super::*;
let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap();
let a_cred = getsockopt(a, super::PeerCredentials).unwrap();
let b_cred = getsockopt(b, super::PeerCredentials).unwrap();
assert_eq!(a_cred, b_cred);
assert_ne!(a_cred.pid(), 0);
}
#[test]
fn is_socket_type_unix() {
use super::super::*;
use crate::unistd::close;
let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap();
let a_type = getsockopt(a, super::SockType).unwrap();
assert_eq!(a_type, SockType::Stream);
close(a).unwrap();
close(b).unwrap();
}
#[test]
fn is_socket_type_dgram() {
use super::super::*;
use crate::unistd::close;
let s = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
let s_type = getsockopt(s, super::SockType).unwrap();
assert_eq!(s_type, SockType::Datagram);
close(s).unwrap();
}
#[cfg(any(target_os = "freebsd",
target_os = "linux"))]
#[test]
fn can_get_listen_on_tcp_socket() {
use super::super::*;
use crate::unistd::close;
let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
let s_listening = getsockopt(s, super::AcceptConn).unwrap();
assert!(!s_listening);
listen(s, 10).unwrap();
let s_listening2 = getsockopt(s, super::AcceptConn).unwrap();
assert!(s_listening2);
close(s).unwrap();
}
}