summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Lerche <me@carllerche.com>2015-02-23 15:23:06 -0800
committerCarl Lerche <me@carllerche.com>2015-02-23 15:23:06 -0800
commit979faf097691dc35bb12b91dcde0d804a3d32b79 (patch)
tree26b288ea7fc59d654022b5da5133abbc533808ab
parent88988a31a017ffada1528029c910b56b12ac281b (diff)
downloadnix-979faf097691dc35bb12b91dcde0d804a3d32b79.zip
Improve ergonomics of getsockopt / setsockopt
-rw-r--r--src/sys/socket/consts.rs203
-rw-r--r--src/sys/socket/mod.rs71
-rw-r--r--src/sys/socket/sockopt.rs220
3 files changed, 369 insertions, 125 deletions
diff --git a/src/sys/socket/consts.rs b/src/sys/socket/consts.rs
index e23098b6..39bcd733 100644
--- a/src/sys/socket/consts.rs
+++ b/src/sys/socket/consts.rs
@@ -19,64 +19,62 @@ mod os {
pub const SOCK_RAW: SockType = 3;
pub const SOCK_RDM: SockType = 4;
- pub type SockLevel = c_int;
-
- pub const SOL_IP: SockLevel = 0;
- pub const IPPROTO_IP: SockLevel = SOL_IP;
- pub const SOL_SOCKET: SockLevel = 1;
- pub const SOL_TCP: SockLevel = 6;
- pub const IPPROTO_TCP: SockLevel = SOL_TCP;
- pub const SOL_UDP: SockLevel = 17;
- pub const SOL_IPV6: SockLevel = 41;
-
- pub type SockOpt = c_int;
-
- pub const SO_ACCEPTCONN: SockOpt = 30;
- pub const SO_BINDTODEVICE: SockOpt = 25;
- pub const SO_BROADCAST: SockOpt = 6;
- pub const SO_BSDCOMPAT: SockOpt = 14;
- pub const SO_DEBUG: SockOpt = 1;
- pub const SO_DOMAIN: SockOpt = 39;
- pub const SO_ERROR: SockOpt = 4;
- pub const SO_DONTROUTE: SockOpt = 5;
- pub const SO_KEEPALIVE: SockOpt = 9;
- pub const SO_LINGER: SockOpt = 13;
- pub const SO_MARK: SockOpt = 36;
- pub const SO_OOBINLINE: SockOpt = 10;
- pub const SO_PASSCRED: SockOpt = 16;
- pub const SO_PEEK_OFF: SockOpt = 42;
- pub const SO_PEERCRED: SockOpt = 17;
- pub const SO_PRIORITY: SockOpt = 12;
- pub const SO_PROTOCOL: SockOpt = 38;
- pub const SO_RCVBUF: SockOpt = 8;
- pub const SO_RCVBUFFORCE: SockOpt = 33;
- pub const SO_RCVLOWAT: SockOpt = 18;
- pub const SO_SNDLOWAT: SockOpt = 19;
- pub const SO_RCVTIMEO: SockOpt = 20;
- pub const SO_SNDTIMEO: SockOpt = 21;
- pub const SO_REUSEADDR: SockOpt = 2;
- pub const SO_REUSEPORT: SockOpt = 15;
- pub const SO_RXQ_OVFL: SockOpt = 40;
- pub const SO_SNDBUF: SockOpt = 7;
- pub const SO_SNDBUFFORCE: SockOpt = 32;
- pub const SO_TIMESTAMP: SockOpt = 29;
- pub const SO_TYPE: SockOpt = 3;
- pub const SO_BUSY_POLL: SockOpt = 46;
+ pub const SOL_IP: c_int = 0;
+ pub const SOL_SOCKET: c_int = 1;
+ pub const SOL_TCP: c_int = 6;
+ pub const SOL_UDP: c_int = 17;
+ pub const SOL_IPV6: c_int = 41;
+ pub const IPPROTO_IP: c_int = SOL_IP;
+ pub const IPPROTO_IPV6: c_int = SOL_IPV6;
+ pub const IPPROTO_TCP: c_int = SOL_TCP;
+ pub const IPPROTO_UDP: c_int = SOL_UDP;
+
+ pub const SO_ACCEPTCONN: c_int = 30;
+ pub const SO_BINDTODEVICE: c_int = 25;
+ pub const SO_BROADCAST: c_int = 6;
+ pub const SO_BSDCOMPAT: c_int = 14;
+ pub const SO_DEBUG: c_int = 1;
+ pub const SO_DOMAIN: c_int = 39;
+ pub const SO_ERROR: c_int = 4;
+ pub const SO_DONTROUTE: c_int = 5;
+ pub const SO_KEEPALIVE: c_int = 9;
+ pub const SO_LINGER: c_int = 13;
+ pub const SO_MARK: c_int = 36;
+ pub const SO_OOBINLINE: c_int = 10;
+ pub const SO_PASSCRED: c_int = 16;
+ pub const SO_PEEK_OFF: c_int = 42;
+ pub const SO_PEERCRED: c_int = 17;
+ pub const SO_PRIORITY: c_int = 12;
+ pub const SO_PROTOCOL: c_int = 38;
+ pub const SO_RCVBUF: c_int = 8;
+ pub const SO_RCVBUFFORCE: c_int = 33;
+ pub const SO_RCVLOWAT: c_int = 18;
+ pub const SO_SNDLOWAT: c_int = 19;
+ pub const SO_RCVTIMEO: c_int = 20;
+ pub const SO_SNDTIMEO: c_int = 21;
+ pub const SO_REUSEADDR: c_int = 2;
+ pub const SO_REUSEPORT: c_int = 15;
+ pub const SO_RXQ_OVFL: c_int = 40;
+ pub const SO_SNDBUF: c_int = 7;
+ pub const SO_SNDBUFFORCE: c_int = 32;
+ pub const SO_TIMESTAMP: c_int = 29;
+ pub const SO_TYPE: c_int = 3;
+ pub const SO_BUSY_POLL: c_int = 46;
// Socket options for TCP sockets
- pub const TCP_NODELAY: SockOpt = 1;
- pub const TCP_MAXSEG: SockOpt = 2;
- pub const TCP_CORK: SockOpt = 3;
+ pub const TCP_NODELAY: c_int = 1;
+ pub const TCP_MAXSEG: c_int = 2;
+ pub const TCP_CORK: c_int = 3;
// Socket options for the IP layer of the socket
- pub const IP_MULTICAST_IF: SockOpt = 32;
+ pub const IP_MULTICAST_IF: c_int = 32;
pub type IpMulticastTtl = uint8_t;
- pub const IP_MULTICAST_TTL: SockOpt = 33;
- pub const IP_MULTICAST_LOOP: SockOpt = 34;
- pub const IP_ADD_MEMBERSHIP: SockOpt = 35;
- pub const IP_DROP_MEMBERSHIP: SockOpt = 36;
+ pub const IP_MULTICAST_TTL: c_int = 33;
+ pub const IP_MULTICAST_LOOP: c_int = 34;
+ pub const IP_ADD_MEMBERSHIP: c_int = 35;
+ pub const IP_DROP_MEMBERSHIP: c_int = 36;
pub type InAddrT = u32;
@@ -111,67 +109,64 @@ mod os {
pub const SOCK_RAW: SockType = 3;
pub const SOCK_RDM: SockType = 4;
- pub type SockLevel = c_int;
-
- pub const SOL_SOCKET: SockLevel = 0xffff;
- pub const IPPROTO_IP: SockLevel = 0;
- pub const IPPROTO_TCP: SockLevel = 6;
- pub const IPPROTO_UDP: SockLevel = 17;
-
- pub type SockOpt = c_int;
-
- pub const SO_ACCEPTCONN: SockOpt = 0x0002;
- pub const SO_BROADCAST: SockOpt = 0x0020;
- pub const SO_DEBUG: SockOpt = 0x0001;
- pub const SO_DONTTRUNC: SockOpt = 0x2000;
- pub const SO_ERROR: SockOpt = 0x1007;
- pub const SO_DONTROUTE: SockOpt = 0x0010;
- pub const SO_KEEPALIVE: SockOpt = 0x0008;
- pub const SO_LABEL: SockOpt = 0x1010;
- pub const SO_LINGER: SockOpt = 0x0080;
- pub const SO_NREAD: SockOpt = 0x1020;
- pub const SO_NKE: SockOpt = 0x1021;
- pub const SO_NOSIGPIPE: SockOpt = 0x1022;
- pub const SO_NOADDRERR: SockOpt = 0x1023;
- pub const SO_NOTIFYCONFLICT: SockOpt = 0x1026;
- pub const SO_NP_EXTENSIONS: SockOpt = 0x1083;
- pub const SO_NWRITE: SockOpt = 0x1024;
- pub const SO_OOBINLINE: SockOpt = 0x0100;
- pub const SO_PEERLABEL: SockOpt = 0x1011;
- pub const SO_RCVBUF: SockOpt = 0x1002;
- pub const SO_RCVLOWAT: SockOpt = 0x1004;
- pub const SO_SNDLOWAT: SockOpt = 0x1003;
- pub const SO_RCVTIMEO: SockOpt = 0x1006;
- pub const SO_SNDTIMEO: SockOpt = 0x1005;
- pub const SO_RANDOMPORT: SockOpt = 0x1082;
- pub const SO_RESTRICTIONS: SockOpt = 0x1081;
- pub const SO_RESTRICT_DENYIN: SockOpt = 0x00000001;
- pub const SO_RESTRICT_DENYOUT: SockOpt = 0x00000002;
- pub const SO_REUSEADDR: SockOpt = 0x0004;
- pub const SO_REUSEPORT: SockOpt = 0x0200;
- pub const SO_REUSESHAREUID: SockOpt = 0x1025;
- pub const SO_SNDBUF: SockOpt = 0x1001;
- pub const SO_TIMESTAMP: SockOpt = 0x0400;
- pub const SO_TIMESTAMP_MONOTONIC: SockOpt = 0x0800;
- pub const SO_TYPE: SockOpt = 0x1008;
- pub const SO_WANTMORE: SockOpt = 0x4000;
- pub const SO_WANTOOBFLAG: SockOpt = 0x8000;
+ pub const SOL_SOCKET: c_int = 0xffff;
+ pub const IPPROTO_IP: c_int = 0;
+ pub const IPPROTO_IPV6: c_int = 41;
+ pub const IPPROTO_TCP: c_int = 6;
+ pub const IPPROTO_UDP: c_int = 17;
+
+ pub const SO_ACCEPTCONN: c_int = 0x0002;
+ pub const SO_BROADCAST: c_int = 0x0020;
+ pub const SO_DEBUG: c_int = 0x0001;
+ pub const SO_DONTTRUNC: c_int = 0x2000;
+ pub const SO_ERROR: c_int = 0x1007;
+ pub const SO_DONTROUTE: c_int = 0x0010;
+ pub const SO_KEEPALIVE: c_int = 0x0008;
+ pub const SO_LABEL: c_int = 0x1010;
+ pub const SO_LINGER: c_int = 0x0080;
+ pub const SO_NREAD: c_int = 0x1020;
+ pub const SO_NKE: c_int = 0x1021;
+ pub const SO_NOSIGPIPE: c_int = 0x1022;
+ pub const SO_NOADDRERR: c_int = 0x1023;
+ pub const SO_NOTIFYCONFLICT: c_int = 0x1026;
+ pub const SO_NP_EXTENSIONS: c_int = 0x1083;
+ pub const SO_NWRITE: c_int = 0x1024;
+ pub const SO_OOBINLINE: c_int = 0x0100;
+ pub const SO_PEERLABEL: c_int = 0x1011;
+ pub const SO_RCVBUF: c_int = 0x1002;
+ pub const SO_RCVLOWAT: c_int = 0x1004;
+ pub const SO_SNDLOWAT: c_int = 0x1003;
+ pub const SO_RCVTIMEO: c_int = 0x1006;
+ pub const SO_SNDTIMEO: c_int = 0x1005;
+ pub const SO_RANDOMPORT: c_int = 0x1082;
+ pub const SO_RESTRICTIONS: c_int = 0x1081;
+ pub const SO_RESTRICT_DENYIN: c_int = 0x00000001;
+ pub const SO_RESTRICT_DENYOUT: c_int = 0x00000002;
+ pub const SO_REUSEADDR: c_int = 0x0004;
+ pub const SO_REUSEPORT: c_int = 0x0200;
+ pub const SO_REUSESHAREUID: c_int = 0x1025;
+ pub const SO_SNDBUF: c_int = 0x1001;
+ pub const SO_TIMESTAMP: c_int = 0x0400;
+ pub const SO_TIMESTAMP_MONOTONIC: c_int = 0x0800;
+ pub const SO_TYPE: c_int = 0x1008;
+ pub const SO_WANTMORE: c_int = 0x4000;
+ pub const SO_WANTOOBFLAG: c_int = 0x8000;
#[allow(overflowing_literals)]
- pub const SO_RESTRICT_DENYSET: SockOpt = 0x80000000;
+ pub const SO_RESTRICT_DENYSET: c_int = 0x80000000;
// Socket options for TCP sockets
- pub const TCP_NODELAY: SockOpt = 1;
- pub const TCP_MAXSEG: SockOpt = 2;
+ pub const TCP_NODELAY: c_int = 1;
+ pub const TCP_MAXSEG: c_int = 2;
// Socket options for the IP layer of the socket
- pub const IP_MULTICAST_IF: SockOpt = 9;
+ pub const IP_MULTICAST_IF: c_int = 9;
pub type IpMulticastTtl = uint8_t;
- pub const IP_MULTICAST_TTL: SockOpt = 10;
- pub const IP_MULTICAST_LOOP: SockOpt = 11;
- pub const IP_ADD_MEMBERSHIP: SockOpt = 12;
- pub const IP_DROP_MEMBERSHIP: SockOpt = 13;
+ pub const IP_MULTICAST_TTL: c_int = 10;
+ pub const IP_MULTICAST_LOOP: c_int = 11;
+ pub const IP_ADD_MEMBERSHIP: c_int = 12;
+ pub const IP_DROP_MEMBERSHIP: c_int = 13;
pub type InAddrT = u32;
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 7e8afbfd..046df007 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -10,6 +10,7 @@ use std::os::unix::prelude::*;
mod addr;
mod consts;
mod ffi;
+pub mod sockopt;
/*
*
@@ -264,34 +265,62 @@ pub struct linger {
pub l_linger: c_int
}
-pub fn getsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &mut T) -> NixResult<usize> {
- let mut len = mem::size_of::<T>() as socklen_t;
+/*
+ *
+ * ===== Socket Options =====
+ *
+ */
- let res = unsafe {
- ffi::getsockopt(
- fd, level, opt,
- mem::transmute(val),
- &mut len as *mut socklen_t)
- };
+/// Represents a socket option that can be accessed or set
+pub trait SockOpt : Copy + fmt::Debug {
+ /// Type of `getsockopt` return value
+ type Get;
- if res < 0 {
- return Err(NixError::Sys(Errno::last()));
- }
+ /// Type of value used to set the socket option. Used as the argument to
+ /// `setsockopt`.
+ type Set;
- Ok(len as usize)
+ #[doc(hidden)]
+ fn get(&self, fd: Fd, level: c_int) -> NixResult<Self::Get>;
+
+ #[doc(hidden)]
+ fn set(&self, fd: Fd, level: c_int, val: Self::Set) -> NixResult<()>;
}
-pub fn setsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &T) -> NixResult<()> {
- let len = mem::size_of::<T>() as socklen_t;
+pub enum SockLevel {
+ Socket,
+ Tcp,
+ Ip,
+ Ipv6,
+ Udp
+}
- let res = unsafe {
- ffi::setsockopt(
- fd, level, opt,
- mem::transmute(val),
- len)
- };
+impl SockLevel {
+ fn as_cint(&self) -> c_int {
+ use self::SockLevel::*;
- from_ffi(res)
+ match *self {
+ Socket => consts::SOL_SOCKET,
+ Tcp => consts::IPPROTO_TCP,
+ Ip => consts::IPPROTO_IP,
+ Ipv6 => consts::IPPROTO_IPV6,
+ Udp => consts::IPPROTO_UDP,
+ }
+ }
+}
+
+/// Get the current value for the requested socket option
+///
+/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
+pub fn getsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O) -> NixResult<O::Get> {
+ opt.get(fd, level.as_cint())
+}
+
+/// Sets the value for the requested socket option
+///
+/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
+pub fn setsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O, val: O::Set) -> NixResult<()> {
+ opt.set(fd, level.as_cint(), val)
}
fn getpeername_sockaddr<T>(sockfd: Fd, addr: &T) -> NixResult<bool> {
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
+ }
+}