diff options
Diffstat (limited to 'third_party/rust/nix/src/sys/socket/sockopt.rs')
-rw-r--r-- | third_party/rust/nix/src/sys/socket/sockopt.rs | 1422 |
1 files changed, 1422 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/socket/sockopt.rs b/third_party/rust/nix/src/sys/socket/sockopt.rs new file mode 100644 index 0000000000..06e9ee4563 --- /dev/null +++ b/third_party/rust/nix/src/sys/socket/sockopt.rs @@ -0,0 +1,1422 @@ +//! 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::{ + convert::TryFrom, + mem::{self, MaybeUninit} +}; +#[cfg(target_family = "unix")] +use std::os::unix::ffi::OsStrExt; +use std::os::unix::io::RawFd; + +// 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(&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) + } + } + } + }; +} + +/// 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(&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) + } + } + } + } + }; +} + +/// 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* +/// (`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`/`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(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); + }; + + /* + * 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(any(target_os = "illumos", target_os = "solaris")))] +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(any(target_os = "android", target_os = "linux"))] { + #[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(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")))] + /// 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 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(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +#[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 +); +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( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios" +))] +sockopt_impl!( + /// Get the credentials of the peer process of a connected unix domain + /// socket. + LocalPeerCred, + GetOnly, + 0, + libc::LOCAL_PEERCRED, + super::XuCred +); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Return the credentials of the foreign process connected to this socket. + 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")))] + /// 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( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux" +))] +#[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(any(target_os = "android", target_os = "linux"))] { + sockopt_impl!( + /// The maximum segment size for outgoing TCP packets. + TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32); + } else { + 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")))] +#[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(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +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")))] +#[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 +); +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(any(target_os = "android", target_os = "linux"))] +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(any(target_os = "android", target_os = "linux"))] +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(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Bind this socket to a particular device like “eth0”. + 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)] + // Not documented by Linux! + 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)] + // Not documented by Linux! + Ip6tOriginalDst, + GetOnly, + libc::SOL_IPV6, + libc::IP6T_SO_ORIGINAL_DST, + libc::sockaddr_in6 +); +#[cfg(any(target_os = "linux"))] +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(target_os = "haiku"))] +sockopt_impl!( + /// Enable or disable the receiving of the `SO_TIMESTAMP` control message. + ReceiveTimestamp, + Both, + libc::SOL_SOCKET, + libc::SO_TIMESTAMP, + bool +); +#[cfg(all(target_os = "linux"))] +sockopt_impl!( + /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message. + 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")))] + /// 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 = "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(any(target_os = "android", target_os = "linux"))] +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( + 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")))] + /// 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( + 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")))] + /// Set delivery of the `IPV6_PKTINFO` control message on incoming + /// datagrams. + 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")))] + /// 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(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")))] + /// 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(target_os = "android", target_os = "freebsd", target_os = "linux"))] +#[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(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +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(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Enable extended reliable error message passing. + Ipv4RecvErr, + Both, + libc::IPPROTO_IP, + libc::IP_RECVERR, + bool +); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Control receiving of asynchronous error options. + Ipv6RecvErr, + Both, + libc::IPPROTO_IPV6, + libc::IPV6_RECVERR, + bool +); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Fetch the current system-estimated Path MTU. + IpMtu, + GetOnly, + libc::IPPROTO_IP, + libc::IP_MTU, + libc::c_int +); +#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +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(target_os = "android", target_os = "freebsd", target_os = "linux"))] +sockopt_impl!( + /// Set the unicast hop limit for the socket. + 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")))] + /// The `recvmsg(2)` call will return the destination IP address for a UDP + /// datagram. + Ipv6OrigDstAddr, + Both, + libc::IPPROTO_IPV6, + libc::IPV6_ORIGDSTADDR, + bool +); +#[cfg(any(target_os = "ios", target_os = "macos"))] +sockopt_impl!( + /// Set "don't fragment packet" flag on the IP packet. + 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!( + /// 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(any(target_os = "android", target_os = "linux"))] +#[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(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)] +// Not documented by Linux! +#[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) + } + } +} + +/* + * + * ===== 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() 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() + } +} + +/// 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() 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 + } +} + +/// 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::<c_int>() 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() 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() + } +} + +/// 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::<c_int>() 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() 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 + } +} + +/// 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::<c_int>() 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() 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() + } +} + +/// 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() 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(); + } +} |