nix/sys/socket/
sockopt.rs

1//! Socket options as used by `setsockopt` and `getsockopt`.
2use cfg_if::cfg_if;
3use super::{GetSockOpt, SetSockOpt};
4use crate::Result;
5use crate::errno::Errno;
6use crate::sys::time::TimeVal;
7use libc::{self, c_int, c_void, socklen_t};
8use std::convert::TryFrom;
9use std::mem::{
10    self,
11    MaybeUninit
12};
13use std::os::unix::io::RawFd;
14use std::ffi::{OsStr, OsString};
15#[cfg(target_family = "unix")]
16use std::os::unix::ffi::OsStrExt;
17
18// Constants
19// TCP_CA_NAME_MAX isn't defined in user space include files
20#[cfg(any(target_os = "freebsd", target_os = "linux"))] 
21const TCP_CA_NAME_MAX: usize = 16;
22
23/// Helper for implementing `SetSockOpt` for a given socket option. See
24/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html).
25///
26/// This macro aims to help implementing `SetSockOpt` for different socket options that accept
27/// different kinds of data to be used with `setsockopt`.
28///
29/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
30/// you are implementing represents a simple type.
31///
32/// # Arguments
33///
34/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
35/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
36///    (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
37///    and more. Please refer to your system manual for more options. Will be passed as the second
38///    argument (`level`) to the `setsockopt` call.
39/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
40///    `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
41///    to the `setsockopt` call.
42/// * Type of the value that you are going to set.
43/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
44///    `bool`, `SetUsize` for `usize`, etc.).
45macro_rules! setsockopt_impl {
46    ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
47        impl SetSockOpt for $name {
48            type Val = $ty;
49
50            fn set(&self, fd: RawFd, val: &$ty) -> Result<()> {
51                unsafe {
52                    let setter: $setter = Set::new(val);
53
54                    let res = libc::setsockopt(fd, $level, $flag,
55                                               setter.ffi_ptr(),
56                                               setter.ffi_len());
57                    Errno::result(res).map(drop)
58                }
59            }
60        }
61    }
62}
63
64/// Helper for implementing `GetSockOpt` for a given socket option. See
65/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html).
66///
67/// This macro aims to help implementing `GetSockOpt` for different socket options that accept
68/// different kinds of data to be use with `getsockopt`.
69///
70/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
71/// you are implementing represents a simple type.
72///
73/// # Arguments
74///
75/// * Name of the type you want to implement `GetSockOpt` for.
76/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`),  *ip
77///    protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),  and more. Please refer
78///    to your system manual for more options. Will be passed as the second argument (`level`) to
79///    the `getsockopt` call.
80/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
81///    `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to
82///    the `getsockopt` call.
83/// * Type of the value that you are going to get.
84/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
85///    `bool`, `GetUsize` for `usize`, etc.).
86macro_rules! getsockopt_impl {
87    ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
88        impl GetSockOpt for $name {
89            type Val = $ty;
90
91            fn get(&self, fd: RawFd) -> Result<$ty> {
92                unsafe {
93                    let mut getter: $getter = Get::uninit();
94
95                    let res = libc::getsockopt(fd, $level, $flag,
96                                               getter.ffi_ptr(),
97                                               getter.ffi_len());
98                    Errno::result(res)?;
99
100                    match <$ty>::try_from(getter.assume_init()) {
101                        Err(_) => Err(Errno::EINVAL),
102                        Ok(r) => Ok(r)
103                    }
104                }
105            }
106        }
107    }
108}
109
110/// Helper to generate the sockopt accessors. See
111/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html) and
112/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html).
113///
114/// This macro aims to help implementing `GetSockOpt` and `SetSockOpt` for different socket options
115/// that accept different kinds of data to be use with `getsockopt` and `setsockopt` respectively.
116///
117/// Basically this macro wraps up the [`getsockopt_impl!`](macro.getsockopt_impl.html) and
118/// [`setsockopt_impl!`](macro.setsockopt_impl.html) macros.
119///
120/// # Arguments
121///
122/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
123///    both of them.
124/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
125/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
126///    (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
127///    and more. Please refer to your system manual for more options. Will be passed as the second
128///    argument (`level`) to the `getsockopt`/`setsockopt` call.
129/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
130///    `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
131///    to the `setsockopt`/`getsockopt` call.
132/// * `$ty:ty`: type of the value that will be get/set.
133/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
134/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
135// Some targets don't use all rules.
136#[allow(unknown_lints)]
137#[allow(unused_macro_rules)]
138macro_rules! sockopt_impl {
139    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
140        sockopt_impl!($(#[$attr])*
141                      $name, GetOnly, $level, $flag, bool, GetBool);
142    };
143
144    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
145        sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
146    };
147
148    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
149    {
150        sockopt_impl!($(#[$attr])*
151                      $name, GetOnly, $level, $flag, usize, GetUsize);
152    };
153
154    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
155        sockopt_impl!($(#[$attr])*
156                      $name, SetOnly, $level, $flag, bool, SetBool);
157    };
158
159    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
160        sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
161    };
162
163    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
164    {
165        sockopt_impl!($(#[$attr])*
166                      $name, SetOnly, $level, $flag, usize, SetUsize);
167    };
168
169    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
170        sockopt_impl!($(#[$attr])*
171                      $name, Both, $level, $flag, bool, GetBool, SetBool);
172    };
173
174    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
175        sockopt_impl!($(#[$attr])*
176                      $name, Both, $level, $flag, u8, GetU8, SetU8);
177    };
178
179    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
180        sockopt_impl!($(#[$attr])*
181                      $name, Both, $level, $flag, usize, GetUsize, SetUsize);
182    };
183
184    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
185     OsString<$array:ty>) =>
186    {
187        sockopt_impl!($(#[$attr])*
188                      $name, Both, $level, $flag, OsString, GetOsString<$array>,
189                      SetOsString);
190    };
191
192    /*
193     * Matchers with generic getter types must be placed at the end, so
194     * they'll only match _after_ specialized matchers fail
195     */
196    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
197    {
198        sockopt_impl!($(#[$attr])*
199                      $name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
200    };
201
202    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
203     $getter:ty) =>
204    {
205        $(#[$attr])*
206        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
207        pub struct $name;
208
209        getsockopt_impl!($name, $level, $flag, $ty, $getter);
210    };
211
212    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
213    {
214        sockopt_impl!($(#[$attr])*
215                      $name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
216    };
217
218    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
219     $setter:ty) =>
220    {
221        $(#[$attr])*
222        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
223        pub struct $name;
224
225        setsockopt_impl!($name, $level, $flag, $ty, $setter);
226    };
227
228    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty,
229     $getter:ty, $setter:ty) =>
230    {
231        $(#[$attr])*
232        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
233        pub struct $name;
234
235        setsockopt_impl!($name, $level, $flag, $ty, $setter);
236        getsockopt_impl!($name, $level, $flag, $ty, $getter);
237    };
238
239    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
240        sockopt_impl!($(#[$attr])*
241                      $name, Both, $level, $flag, $ty, GetStruct<$ty>,
242                      SetStruct<$ty>);
243    };
244}
245
246/*
247 *
248 * ===== Define sockopts =====
249 *
250 */
251
252sockopt_impl!(
253    /// Enables local address reuse
254    ReuseAddr, Both, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool
255);
256#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
257sockopt_impl!(
258    /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
259    /// identical socket address.
260    ReusePort, Both, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool);
261sockopt_impl!(
262    /// Under most circumstances, TCP sends data when it is presented; when
263    /// outstanding data has not yet been acknowledged, it gathers small amounts
264    /// of output to be sent in a single packet once an acknowledgement is
265    /// received.  For a small number of clients, such as window systems that
266    /// send a stream of mouse events which receive no replies, this
267    /// packetization may cause significant delays.  The boolean option
268    /// TCP_NODELAY defeats this algorithm.
269    TcpNoDelay, Both, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool);
270sockopt_impl!(
271    /// When enabled,  a close(2) or shutdown(2) will not return until all
272    /// queued messages for the socket have been successfully sent or the
273    /// linger timeout has been reached.
274    Linger, Both, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger);
275sockopt_impl!(
276    /// Join a multicast group
277    IpAddMembership, SetOnly, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP,
278    super::IpMembershipRequest);
279sockopt_impl!(
280    /// Leave a multicast group.
281    IpDropMembership, SetOnly, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP,
282    super::IpMembershipRequest);
283cfg_if! {
284    if #[cfg(any(target_os = "android", target_os = "linux"))] {
285        sockopt_impl!(
286            /// Join an IPv6 multicast group.
287            Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
288        sockopt_impl!(
289            /// Leave an IPv6 multicast group.
290            Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
291    } else if #[cfg(any(target_os = "dragonfly",
292                        target_os = "freebsd",
293                        target_os = "illumos",
294                        target_os = "ios",
295                        target_os = "macos",
296                        target_os = "netbsd",
297                        target_os = "openbsd",
298                        target_os = "solaris"))] {
299        sockopt_impl!(
300            /// Join an IPv6 multicast group.
301            Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
302            libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
303        sockopt_impl!(
304            /// Leave an IPv6 multicast group.
305            Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
306            libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
307    }
308}
309sockopt_impl!(
310    /// Set or read the time-to-live value of outgoing multicast packets for
311    /// this socket.
312    IpMulticastTtl, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8);
313sockopt_impl!(
314    /// Set or read a boolean integer argument that determines whether sent
315    /// multicast packets should be looped back to the local sockets.
316    IpMulticastLoop, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool);
317#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
318sockopt_impl!(
319    /// If enabled, this boolean option allows binding to an IP address that
320    /// is nonlocal or does not (yet) exist.
321    IpFreebind, Both, libc::IPPROTO_IP, libc::IP_FREEBIND, bool);
322sockopt_impl!(
323    /// Specify the receiving timeout until reporting an error.
324    ReceiveTimeout, Both, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal);
325sockopt_impl!(
326    /// Specify the sending timeout until reporting an error.
327    SendTimeout, Both, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal);
328sockopt_impl!(
329    /// Set or get the broadcast flag.
330    Broadcast, Both, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
331sockopt_impl!(
332    /// If this option is enabled, out-of-band data is directly placed into
333    /// the receive data stream.
334    OobInline, Both, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
335sockopt_impl!(
336    /// Get and clear the pending socket error.
337    SocketError, GetOnly, libc::SOL_SOCKET, libc::SO_ERROR, i32);
338sockopt_impl!(
339    /// Enable sending of keep-alive messages on connection-oriented sockets.
340    KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
341#[cfg(any(
342        target_os = "dragonfly",
343        target_os = "freebsd",
344        target_os = "macos",
345        target_os = "ios"
346))]
347sockopt_impl!(
348    /// Get the credentials of the peer process of a connected unix domain
349    /// socket.
350    LocalPeerCred, GetOnly, 0, libc::LOCAL_PEERCRED, super::XuCred);
351#[cfg(any(target_os = "android", target_os = "linux"))]
352sockopt_impl!(
353    /// Return the credentials of the foreign process connected to this socket.
354    PeerCredentials, GetOnly, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
355#[cfg(any(target_os = "ios",
356          target_os = "macos"))]
357sockopt_impl!(
358    /// Specify the amount of time, in seconds, that the connection must be idle
359    /// before keepalive probes (if enabled) are sent.
360    TcpKeepAlive, Both, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32);
361#[cfg(any(target_os = "android",
362          target_os = "dragonfly",
363          target_os = "freebsd",
364          target_os = "linux",
365          target_os = "nacl"))]
366sockopt_impl!(
367    /// The time (in seconds) the connection needs to remain idle before TCP
368    /// starts sending keepalive probes
369    TcpKeepIdle, Both, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32);
370cfg_if! {
371    if #[cfg(any(target_os = "android", target_os = "linux"))] {
372        sockopt_impl!(
373            /// The maximum segment size for outgoing TCP packets.
374            TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
375    } else {
376        sockopt_impl!(
377            /// The maximum segment size for outgoing TCP packets.
378            TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
379    }
380}
381#[cfg(not(target_os = "openbsd"))]
382sockopt_impl!(
383    /// The maximum number of keepalive probes TCP should send before
384    /// dropping the connection.
385    TcpKeepCount, Both, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32);
386#[cfg(any(target_os = "android",
387          target_os = "fuchsia",
388          target_os = "linux"))]
389sockopt_impl!(
390    #[allow(missing_docs)]
391    // Not documented by Linux!
392    TcpRepair, Both, libc::IPPROTO_TCP, libc::TCP_REPAIR, u32);
393#[cfg(not(target_os = "openbsd"))]
394sockopt_impl!(
395    /// The time (in seconds) between individual keepalive probes.
396    TcpKeepInterval, Both, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32);
397#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
398sockopt_impl!(
399    /// Specifies the maximum amount of time in milliseconds that transmitted
400    /// data may remain unacknowledged before TCP will forcibly close the
401    /// corresponding connection
402    TcpUserTimeout, Both, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT, u32);
403sockopt_impl!(
404    /// Sets or gets the maximum socket receive buffer in bytes.
405    RcvBuf, Both, libc::SOL_SOCKET, libc::SO_RCVBUF, usize);
406sockopt_impl!(
407    /// Sets or gets the maximum socket send buffer in bytes.
408    SndBuf, Both, libc::SOL_SOCKET, libc::SO_SNDBUF, usize);
409#[cfg(any(target_os = "android", target_os = "linux"))]
410sockopt_impl!(
411    /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
412    /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be
413    /// overridden.
414    RcvBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize);
415#[cfg(any(target_os = "android", target_os = "linux"))]
416sockopt_impl!(
417    /// Using this socket option, a privileged (`CAP_NET_ADMIN`)  process can
418    /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be
419    /// overridden.
420    SndBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize);
421sockopt_impl!(
422    /// Gets the socket type as an integer.
423    SockType, GetOnly, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType, GetStruct<i32>);
424sockopt_impl!(
425    /// Returns a value indicating whether or not this socket has been marked to
426    /// accept connections with `listen(2)`.
427    AcceptConn, GetOnly, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
428#[cfg(any(target_os = "android", target_os = "linux"))]
429sockopt_impl!(
430    /// Bind this socket to a particular device like “eth0”.
431    BindToDevice, Both, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
432#[cfg(any(target_os = "android", target_os = "linux"))]
433sockopt_impl!(
434    #[allow(missing_docs)]
435    // Not documented by Linux!
436    OriginalDst, GetOnly, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
437#[cfg(any(target_os = "android", target_os = "linux"))]
438sockopt_impl!(
439    #[allow(missing_docs)]
440    // Not documented by Linux!
441    Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6);
442sockopt_impl!( 
443    /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
444    ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
445#[cfg(all(target_os = "linux"))]
446sockopt_impl!(
447    /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message.
448    ReceiveTimestampns, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
449#[cfg(any(target_os = "android", target_os = "linux"))]
450sockopt_impl!(
451    /// Setting this boolean option enables transparent proxying on this socket.
452    IpTransparent, Both, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
453#[cfg(target_os = "openbsd")]
454sockopt_impl!(
455    /// Allows the socket to be bound to addresses which are not local to the
456    /// machine, so it can be used to make a transparent proxy.
457    BindAny, Both, libc::SOL_SOCKET, libc::SO_BINDANY, bool);
458#[cfg(target_os = "freebsd")]
459sockopt_impl!(
460    /// Can `bind(2)` to any address, even one not bound to any available
461    /// network interface in the system.
462    BindAny, Both, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
463#[cfg(target_os = "linux")]
464sockopt_impl!(
465    /// Set the mark for each packet sent through this socket (similar to the
466    /// netfilter MARK target but socket-based).
467    Mark, Both, libc::SOL_SOCKET, libc::SO_MARK, u32);
468#[cfg(any(target_os = "android", target_os = "linux"))]
469sockopt_impl!(
470    /// Enable or disable the receiving of the `SCM_CREDENTIALS` control
471    /// message.
472    PassCred, Both, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
473#[cfg(any(target_os = "freebsd", target_os = "linux"))] 
474sockopt_impl!(
475    /// This option allows the caller to set the TCP congestion control
476    /// algorithm to be used,  on a per-socket basis.
477    TcpCongestion, Both, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>);
478#[cfg(any(
479    target_os = "android",
480    target_os = "ios",
481    target_os = "linux",
482    target_os = "macos",
483    target_os = "netbsd",
484))]
485sockopt_impl!(
486    /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo
487    /// structure that supplies some information about the incoming packet.
488    Ipv4PacketInfo, Both, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
489#[cfg(any(
490    target_os = "android",
491    target_os = "freebsd",
492    target_os = "ios",
493    target_os = "linux",
494    target_os = "macos",
495    target_os = "netbsd",
496    target_os = "openbsd",
497))]
498sockopt_impl!(
499    /// Set delivery of the `IPV6_PKTINFO` control message on incoming
500    /// datagrams.
501    Ipv6RecvPacketInfo, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
502#[cfg(any(
503    target_os = "freebsd",
504    target_os = "ios",
505    target_os = "macos",
506    target_os = "netbsd",
507    target_os = "openbsd",
508))]
509sockopt_impl!(
510    /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to
511    /// the interface on which the packet was received.
512    Ipv4RecvIf, Both, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
513#[cfg(any(
514    target_os = "freebsd",
515    target_os = "ios",
516    target_os = "macos",
517    target_os = "netbsd",
518    target_os = "openbsd",
519))]
520sockopt_impl!(
521    /// The `recvmsg(2)` call will return the destination IP address for a UDP
522    /// datagram.
523    Ipv4RecvDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
524#[cfg(target_os = "linux")]
525sockopt_impl!(
526    #[allow(missing_docs)]
527    // Not documented by Linux!
528    UdpGsoSegment, Both, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
529#[cfg(target_os = "linux")]
530sockopt_impl!(
531    #[allow(missing_docs)]
532    // Not documented by Linux!
533    UdpGroSegment, Both, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
534#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
535sockopt_impl!(
536    /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should
537    /// be attached to received skbs indicating the number of packets dropped by
538    /// the socket since its creation.
539    RxqOvfl, Both, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int);
540sockopt_impl!(
541    /// The socket is restricted to sending and receiving IPv6 packets only.
542    Ipv6V6Only, Both, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, bool);
543#[cfg(any(target_os = "android", target_os = "linux"))]
544sockopt_impl!(
545    /// Enable extended reliable error message passing.
546    Ipv4RecvErr, Both, libc::IPPROTO_IP, libc::IP_RECVERR, bool);
547#[cfg(any(target_os = "android", target_os = "linux"))]
548sockopt_impl!(
549    /// Control receiving of asynchronous error options.
550    Ipv6RecvErr, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVERR, bool);
551#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
552sockopt_impl!(
553    /// Set or retrieve the current time-to-live field that is used in every
554    /// packet sent from this socket.
555    Ipv4Ttl, Both, libc::IPPROTO_IP, libc::IP_TTL, libc::c_int);
556#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
557sockopt_impl!(
558    /// Set the unicast hop limit for the socket.
559    Ipv6Ttl, Both, libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS, libc::c_int);
560
561#[allow(missing_docs)]
562// Not documented by Linux!
563#[cfg(any(target_os = "android", target_os = "linux"))]
564#[derive(Copy, Clone, Debug)]
565pub struct AlgSetAeadAuthSize;
566
567// ALG_SET_AEAD_AUTH_SIZE read the length from passed `option_len`
568// See https://elixir.bootlin.com/linux/v4.4/source/crypto/af_alg.c#L222
569#[cfg(any(target_os = "android", target_os = "linux"))]
570impl SetSockOpt for AlgSetAeadAuthSize {
571    type Val = usize;
572
573    fn set(&self, fd: RawFd, val: &usize) -> Result<()> {
574        unsafe {
575            let res = libc::setsockopt(fd,
576                                       libc::SOL_ALG,
577                                       libc::ALG_SET_AEAD_AUTHSIZE,
578                                       ::std::ptr::null(),
579                                       *val as libc::socklen_t);
580            Errno::result(res).map(drop)
581        }
582    }
583}
584
585#[allow(missing_docs)]
586// Not documented by Linux!
587#[cfg(any(target_os = "android", target_os = "linux"))]
588#[derive(Clone, Debug)]
589pub struct AlgSetKey<T>(::std::marker::PhantomData<T>);
590
591#[cfg(any(target_os = "android", target_os = "linux"))]
592impl<T> Default for AlgSetKey<T> {
593    fn default() -> Self {
594        AlgSetKey(Default::default())
595    }
596}
597
598#[cfg(any(target_os = "android", target_os = "linux"))]
599impl<T> SetSockOpt for AlgSetKey<T> where T: AsRef<[u8]> + Clone {
600    type Val = T;
601
602    fn set(&self, fd: RawFd, val: &T) -> Result<()> {
603        unsafe {
604            let res = libc::setsockopt(fd,
605                                       libc::SOL_ALG,
606                                       libc::ALG_SET_KEY,
607                                       val.as_ref().as_ptr() as *const _,
608                                       val.as_ref().len() as libc::socklen_t);
609            Errno::result(res).map(drop)
610        }
611    }
612}
613
614/*
615 *
616 * ===== Accessor helpers =====
617 *
618 */
619
620/// Helper trait that describes what is expected from a `GetSockOpt` getter.
621trait Get<T> {
622    /// Returns an uninitialized value.
623    fn uninit() -> Self;
624    /// Returns a pointer to the stored value. This pointer will be passed to the system's
625    /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`).
626    fn ffi_ptr(&mut self) -> *mut c_void;
627    /// Returns length of the stored value. This pointer will be passed to the system's
628    /// `getsockopt` call (`man 3p getsockopt`, argument `option_len`).
629    fn ffi_len(&mut self) -> *mut socklen_t;
630    /// Returns the hopefully initialized inner value.
631    unsafe fn assume_init(self) -> T;
632}
633
634/// Helper trait that describes what is expected from a `SetSockOpt` setter.
635trait Set<'a, T> {
636    /// Initialize the setter with a given value.
637    fn new(val: &'a T) -> Self;
638    /// Returns a pointer to the stored value. This pointer will be passed to the system's
639    /// `setsockopt` call (`man 3p setsockopt`, argument `option_value`).
640    fn ffi_ptr(&self) -> *const c_void;
641    /// Returns length of the stored value. This pointer will be passed to the system's
642    /// `setsockopt` call (`man 3p setsockopt`, argument `option_len`).
643    fn ffi_len(&self) -> socklen_t;
644}
645
646/// Getter for an arbitrary `struct`.
647struct GetStruct<T> {
648    len: socklen_t,
649    val: MaybeUninit<T>,
650}
651
652impl<T> Get<T> for GetStruct<T> {
653    fn uninit() -> Self {
654        GetStruct {
655            len: mem::size_of::<T>() as socklen_t,
656            val: MaybeUninit::uninit(),
657        }
658    }
659
660    fn ffi_ptr(&mut self) -> *mut c_void {
661        self.val.as_mut_ptr() as *mut c_void
662    }
663
664    fn ffi_len(&mut self) -> *mut socklen_t {
665        &mut self.len
666    }
667
668    unsafe fn assume_init(self) -> T {
669        assert_eq!(self.len as usize, mem::size_of::<T>(), "invalid getsockopt implementation");
670        self.val.assume_init()
671    }
672}
673
674/// Setter for an arbitrary `struct`.
675struct SetStruct<'a, T: 'static> {
676    ptr: &'a T,
677}
678
679impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
680    fn new(ptr: &'a T) -> SetStruct<'a, T> {
681        SetStruct { ptr }
682    }
683
684    fn ffi_ptr(&self) -> *const c_void {
685        self.ptr as *const T as *const c_void
686    }
687
688    fn ffi_len(&self) -> socklen_t {
689        mem::size_of::<T>() as socklen_t
690    }
691}
692
693/// Getter for a boolean value.
694struct GetBool {
695    len: socklen_t,
696    val: MaybeUninit<c_int>,
697}
698
699impl Get<bool> for GetBool {
700    fn uninit() -> Self {
701        GetBool {
702            len: mem::size_of::<c_int>() as socklen_t,
703            val: MaybeUninit::uninit(),
704        }
705    }
706
707    fn ffi_ptr(&mut self) -> *mut c_void {
708        self.val.as_mut_ptr() as *mut c_void
709    }
710
711    fn ffi_len(&mut self) -> *mut socklen_t {
712        &mut self.len
713    }
714
715    unsafe fn assume_init(self) -> bool {
716        assert_eq!(self.len as usize, mem::size_of::<c_int>(), "invalid getsockopt implementation");
717        self.val.assume_init() != 0
718    }
719}
720
721/// Setter for a boolean value.
722struct SetBool {
723    val: c_int,
724}
725
726impl<'a> Set<'a, bool> for SetBool {
727    fn new(val: &'a bool) -> SetBool {
728        SetBool { val: i32::from(*val) }
729    }
730
731    fn ffi_ptr(&self) -> *const c_void {
732        &self.val as *const c_int as *const c_void
733    }
734
735    fn ffi_len(&self) -> socklen_t {
736        mem::size_of::<c_int>() as socklen_t
737    }
738}
739
740/// Getter for an `u8` value.
741struct GetU8 {
742    len: socklen_t,
743    val: MaybeUninit<u8>,
744}
745
746impl Get<u8> for GetU8 {
747    fn uninit() -> Self {
748        GetU8 {
749            len: mem::size_of::<u8>() as socklen_t,
750            val: MaybeUninit::uninit(),
751        }
752    }
753
754    fn ffi_ptr(&mut self) -> *mut c_void {
755        self.val.as_mut_ptr() as *mut c_void
756    }
757
758    fn ffi_len(&mut self) -> *mut socklen_t {
759        &mut self.len
760    }
761
762    unsafe fn assume_init(self) -> u8 {
763        assert_eq!(self.len as usize, mem::size_of::<u8>(), "invalid getsockopt implementation");
764        self.val.assume_init()
765    }
766}
767
768/// Setter for an `u8` value.
769struct SetU8 {
770    val: u8,
771}
772
773impl<'a> Set<'a, u8> for SetU8 {
774    fn new(val: &'a u8) -> SetU8 {
775        SetU8 { val: *val }
776    }
777
778    fn ffi_ptr(&self) -> *const c_void {
779        &self.val as *const u8 as *const c_void
780    }
781
782    fn ffi_len(&self) -> socklen_t {
783        mem::size_of::<c_int>() as socklen_t
784    }
785}
786
787/// Getter for an `usize` value.
788struct GetUsize {
789    len: socklen_t,
790    val: MaybeUninit<c_int>,
791}
792
793impl Get<usize> for GetUsize {
794    fn uninit() -> Self {
795        GetUsize {
796            len: mem::size_of::<c_int>() as socklen_t,
797            val: MaybeUninit::uninit(),
798        }
799    }
800
801    fn ffi_ptr(&mut self) -> *mut c_void {
802        self.val.as_mut_ptr() as *mut c_void
803    }
804
805    fn ffi_len(&mut self) -> *mut socklen_t {
806        &mut self.len
807    }
808
809    unsafe fn assume_init(self) -> usize {
810        assert_eq!(self.len as usize, mem::size_of::<c_int>(), "invalid getsockopt implementation");
811        self.val.assume_init() as usize
812    }
813}
814
815/// Setter for an `usize` value.
816struct SetUsize {
817    val: c_int,
818}
819
820impl<'a> Set<'a, usize> for SetUsize {
821    fn new(val: &'a usize) -> SetUsize {
822        SetUsize { val: *val as c_int }
823    }
824
825    fn ffi_ptr(&self) -> *const c_void {
826        &self.val as *const c_int as *const c_void
827    }
828
829    fn ffi_len(&self) -> socklen_t {
830        mem::size_of::<c_int>() as socklen_t
831    }
832}
833
834/// Getter for a `OsString` value.
835struct GetOsString<T: AsMut<[u8]>> {
836    len: socklen_t,
837    val: MaybeUninit<T>,
838}
839
840impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
841    fn uninit() -> Self {
842        GetOsString {
843            len: mem::size_of::<T>() as socklen_t,
844            val: MaybeUninit::uninit(),
845        }
846    }
847
848    fn ffi_ptr(&mut self) -> *mut c_void {
849        self.val.as_mut_ptr() as *mut c_void
850    }
851
852    fn ffi_len(&mut self) -> *mut socklen_t {
853        &mut self.len
854    }
855
856    unsafe fn assume_init(self) -> OsString {
857        let len = self.len as usize;
858        let mut v = self.val.assume_init();
859        OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
860    }
861}
862
863/// Setter for a `OsString` value.
864struct SetOsString<'a> {
865    val: &'a OsStr,
866}
867
868impl<'a> Set<'a, OsString> for SetOsString<'a> {
869    fn new(val: &'a OsString) -> SetOsString {
870        SetOsString { val: val.as_os_str() }
871    }
872
873    fn ffi_ptr(&self) -> *const c_void {
874        self.val.as_bytes().as_ptr() as *const c_void
875    }
876
877    fn ffi_len(&self) -> socklen_t {
878        self.val.len() as socklen_t
879    }
880}
881
882
883#[cfg(test)]
884mod test {
885    #[cfg(any(target_os = "android", target_os = "linux"))]
886    #[test]
887    fn can_get_peercred_on_unix_socket() {
888        use super::super::*;
889
890        let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap();
891        let a_cred = getsockopt(a, super::PeerCredentials).unwrap();
892        let b_cred = getsockopt(b, super::PeerCredentials).unwrap();
893        assert_eq!(a_cred, b_cred);
894        assert!(a_cred.pid() != 0);
895    }
896
897    #[test]
898    fn is_socket_type_unix() {
899        use super::super::*;
900        use crate::unistd::close;
901
902        let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap();
903        let a_type = getsockopt(a, super::SockType).unwrap();
904        assert_eq!(a_type, SockType::Stream);
905        close(a).unwrap();
906        close(b).unwrap();
907    }
908
909    #[test]
910    fn is_socket_type_dgram() {
911        use super::super::*;
912        use crate::unistd::close;
913
914        let s = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
915        let s_type = getsockopt(s, super::SockType).unwrap();
916        assert_eq!(s_type, SockType::Datagram);
917        close(s).unwrap();
918    }
919
920    #[cfg(any(target_os = "freebsd",
921              target_os = "linux",
922              target_os = "nacl"))]
923    #[test]
924    fn can_get_listen_on_tcp_socket() {
925        use super::super::*;
926        use crate::unistd::close;
927
928        let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
929        let s_listening = getsockopt(s, super::AcceptConn).unwrap();
930        assert!(!s_listening);
931        listen(s, 10).unwrap();
932        let s_listening2 = getsockopt(s, super::AcceptConn).unwrap();
933        assert!(s_listening2);
934        close(s).unwrap();
935    }
936
937}