diff options
Diffstat (limited to 'src/sys/socket/sockopt.rs')
-rw-r--r-- | src/sys/socket/sockopt.rs | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs new file mode 100644 index 00000000..79c1800e --- /dev/null +++ b/src/sys/socket/sockopt.rs @@ -0,0 +1,220 @@ +use {NixResult, NixError, from_ffi}; +use super::{ffi, consts, SockOpt}; +use errno::Errno; +use libc::{c_int, uint8_t, c_void, socklen_t}; +use std::mem; +use std::os::unix::Fd; + +// Helper to generate the sockopt accessors +// TODO: Figure out how to ommit gets when not supported by opt +macro_rules! sockopt_impl { + ($name:ident, $flag:path, bool) => { + sockopt_impl!($name, $flag, bool, GetBool, bool, SetBool); + }; + + ($name:ident, $flag:path, u8) => { + sockopt_impl!($name, $flag, u8, GetU8, u8, SetU8); + }; + + ($name:ident, $flag:path, $ty:ty) => { + sockopt_impl!($name, $flag, $ty, GetStruct<$ty>, &'a $ty, SetStruct<$ty>); + }; + + ($name:ident, $flag:path, $get_ty:ty, $getter:ty, $set_ty:ty, $setter:ty) => { + #[derive(Copy, Debug)] + pub struct $name; + + impl<'a> SockOpt for $name { + type Get = $get_ty; + type Set = $set_ty; + + fn get(&self, fd: Fd, level: c_int) -> NixResult<$get_ty> { + unsafe { + let mut getter: $getter = Get::blank(); + + let res = ffi::getsockopt( + fd, level, $flag, + getter.ffi_ptr(), + getter.ffi_len()); + + if res < 0 { + return Err(NixError::Sys(Errno::last())); + } + + Ok(getter.unwrap()) + } + } + + fn set(&self, fd: Fd, level: c_int, val: $set_ty) -> NixResult<()> { + unsafe { + let setter: $setter = Set::new(val); + + let res = ffi::setsockopt( + fd, level, $flag, + setter.ffi_ptr(), + setter.ffi_len()); + + from_ffi(res) + } + } + } + }; +} + +/* + * + * ===== Define sockopts ===== + * + */ + +sockopt_impl!(ReuseAddr, consts::SO_REUSEADDR, bool); +sockopt_impl!(ReusePort, consts::SO_REUSEPORT, bool); +sockopt_impl!(TcpNoDelay, consts::TCP_NODELAY, bool); +sockopt_impl!(Linger, consts::SO_LINGER, super::linger); +sockopt_impl!(IpAddMembership, consts::IP_ADD_MEMBERSHIP, super::ip_mreq); +sockopt_impl!(IpMulticastTtl, consts::IP_MULTICAST_TTL, u8); + +/* + * + * ===== Accessor helpers ===== + * + */ + +trait Get<T> { + unsafe fn blank() -> Self; + unsafe fn ffi_ptr(&mut self) -> *mut c_void; + unsafe fn ffi_len(&mut self) -> *mut socklen_t; + unsafe fn unwrap(self) -> T; +} + +trait Set<T> { + fn new(val: T) -> Self; + unsafe fn ffi_ptr(&self) -> *const c_void; + unsafe fn ffi_len(&self) -> socklen_t; +} + +struct GetStruct<T> { + len: socklen_t, + val: T, +} + +impl<T> Get<T> for GetStruct<T> { + unsafe fn blank() -> Self { + mem::zeroed() + } + + unsafe fn ffi_ptr(&mut self) -> *mut c_void { + mem::transmute(&mut self.val) + } + + unsafe fn ffi_len(&mut self) -> *mut socklen_t { + mem::transmute(&mut self.len) + } + + unsafe fn unwrap(self) -> T { + assert!(self.len as usize == mem::size_of::<T>(), "invalid getsockopt implementation"); + self.val + } +} + +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: ptr } + } + + unsafe fn ffi_ptr(&self) -> *const c_void { + mem::transmute(self.ptr) + } + + unsafe fn ffi_len(&self) -> socklen_t { + mem::size_of::<T>() as socklen_t + } +} + +struct GetBool { + len: socklen_t, + val: c_int, +} + +impl Get<bool> for GetBool { + unsafe fn blank() -> Self { + mem::zeroed() + } + + unsafe fn ffi_ptr(&mut self) -> *mut c_void { + mem::transmute(&mut self.val) + } + + unsafe fn ffi_len(&mut self) -> *mut socklen_t { + mem::transmute(&mut self.len) + } + + unsafe fn unwrap(self) -> bool { + assert!(self.len as usize == mem::size_of::<c_int>(), "invalid getsockopt implementation"); + self.val != 0 + } +} + +struct SetBool { + val: c_int, +} + +impl Set<bool> for SetBool { + fn new(val: bool) -> SetBool { + SetBool { val: if val { 1 } else { 0 } } + } + + unsafe fn ffi_ptr(&self) -> *const c_void { + mem::transmute(&self.val) + } + + unsafe fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} + +struct GetU8 { + len: socklen_t, + val: uint8_t, +} + +impl Get<u8> for GetU8 { + unsafe fn blank() -> Self { + mem::zeroed() + } + + unsafe fn ffi_ptr(&mut self) -> *mut c_void { + mem::transmute(&mut self.val) + } + + unsafe fn ffi_len(&mut self) -> *mut socklen_t { + mem::transmute(&mut self.len) + } + + unsafe fn unwrap(self) -> u8 { + assert!(self.len as usize == mem::size_of::<uint8_t>(), "invalid getsockopt implementation"); + self.val as u8 + } +} + +struct SetU8 { + val: uint8_t, +} + +impl Set<u8> for SetU8 { + fn new(val: u8) -> SetU8 { + SetU8 { val: val as uint8_t } + } + + unsafe fn ffi_ptr(&self) -> *const c_void { + mem::transmute(&self.val) + } + + unsafe fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} |