| //! Socket options as used by `setsockopt` and `getsockopt`. |
| use super::{GetSockOpt, SetSockOpt}; |
| use crate::errno::Errno; |
| use crate::sys::time::TimeVal; |
| use crate::Result; |
| use cfg_if::cfg_if; |
| use libc::{self, c_int, c_void, socklen_t}; |
| use std::ffi::{OsStr, OsString}; |
| use std::mem::{self, MaybeUninit}; |
| use std::os::unix::ffi::OsStrExt; |
| use std::os::unix::io::{AsFd, AsRawFd}; |
| |
| // Constants |
| // TCP_CA_NAME_MAX isn't defined in user space include files |
| #[cfg(any(target_os = "freebsd", target_os = "linux"))] |
| #[cfg(feature = "net")] |
| const TCP_CA_NAME_MAX: usize = 16; |
| |
| /// Helper for implementing `SetSockOpt` for a given socket option. See |
| /// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). |
| /// |
| /// This macro aims to help implementing `SetSockOpt` for different socket options that accept |
| /// different kinds of data to be used with `setsockopt`. |
| /// |
| /// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option |
| /// you are implementing represents a simple type. |
| /// |
| /// # Arguments |
| /// |
| /// * `$name:ident`: name of the type you want to implement `SetSockOpt` for. |
| /// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets* |
| /// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), |
| /// and more. Please refer to your system manual for more options. Will be passed as the second |
| /// argument (`level`) to the `setsockopt` call. |
| /// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, |
| /// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) |
| /// to the `setsockopt` call. |
| /// * Type of the value that you are going to set. |
| /// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for |
| /// `bool`, `SetUsize` for `usize`, etc.). |
| macro_rules! setsockopt_impl { |
| ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => { |
| impl SetSockOpt for $name { |
| type Val = $ty; |
| |
| fn set<F: AsFd>(&self, fd: &F, val: &$ty) -> Result<()> { |
| unsafe { |
| let setter: $setter = Set::new(val); |
| |
| let res = libc::setsockopt( |
| fd.as_fd().as_raw_fd(), |
| $level, |
| $flag, |
| setter.ffi_ptr(), |
| setter.ffi_len(), |
| ); |
| Errno::result(res).map(drop) |
| } |
| } |
| } |
| }; |
| } |
| |
| /// Helper for implementing `GetSockOpt` for a given socket option. See |
| /// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html). |
| /// |
| /// This macro aims to help implementing `GetSockOpt` for different socket options that accept |
| /// different kinds of data to be use with `getsockopt`. |
| /// |
| /// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option |
| /// you are implementing represents a simple type. |
| /// |
| /// # Arguments |
| /// |
| /// * Name of the type you want to implement `GetSockOpt` for. |
| /// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip |
| /// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer |
| /// to your system manual for more options. Will be passed as the second argument (`level`) to |
| /// the `getsockopt` call. |
| /// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, |
| /// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to |
| /// the `getsockopt` call. |
| /// * Type of the value that you are going to get. |
| /// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for |
| /// `bool`, `GetUsize` for `usize`, etc.). |
| macro_rules! getsockopt_impl { |
| ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => { |
| impl GetSockOpt for $name { |
| type Val = $ty; |
| |
| fn get<F: AsFd>(&self, fd: &F) -> Result<$ty> { |
| unsafe { |
| let mut getter: $getter = Get::uninit(); |
| |
| let res = libc::getsockopt( |
| fd.as_fd().as_raw_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), |
| } |
| } |
| } |
| } |
| }; |
| } |
| |
| /// Helper to generate the sockopt accessors. See |
| /// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html) and |
| /// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). |
| /// |
| /// This macro aims to help implementing `GetSockOpt` and `SetSockOpt` for different socket options |
| /// that accept different kinds of data to be use with `getsockopt` and `setsockopt` respectively. |
| /// |
| /// Basically this macro wraps up the [`getsockopt_impl!`](macro.getsockopt_impl.html) and |
| /// [`setsockopt_impl!`](macro.setsockopt_impl.html) macros. |
| /// |
| /// # Arguments |
| /// |
| /// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or |
| /// both of them. |
| /// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for. |
| /// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets* |
| /// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), |
| /// and more. Please refer to your system manual for more options. Will be passed as the second |
| /// argument (`level`) to the `getsockopt`/`setsockopt` call. |
| /// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, |
| /// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) |
| /// to the `setsockopt`/`getsockopt` call. |
| /// * `$ty:ty`: type of the value that will be get/set. |
| /// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`. |
| /// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`. |
| // Some targets don't use all rules. |
| #[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); |
| }; |
| |
| /* |
| * Matchers with generic getter types must be placed at the end, so |
| * they'll only match _after_ specialized matchers fail |
| */ |
| ($(#[$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>); |
| }; |
| } |
| |
| /* |
| * |
| * ===== Define sockopts ===== |
| * |
| */ |
| |
| sockopt_impl!( |
| /// Enables local address reuse |
| ReuseAddr, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_REUSEADDR, |
| bool |
| ); |
| #[cfg(not(solarish))] |
| sockopt_impl!( |
| /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an |
| /// identical socket address. |
| ReusePort, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_REUSEPORT, |
| bool |
| ); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Under most circumstances, TCP sends data when it is presented; when |
| /// outstanding data has not yet been acknowledged, it gathers small amounts |
| /// of output to be sent in a single packet once an acknowledgement is |
| /// received. For a small number of clients, such as window systems that |
| /// send a stream of mouse events which receive no replies, this |
| /// packetization may cause significant delays. The boolean option |
| /// TCP_NODELAY defeats this algorithm. |
| TcpNoDelay, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_NODELAY, |
| bool |
| ); |
| sockopt_impl!( |
| /// When enabled, a close(2) or shutdown(2) will not return until all |
| /// queued messages for the socket have been successfully sent or the |
| /// linger timeout has been reached. |
| Linger, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_LINGER, |
| libc::linger |
| ); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Join a multicast group |
| IpAddMembership, |
| SetOnly, |
| libc::IPPROTO_IP, |
| libc::IP_ADD_MEMBERSHIP, |
| super::IpMembershipRequest |
| ); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Leave a multicast group. |
| IpDropMembership, |
| SetOnly, |
| libc::IPPROTO_IP, |
| libc::IP_DROP_MEMBERSHIP, |
| super::IpMembershipRequest |
| ); |
| cfg_if! { |
| if #[cfg(linux_android)] { |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Join an IPv6 multicast group. |
| Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Leave an IPv6 multicast group. |
| Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest); |
| } else if #[cfg(any(bsd, solarish))] { |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Join an IPv6 multicast group. |
| Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, |
| libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Leave an IPv6 multicast group. |
| Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, |
| libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest); |
| } |
| } |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Set or read the time-to-live value of outgoing multicast packets for |
| /// this socket. |
| IpMulticastTtl, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_MULTICAST_TTL, |
| u8 |
| ); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Set or read the hop limit value of outgoing IPv6 multicast packets for |
| /// this socket. |
| Ipv6MulticastHops, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_MULTICAST_HOPS, |
| libc::c_int |
| ); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Set or read a boolean integer argument that determines whether sent |
| /// multicast packets should be looped back to the local sockets. |
| IpMulticastLoop, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_MULTICAST_LOOP, |
| bool |
| ); |
| #[cfg(target_os = "linux")] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Set the protocol-defined priority for all packets to be |
| /// sent on this socket |
| Priority, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_PRIORITY, |
| libc::c_int |
| ); |
| #[cfg(target_os = "linux")] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Set or receive the Type-Of-Service (TOS) field that is |
| /// sent with every IP packet originating from this socket |
| IpTos, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_TOS, |
| libc::c_int |
| ); |
| #[cfg(target_os = "linux")] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Traffic class associated with outgoing packets |
| Ipv6TClass, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_TCLASS, |
| libc::c_int |
| ); |
| #[cfg(any(linux_android, target_os = "fuchsia"))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// If enabled, this boolean option allows binding to an IP address that |
| /// is nonlocal or does not (yet) exist. |
| IpFreebind, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_FREEBIND, |
| bool |
| ); |
| #[cfg(linux_android)] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// If enabled, the kernel will not reserve an ephemeral port when binding |
| /// socket with a port number of 0. The port will later be automatically |
| /// chosen at connect time, in a way that allows sharing a source port as |
| /// long as the 4-tuple is unique. |
| IpBindAddressNoPort, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_BIND_ADDRESS_NO_PORT, |
| bool |
| ); |
| sockopt_impl!( |
| /// Specify the receiving timeout until reporting an error. |
| ReceiveTimeout, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_RCVTIMEO, |
| TimeVal |
| ); |
| sockopt_impl!( |
| /// Specify the sending timeout until reporting an error. |
| SendTimeout, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_SNDTIMEO, |
| TimeVal |
| ); |
| sockopt_impl!( |
| /// Set or get the broadcast flag. |
| Broadcast, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_BROADCAST, |
| bool |
| ); |
| sockopt_impl!( |
| /// If this option is enabled, out-of-band data is directly placed into |
| /// the receive data stream. |
| OobInline, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_OOBINLINE, |
| bool |
| ); |
| sockopt_impl!( |
| /// Get and clear the pending socket error. |
| SocketError, |
| GetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_ERROR, |
| i32 |
| ); |
| sockopt_impl!( |
| /// Set or get the don't route flag. |
| DontRoute, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_DONTROUTE, |
| bool |
| ); |
| sockopt_impl!( |
| /// Enable sending of keep-alive messages on connection-oriented sockets. |
| KeepAlive, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_KEEPALIVE, |
| bool |
| ); |
| #[cfg(any(freebsdlike, apple_targets))] |
| sockopt_impl!( |
| /// Get the credentials of the peer process of a connected unix domain |
| /// socket. |
| LocalPeerCred, |
| GetOnly, |
| 0, |
| libc::LOCAL_PEERCRED, |
| super::XuCred |
| ); |
| #[cfg(apple_targets)] |
| sockopt_impl!( |
| /// Get the PID of the peer process of a connected unix domain socket. |
| LocalPeerPid, |
| GetOnly, |
| 0, |
| libc::LOCAL_PEERPID, |
| libc::c_int |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Return the credentials of the foreign process connected to this socket. |
| PeerCredentials, |
| GetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_PEERCRED, |
| super::UnixCredentials |
| ); |
| #[cfg(target_os = "freebsd")] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Get backlog limit of the socket |
| ListenQLimit, |
| GetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_LISTENQLIMIT, |
| u32 |
| ); |
| #[cfg(apple_targets)] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Specify the amount of time, in seconds, that the connection must be idle |
| /// before keepalive probes (if enabled) are sent. |
| TcpKeepAlive, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_KEEPALIVE, |
| u32 |
| ); |
| #[cfg(any(freebsdlike, linux_android))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The time (in seconds) the connection needs to remain idle before TCP |
| /// starts sending keepalive probes |
| TcpKeepIdle, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_KEEPIDLE, |
| u32 |
| ); |
| cfg_if! { |
| if #[cfg(linux_android)] { |
| sockopt_impl!( |
| /// The maximum segment size for outgoing TCP packets. |
| TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32); |
| } else if #[cfg(not(target_os = "redox"))] { |
| sockopt_impl!( |
| /// The maximum segment size for outgoing TCP packets. |
| TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32); |
| } |
| } |
| #[cfg(not(any( |
| target_os = "openbsd", |
| target_os = "haiku", |
| target_os = "redox" |
| )))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The maximum number of keepalive probes TCP should send before |
| /// dropping the connection. |
| TcpKeepCount, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_KEEPCNT, |
| u32 |
| ); |
| #[cfg(any(linux_android, target_os = "fuchsia"))] |
| sockopt_impl!( |
| #[allow(missing_docs)] |
| // Not documented by Linux! |
| TcpRepair, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_REPAIR, |
| u32 |
| ); |
| #[cfg(not(any( |
| target_os = "openbsd", |
| target_os = "haiku", |
| target_os = "redox" |
| )))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The time (in seconds) between individual keepalive probes. |
| 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")))] |
| /// Specifies the maximum amount of time in milliseconds that transmitted |
| /// data may remain unacknowledged before TCP will forcibly close the |
| /// corresponding connection |
| TcpUserTimeout, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_USER_TIMEOUT, |
| u32 |
| ); |
| #[cfg(linux_android)] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Enables TCP Fast Open (RFC 7413) on a connecting socket. If a fast open |
| /// cookie is not available (first attempt to connect), `connect` syscall |
| /// will behave as usual, except for internally trying to solicit a cookie |
| /// from remote peer. When cookie is available, the next `connect` syscall |
| /// will immediately succeed without actually establishing TCP connection. |
| /// The connection establishment will be defered till the next `write` or |
| /// `sendmsg` syscalls on the socket, allowing TCP prtocol to establish |
| /// connection and send data in the same packets. Note: calling `read` right |
| /// after `connect` without `write` on the socket will cause the blocking |
| /// socket to be blocked forever. |
| TcpFastOpenConnect, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_FASTOPEN_CONNECT, |
| bool |
| ); |
| sockopt_impl!( |
| /// Sets or gets the maximum socket receive buffer in bytes. |
| RcvBuf, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_RCVBUF, |
| usize |
| ); |
| sockopt_impl!( |
| /// Sets or gets the maximum socket send buffer in bytes. |
| SndBuf, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_SNDBUF, |
| usize |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can |
| /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be |
| /// overridden. |
| RcvBufForce, |
| SetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_RCVBUFFORCE, |
| usize |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can |
| /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be |
| /// overridden. |
| SndBufForce, |
| SetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_SNDBUFFORCE, |
| usize |
| ); |
| sockopt_impl!( |
| /// Gets the socket type as an integer. |
| SockType, |
| GetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_TYPE, |
| super::SockType, |
| GetStruct<i32> |
| ); |
| sockopt_impl!( |
| /// Returns a value indicating whether or not this socket has been marked to |
| /// accept connections with `listen(2)`. |
| AcceptConn, |
| GetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_ACCEPTCONN, |
| bool |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Bind this socket to a particular device like “eth0”. |
| BindToDevice, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_BINDTODEVICE, |
| OsString<[u8; libc::IFNAMSIZ]> |
| ); |
| #[cfg(linux_android)] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| #[allow(missing_docs)] |
| // Not documented by Linux! |
| OriginalDst, |
| GetOnly, |
| libc::SOL_IP, |
| libc::SO_ORIGINAL_DST, |
| libc::sockaddr_in |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| #[allow(missing_docs)] |
| // Not documented by Linux! |
| Ip6tOriginalDst, |
| GetOnly, |
| libc::SOL_IPV6, |
| libc::IP6T_SO_ORIGINAL_DST, |
| libc::sockaddr_in6 |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Specifies exact type of timestamping information collected by the kernel |
| /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) |
| Timestamping, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_TIMESTAMPING, |
| super::TimestampingFlag |
| ); |
| #[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "hurd", target_os = "redox")))] |
| sockopt_impl!( |
| /// Enable or disable the receiving of the `SO_TIMESTAMP` control message. |
| ReceiveTimestamp, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_TIMESTAMP, |
| bool |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message. |
| ReceiveTimestampns, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_TIMESTAMPNS, |
| bool |
| ); |
| #[cfg(target_os = "freebsd")] |
| sockopt_impl!( |
| /// Sets a specific timestamp format instead of the classic `SCM_TIMESTAMP`, |
| /// to follow up after `SO_TIMESTAMP` is set. |
| TsClock, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_TS_CLOCK, |
| super::SocketTimestamp |
| ); |
| #[cfg(linux_android)] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Setting this boolean option enables transparent proxying on this socket. |
| 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")))] |
| /// Allows the socket to be bound to addresses which are not local to the |
| /// machine, so it can be used to make a transparent proxy. |
| 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")))] |
| /// Can `bind(2)` to any address, even one not bound to any available |
| /// network interface in the system. |
| BindAny, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_BINDANY, |
| bool |
| ); |
| #[cfg(target_os = "freebsd")] |
| sockopt_impl!( |
| /// Set the route table (FIB) for this socket up to the `net.fibs` OID limit |
| /// (more specific than the setfib command line/call which are process based). |
| Fib, |
| SetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_SETFIB, |
| i32 |
| ); |
| #[cfg(target_os = "freebsd")] |
| sockopt_impl!( |
| /// Set `so_user_cookie` for this socket allowing network traffic based |
| /// upon it, similar to Linux's netfilter MARK. |
| UserCookie, |
| SetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_USER_COOKIE, |
| u32 |
| ); |
| #[cfg(target_os = "openbsd")] |
| sockopt_impl!( |
| /// Set the route table for this socket, needs a privileged user if |
| /// the process/socket had been set to the non default route. |
| Rtable, |
| SetOnly, |
| libc::SOL_SOCKET, |
| libc::SO_RTABLE, |
| i32 |
| ); |
| #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] |
| sockopt_impl!( |
| /// Get/set a filter on this socket before accepting connections similarly |
| /// to Linux's TCP_DEFER_ACCEPT but after the listen's call. |
| AcceptFilter, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_ACCEPTFILTER, |
| libc::accept_filter_arg |
| ); |
| #[cfg(target_os = "linux")] |
| sockopt_impl!( |
| /// Set the mark for each packet sent through this socket (similar to the |
| /// netfilter MARK target but socket-based). |
| Mark, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_MARK, |
| u32 |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Enable or disable the receiving of the `SCM_CREDENTIALS` control |
| /// message. |
| 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")))] |
| /// This option allows the caller to set the TCP congestion control |
| /// algorithm to be used, on a per-socket basis. |
| TcpCongestion, |
| Both, |
| libc::IPPROTO_TCP, |
| libc::TCP_CONGESTION, |
| OsString<[u8; TCP_CA_NAME_MAX]> |
| ); |
| #[cfg(any(linux_android, apple_targets, target_os = "netbsd"))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo |
| /// structure that supplies some information about the incoming packet. |
| Ipv4PacketInfo, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_PKTINFO, |
| bool |
| ); |
| #[cfg(any(linux_android, target_os = "freebsd", apple_targets, netbsdlike))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// Set delivery of the `IPV6_PKTINFO` control message on incoming |
| /// datagrams. |
| Ipv6RecvPacketInfo, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_RECVPKTINFO, |
| bool |
| ); |
| #[cfg(bsd)] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to |
| /// the interface on which the packet was received. |
| Ipv4RecvIf, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_RECVIF, |
| bool |
| ); |
| #[cfg(bsd)] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The `recvmsg(2)` call will return the destination IP address for a UDP |
| /// datagram. |
| Ipv4RecvDstAddr, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_RECVDSTADDR, |
| bool |
| ); |
| #[cfg(any(linux_android, target_os = "freebsd"))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The `recvmsg(2)` call will return the destination IP address for a UDP |
| /// datagram. |
| 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)] |
| // Not documented by Linux! |
| 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)] |
| // Not documented by Linux! |
| UdpGroSegment, |
| Both, |
| libc::IPPROTO_UDP, |
| libc::UDP_GRO, |
| bool |
| ); |
| #[cfg(target_os = "linux")] |
| sockopt_impl!( |
| /// Configures the behavior of time-based transmission of packets, for use |
| /// with the `TxTime` control message. |
| TxTime, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_TXTIME, |
| libc::sock_txtime |
| ); |
| #[cfg(any(linux_android, target_os = "fuchsia"))] |
| sockopt_impl!( |
| /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should |
| /// be attached to received skbs indicating the number of packets dropped by |
| /// the socket since its creation. |
| RxqOvfl, |
| Both, |
| libc::SOL_SOCKET, |
| libc::SO_RXQ_OVFL, |
| libc::c_int |
| ); |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The socket is restricted to sending and receiving IPv6 packets only. |
| Ipv6V6Only, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_V6ONLY, |
| bool |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Enable extended reliable error message passing. |
| Ipv4RecvErr, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_RECVERR, |
| bool |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Control receiving of asynchronous error options. |
| Ipv6RecvErr, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_RECVERR, |
| bool |
| ); |
| #[cfg(linux_android)] |
| sockopt_impl!( |
| /// Fetch the current system-estimated Path MTU. |
| IpMtu, |
| GetOnly, |
| libc::IPPROTO_IP, |
| libc::IP_MTU, |
| libc::c_int |
| ); |
| #[cfg(any(linux_android, target_os = "freebsd"))] |
| sockopt_impl!( |
| /// Set or retrieve the current time-to-live field that is used in every |
| /// packet sent from this socket. |
| Ipv4Ttl, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_TTL, |
| libc::c_int |
| ); |
| #[cfg(any(linux_android, target_os = "freebsd"))] |
| sockopt_impl!( |
| /// Set the unicast hop limit for the socket. |
| Ipv6Ttl, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_UNICAST_HOPS, |
| libc::c_int |
| ); |
| #[cfg(any(linux_android, target_os = "freebsd"))] |
| #[cfg(feature = "net")] |
| sockopt_impl!( |
| #[cfg_attr(docsrs, doc(cfg(feature = "net")))] |
| /// The `recvmsg(2)` call will return the destination IP address for a UDP |
| /// datagram. |
| Ipv6OrigDstAddr, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_ORIGDSTADDR, |
| bool |
| ); |
| #[cfg(apple_targets)] |
| sockopt_impl!( |
| /// Set "don't fragment packet" flag on the IP packet. |
| IpDontFrag, |
| Both, |
| libc::IPPROTO_IP, |
| libc::IP_DONTFRAG, |
| bool |
| ); |
| #[cfg(any(linux_android, apple_targets))] |
| sockopt_impl!( |
| /// Set "don't fragment packet" flag on the IPv6 packet. |
| Ipv6DontFrag, |
| Both, |
| libc::IPPROTO_IPV6, |
| libc::IPV6_DONTFRAG, |
| bool |
| ); |
| |
| #[allow(missing_docs)] |
| // Not documented by Linux! |
| #[cfg(linux_android)] |
| #[derive(Copy, Clone, Debug)] |
| pub struct AlgSetAeadAuthSize; |
| |
| // ALG_SET_AEAD_AUTH_SIZE read the length from passed `option_len` |
| // See https://elixir.bootlin.com/linux/v4.4/source/crypto/af_alg.c#L222 |
| #[cfg(linux_android)] |
| impl SetSockOpt for AlgSetAeadAuthSize { |
| type Val = usize; |
| |
| fn set<F: AsFd>(&self, fd: &F, val: &usize) -> Result<()> { |
| unsafe { |
| let res = libc::setsockopt( |
| fd.as_fd().as_raw_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)] |
| // Not documented by Linux! |
| #[cfg(linux_android)] |
| #[derive(Clone, Debug)] |
| pub struct AlgSetKey<T>(::std::marker::PhantomData<T>); |
| |
| #[cfg(linux_android)] |
| impl<T> Default for AlgSetKey<T> { |
| fn default() -> Self { |
| AlgSetKey(Default::default()) |
| } |
| } |
| |
| #[cfg(linux_android)] |
| impl<T> SetSockOpt for AlgSetKey<T> |
| where |
| T: AsRef<[u8]> + Clone, |
| { |
| type Val = T; |
| |
| fn set<F: AsFd>(&self, fd: &F, val: &T) -> Result<()> { |
| unsafe { |
| let res = libc::setsockopt( |
| fd.as_fd().as_raw_fd(), |
| libc::SOL_ALG, |
| libc::ALG_SET_KEY, |
| val.as_ref().as_ptr().cast(), |
| val.as_ref().len() as libc::socklen_t, |
| ); |
| Errno::result(res).map(drop) |
| } |
| } |
| } |
| |
| /// Set the Upper Layer Protocol (ULP) on the TCP socket. |
| /// |
| /// For example, to enable the TLS ULP on a socket, the C function call would be: |
| /// |
| /// ```c |
| /// setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls")); |
| /// ``` |
| /// |
| /// ... and the `nix` equivalent is: |
| /// |
| /// ```ignore,rust |
| /// setsockopt(sock, TcpUlp::default(), b"tls"); |
| /// ``` |
| /// |
| /// Note that the ULP name does not need a trailing NUL terminator (`\0`). |
| #[cfg(linux_android)] |
| #[derive(Clone, Debug)] |
| pub struct TcpUlp<T>(::std::marker::PhantomData<T>); |
| |
| #[cfg(linux_android)] |
| impl<T> Default for TcpUlp<T> { |
| fn default() -> Self { |
| TcpUlp(Default::default()) |
| } |
| } |
| |
| #[cfg(linux_android)] |
| impl<T> SetSockOpt for TcpUlp<T> |
| where |
| T: AsRef<[u8]> + Clone, |
| { |
| type Val = T; |
| |
| fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()> { |
| unsafe { |
| let res = libc::setsockopt( |
| fd.as_fd().as_raw_fd(), |
| libc::SOL_TCP, |
| libc::TCP_ULP, |
| val.as_ref().as_ptr().cast(), |
| val.as_ref().len() as libc::socklen_t, |
| ); |
| Errno::result(res).map(drop) |
| } |
| } |
| } |
| |
| /// Value used with the [`TcpTlsTx`] and [`TcpTlsRx`] socket options. |
| #[cfg(target_os = "linux")] |
| #[derive(Copy, Clone, Debug)] |
| pub enum TlsCryptoInfo { |
| /// AES-128-GCM |
| Aes128Gcm(libc::tls12_crypto_info_aes_gcm_128), |
| |
| /// AES-256-GCM |
| Aes256Gcm(libc::tls12_crypto_info_aes_gcm_256), |
| |
| /// CHACHA20-POLY1305 |
| Chacha20Poly1305(libc::tls12_crypto_info_chacha20_poly1305), |
| } |
| |
| /// Set the Kernel TLS write parameters on the TCP socket. |
| /// |
| /// For example, the C function call would be: |
| /// |
| /// ```c |
| /// setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)); |
| /// ``` |
| /// |
| /// ... and the `nix` equivalent is: |
| /// |
| /// ```ignore,rust |
| /// setsockopt(sock, TcpTlsTx, &crypto_info); |
| /// ``` |
| #[cfg(target_os = "linux")] |
| #[derive(Copy, Clone, Debug)] |
| pub struct TcpTlsTx; |
| |
| #[cfg(target_os = "linux")] |
| impl SetSockOpt for TcpTlsTx { |
| type Val = TlsCryptoInfo; |
| |
| fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()> { |
| let (ffi_ptr, ffi_len) = match val { |
| TlsCryptoInfo::Aes128Gcm(crypto_info) => { |
| (<*const _>::cast(crypto_info), mem::size_of_val(crypto_info)) |
| } |
| TlsCryptoInfo::Aes256Gcm(crypto_info) => { |
| (<*const _>::cast(crypto_info), mem::size_of_val(crypto_info)) |
| } |
| TlsCryptoInfo::Chacha20Poly1305(crypto_info) => { |
| (<*const _>::cast(crypto_info), mem::size_of_val(crypto_info)) |
| } |
| }; |
| unsafe { |
| let res = libc::setsockopt( |
| fd.as_fd().as_raw_fd(), |
| libc::SOL_TLS, |
| libc::TLS_TX, |
| ffi_ptr, |
| ffi_len as libc::socklen_t, |
| ); |
| Errno::result(res).map(drop) |
| } |
| } |
| } |
| |
| /// Set the Kernel TLS read parameters on the TCP socket. |
| /// |
| /// For example, the C function call would be: |
| /// |
| /// ```c |
| /// setsockopt(sock, SOL_TLS, TLS_RX, &crypto_info, sizeof(crypto_info)); |
| /// ``` |
| /// |
| /// ... and the `nix` equivalent is: |
| /// |
| /// ```ignore,rust |
| /// setsockopt(sock, TcpTlsRx, &crypto_info); |
| /// ``` |
| #[cfg(target_os = "linux")] |
| #[derive(Copy, Clone, Debug)] |
| pub struct TcpTlsRx; |
| |
| #[cfg(target_os = "linux")] |
| impl SetSockOpt for TcpTlsRx { |
| type Val = TlsCryptoInfo; |
| |
| fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()> { |
| let (ffi_ptr, ffi_len) = match val { |
| TlsCryptoInfo::Aes128Gcm(crypto_info) => { |
| (<*const _>::cast(crypto_info), mem::size_of_val(crypto_info)) |
| } |
| TlsCryptoInfo::Aes256Gcm(crypto_info) => { |
| (<*const _>::cast(crypto_info), mem::size_of_val(crypto_info)) |
| } |
| TlsCryptoInfo::Chacha20Poly1305(crypto_info) => { |
| (<*const _>::cast(crypto_info), mem::size_of_val(crypto_info)) |
| } |
| }; |
| unsafe { |
| let res = libc::setsockopt( |
| fd.as_fd().as_raw_fd(), |
| libc::SOL_TLS, |
| libc::TLS_RX, |
| ffi_ptr, |
| ffi_len as libc::socklen_t, |
| ); |
| Errno::result(res).map(drop) |
| } |
| } |
| } |
| |
| |
| /* |
| * |
| * ===== Accessor helpers ===== |
| * |
| */ |
| |
| /// Helper trait that describes what is expected from a `GetSockOpt` getter. |
| trait Get<T> { |
| /// Returns an uninitialized value. |
| fn uninit() -> Self; |
| /// Returns a pointer to the stored value. This pointer will be passed to the system's |
| /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`). |
| fn ffi_ptr(&mut self) -> *mut c_void; |
| /// Returns length of the stored value. This pointer will be passed to the system's |
| /// `getsockopt` call (`man 3p getsockopt`, argument `option_len`). |
| fn ffi_len(&mut self) -> *mut socklen_t; |
| /// Returns the hopefully initialized inner value. |
| unsafe fn assume_init(self) -> T; |
| } |
| |
| /// Helper trait that describes what is expected from a `SetSockOpt` setter. |
| trait Set<'a, T> { |
| /// Initialize the setter with a given value. |
| fn new(val: &'a T) -> Self; |
| /// Returns a pointer to the stored value. This pointer will be passed to the system's |
| /// `setsockopt` call (`man 3p setsockopt`, argument `option_value`). |
| fn ffi_ptr(&self) -> *const c_void; |
| /// Returns length of the stored value. This pointer will be passed to the system's |
| /// `setsockopt` call (`man 3p setsockopt`, argument `option_len`). |
| fn ffi_len(&self) -> socklen_t; |
| } |
| |
| /// Getter for an arbitrary `struct`. |
| 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().cast() |
| } |
| |
| 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" |
| ); |
| unsafe { self.val.assume_init() } |
| } |
| } |
| |
| /// Setter for an arbitrary `struct`. |
| 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 |
| } |
| } |
| |
| /// Getter for a boolean value. |
| 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().cast() |
| } |
| |
| 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" |
| ); |
| unsafe { self.val.assume_init() != 0 } |
| } |
| } |
| |
| /// Setter for a boolean value. |
| 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_val(&self.val) as socklen_t |
| } |
| } |
| |
| /// Getter for an `u8` value. |
| 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().cast() |
| } |
| |
| 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" |
| ); |
| unsafe { self.val.assume_init() } |
| } |
| } |
| |
| /// Setter for an `u8` value. |
| 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_val(&self.val) as socklen_t |
| } |
| } |
| |
| /// Getter for an `usize` value. |
| 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().cast() |
| } |
| |
| 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" |
| ); |
| unsafe { self.val.assume_init() as usize } |
| } |
| } |
| |
| /// Setter for an `usize` value. |
| 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_val(&self.val) as socklen_t |
| } |
| } |
| |
| /// Getter for a `OsString` value. |
| 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().cast() |
| } |
| |
| 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 = unsafe { self.val.assume_init() }; |
| OsStr::from_bytes(&v.as_mut()[0..len]).to_owned() |
| } |
| } |
| |
| /// Setter for a `OsString` value. |
| 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().cast() |
| } |
| |
| fn ffi_len(&self) -> socklen_t { |
| self.val.len() as socklen_t |
| } |
| } |
| |