Skip to main content

rama_net/socket/
opts.rs

1//! Options and types in function of creating [`Socket`]s.
2
3use std::{
4    io,
5    net::{Ipv4Addr, SocketAddr},
6    time::Duration,
7};
8
9use super::core::{
10    Domain as SocketDomain, Protocol as SocketProtocol, SockAddr, Socket,
11    TcpKeepalive as SocketTcpKeepAlive, Type as SocketType,
12};
13use crate::address::SocketAddress;
14
15use serde::{Deserialize, Serialize};
16
17/// Specification of the communication domain for a [`Socket`].
18#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, Eq, PartialEq)]
19pub enum Domain {
20    /// Domain for IPv4 communication, corresponding to `AF_INET`.
21    #[default]
22    IPv4,
23    /// Domain for IPv6 communication, corresponding to `AF_INET6`.
24    IPv6,
25    /// Domain for Unix socket communication, corresponding to `AF_UNIX`.
26    Unix,
27}
28
29impl From<SocketAddr> for Domain {
30    fn from(value: SocketAddr) -> Self {
31        if value.is_ipv4() {
32            Self::IPv4
33        } else {
34            Self::IPv6
35        }
36    }
37}
38
39impl From<SocketAddress> for Domain {
40    fn from(value: SocketAddress) -> Self {
41        if value.ip_addr.is_ipv4() {
42            Self::IPv4
43        } else {
44            Self::IPv6
45        }
46    }
47}
48
49impl Domain {
50    #[inline]
51    #[must_use]
52    pub fn as_socket_domain(self) -> SocketDomain {
53        self.into()
54    }
55}
56
57impl From<Domain> for SocketDomain {
58    fn from(value: Domain) -> Self {
59        match value {
60            Domain::IPv4 => Self::IPV4,
61            Domain::IPv6 => Self::IPV6,
62            Domain::Unix => Self::UNIX,
63        }
64    }
65}
66
67/// Protocol specification used for creating [`Socket`]s.
68#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, Eq, PartialEq)]
69pub enum Protocol {
70    /// Protocol corresponding to `ICMPv4` (`sys::IPPROTO_ICMP`)
71    ICMPV4,
72    /// Protocol corresponding to `ICMPv6` (`sys::IPPROTO_ICMPV6`)
73    ICMPV6,
74    #[default]
75    /// Protocol corresponding to `TCP` (`sys::IPPROTO_TCP`)
76    TCP,
77    /// Protocol corresponding to `UDP` (`sys::IPPROTO_UDP`)
78    UDP,
79    #[cfg(target_os = "linux")]
80    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
81    /// Protocol corresponding to `MPTCP` (`sys::IPPROTO_MPTCP`)
82    MPTCP,
83    #[cfg(target_os = "linux")]
84    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
85    /// Protocol corresponding to `DCCP` (`sys::IPPROTO_DCCP`)
86    DCCP,
87    #[cfg(any(target_os = "freebsd", target_os = "linux"))]
88    #[cfg_attr(docsrs, doc(cfg(any(target_os = "freebsd", target_os = "linux"))))]
89    /// Protocol corresponding to `SCTP` (`sys::IPPROTO_SCTP`)
90    SCTP,
91}
92
93impl Protocol {
94    #[inline]
95    #[must_use]
96    pub fn as_socket_protocol(self) -> SocketProtocol {
97        self.into()
98    }
99}
100
101impl From<Protocol> for SocketProtocol {
102    fn from(value: Protocol) -> Self {
103        match value {
104            Protocol::ICMPV4 => Self::ICMPV4,
105            Protocol::ICMPV6 => Self::ICMPV6,
106            Protocol::TCP => Self::TCP,
107            Protocol::UDP => Self::UDP,
108            #[cfg(target_os = "linux")]
109            #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
110            Protocol::MPTCP => Self::MPTCP,
111            #[cfg(target_os = "linux")]
112            #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
113            Protocol::DCCP => Self::DCCP,
114            #[cfg(any(target_os = "freebsd", target_os = "linux"))]
115            #[cfg_attr(docsrs, doc(cfg(any(target_os = "freebsd", target_os = "linux"))))]
116            Protocol::SCTP => Self::SCTP,
117        }
118    }
119}
120
121/// Specification of communication semantics on a [`Socket`].
122#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, Eq, PartialEq)]
123pub enum Type {
124    /// Type corresponding to `SOCK_STREAM`.
125    ///
126    /// Used for protocols such as [`Protocol::TCP`].
127    #[default]
128    Stream,
129    /// Type corresponding to `SOCK_DGRAM`.
130    ///
131    /// Used for protocols such as [`Protocol::UDP`].
132    Datagram,
133    #[cfg(target_os = "linux")]
134    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
135    /// Type corresponding to `SOCK_DCCP`.
136    ///
137    /// Used for the [`Protocol::DCCP`].
138    DCCP,
139    #[cfg(not(target_os = "espidf"))]
140    #[cfg_attr(docsrs, doc(cfg(not(target_os = "espidf"))))]
141    /// Type corresponding to `SOCK_SEQPACKET`.
142    SequencePacket,
143    #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
144    #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "espidf")))))]
145    /// Type corresponding to `SOCK_RAW`.
146    Raw,
147}
148
149impl Type {
150    #[inline]
151    #[must_use]
152    pub fn as_socket_type(self) -> SocketType {
153        self.into()
154    }
155}
156
157impl From<Type> for SocketType {
158    fn from(value: Type) -> Self {
159        match value {
160            Type::Stream => Self::STREAM,
161            Type::Datagram => Self::DGRAM,
162            #[cfg(target_os = "linux")]
163            #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
164            Type::DCCP => Self::DCCP,
165            #[cfg(not(target_os = "espidf"))]
166            #[cfg_attr(docsrs, doc(cfg(not(target_os = "espidf"))))]
167            Type::SequencePacket => Self::SEQPACKET,
168            #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
169            #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "espidf")))))]
170            Type::Raw => Self::RAW,
171        }
172    }
173}
174
175#[derive(Debug, Clone, Default, Serialize)]
176/// Configures a [`Socket`]'s TCP keepalive parameters.
177///
178/// See [`SocketOptions::tcp_keep_alive`].
179pub struct TcpKeepAlive {
180    /// Set the amount of time after which TCP keepalive probes will be sent on
181    /// idle connections.
182    ///
183    /// This will set `TCP_KEEPALIVE` on macOS and iOS, and
184    /// `TCP_KEEPIDLE` on all other Unix operating systems, except
185    /// OpenBSD and Haiku which don't support any way to set this
186    /// option. On Windows, this sets the value of the `tcp_keepalive`
187    /// struct's `keepalivetime` field.
188    ///
189    /// Some platforms specify this value in seconds, so sub-second
190    /// specifications may be omitted.
191    pub time: Option<Duration>,
192
193    #[cfg(not(any(
194        target_os = "openbsd",
195        target_os = "redox",
196        target_os = "solaris",
197        target_os = "nto",
198        target_os = "espidf",
199        target_os = "vita",
200        target_os = "haiku",
201    )))]
202    #[cfg_attr(
203        docsrs,
204        doc(cfg(not(any(
205            target_os = "openbsd",
206            target_os = "redox",
207            target_os = "solaris",
208            target_os = "nto",
209            target_os = "espidf",
210            target_os = "vita",
211            target_os = "haiku",
212        ))))
213    )]
214    /// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the
215    /// value of the `tcp_keepalive` struct's `keepaliveinterval` field.
216    ///
217    /// Sets the time interval between TCP keepalive probes.
218    ///
219    /// Some platforms specify this value in seconds, so sub-second
220    /// specifications may be omitted.
221    pub interval: Option<Duration>,
222
223    #[cfg(not(any(
224        target_os = "openbsd",
225        target_os = "redox",
226        target_os = "solaris",
227        target_os = "windows",
228        target_os = "nto",
229        target_os = "espidf",
230        target_os = "vita",
231        target_os = "haiku",
232    )))]
233    #[cfg_attr(
234        docsrs,
235        doc(cfg(not(any(
236            target_os = "openbsd",
237            target_os = "redox",
238            target_os = "solaris",
239            target_os = "windows",
240            target_os = "nto",
241            target_os = "espidf",
242            target_os = "vita",
243            target_os = "haiku",
244        ))))
245    )]
246    /// Set the value of the `TCP_KEEPCNT` option.
247    ///
248    /// Set the maximum number of TCP keepalive probes that will be sent before
249    /// dropping a connection, if TCP keepalive is enabled on this [`Socket`].
250    pub retries: Option<u32>,
251}
252
253impl<'de> Deserialize<'de> for TcpKeepAlive {
254    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
255    where
256        D: serde::Deserializer<'de>,
257    {
258        #[derive(Deserialize)]
259        #[serde(untagged)]
260        enum Variants {
261            Time(Option<Duration>),
262            Options {
263                time: Option<Duration>,
264                #[cfg(not(any(
265                    target_os = "openbsd",
266                    target_os = "redox",
267                    target_os = "solaris",
268                    target_os = "nto",
269                    target_os = "espidf",
270                    target_os = "vita",
271                    target_os = "haiku",
272                )))]
273                interval: Option<Duration>,
274                #[cfg(not(any(
275                    target_os = "openbsd",
276                    target_os = "redox",
277                    target_os = "solaris",
278                    target_os = "windows",
279                    target_os = "nto",
280                    target_os = "espidf",
281                    target_os = "vita",
282                    target_os = "haiku",
283                )))]
284                retries: Option<u32>,
285            },
286        }
287
288        match Variants::deserialize(deserializer)? {
289            Variants::Time(time) => Ok(Self {
290                time,
291                ..Default::default()
292            }),
293            Variants::Options {
294                time,
295                #[cfg(not(any(
296                    target_os = "openbsd",
297                    target_os = "redox",
298                    target_os = "solaris",
299                    target_os = "nto",
300                    target_os = "espidf",
301                    target_os = "vita",
302                    target_os = "haiku",
303                )))]
304                interval,
305                #[cfg(not(any(
306                    target_os = "openbsd",
307                    target_os = "redox",
308                    target_os = "solaris",
309                    target_os = "windows",
310                    target_os = "nto",
311                    target_os = "espidf",
312                    target_os = "vita",
313                    target_os = "haiku",
314                )))]
315                retries,
316            } => Ok(Self {
317                time,
318                #[cfg(not(any(
319                    target_os = "openbsd",
320                    target_os = "redox",
321                    target_os = "solaris",
322                    target_os = "nto",
323                    target_os = "espidf",
324                    target_os = "vita",
325                    target_os = "haiku",
326                )))]
327                interval,
328                #[cfg(not(any(
329                    target_os = "openbsd",
330                    target_os = "redox",
331                    target_os = "solaris",
332                    target_os = "windows",
333                    target_os = "nto",
334                    target_os = "espidf",
335                    target_os = "vita",
336                    target_os = "haiku",
337                )))]
338                retries,
339            }),
340        }
341    }
342}
343
344impl TcpKeepAlive {
345    #[inline]
346    #[must_use]
347    pub fn into_socket_keep_alive(self) -> SocketTcpKeepAlive {
348        self.into()
349    }
350}
351
352impl From<TcpKeepAlive> for SocketTcpKeepAlive {
353    fn from(value: TcpKeepAlive) -> Self {
354        let ka = Self::new();
355
356        let ka = match value.time {
357            Some(time) => ka.with_time(time),
358            None => ka,
359        };
360
361        #[cfg(not(any(
362            target_os = "openbsd",
363            target_os = "redox",
364            target_os = "solaris",
365            target_os = "nto",
366            target_os = "espidf",
367            target_os = "vita",
368            target_os = "haiku",
369        )))]
370        let ka = match value.interval {
371            Some(interval) => ka.with_interval(interval),
372            None => ka,
373        };
374
375        #[cfg(not(any(
376            target_os = "openbsd",
377            target_os = "redox",
378            target_os = "solaris",
379            target_os = "windows",
380            target_os = "nto",
381            target_os = "espidf",
382            target_os = "vita",
383            target_os = "haiku",
384        )))]
385        let ka = match value.retries {
386            Some(retries) => ka.with_retries(retries),
387            None => ka,
388        };
389
390        ka
391    }
392}
393
394impl SocketOptions {
395    /// Create a default TCP  [`SocketOptions`].
396    #[inline]
397    #[must_use]
398    pub fn default_tcp() -> Self {
399        Default::default()
400    }
401
402    /// Create a default UDP [`SocketOptions`].
403    #[inline]
404    #[must_use]
405    pub fn default_udp() -> Self {
406        Self {
407            r#type: Type::Datagram,
408            protocol: Some(Protocol::UDP),
409            ..Default::default()
410        }
411    }
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize, Default)]
415pub struct SocketOptions {
416    pub r#type: Type,
417    pub protocol: Option<Protocol>,
418
419    /// Bind the [`Socket`] to the specified address.
420    pub address: Option<SocketAddress>,
421
422    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
423    #[cfg_attr(
424        docsrs,
425        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
426    )]
427    /// Bind the [`Socket`] to the specified device.
428    ///
429    /// Sets the value for the `SO_BINDTODEVICE` option on this [`Socket`]].
430    ///
431    /// If a socket is bound to an interface, only packets received from
432    /// that particular interface are processed by the socket.
433    /// Note that this only works for some socket types, particularly [`Domain::IPv4`] [`Socket`]s.
434    pub device: Option<super::DeviceName>,
435
436    /// Set the value of the `SO_BROADCAST` option for this [`Socket`].
437    ///
438    /// When enabled, this [`Socket`] is allowed to send packets to a broadcast address.
439    pub broadcast: Option<bool>,
440
441    /// Set value for the `SO_KEEPALIVE` option on this [`Socket`].
442    ///
443    /// Enable sending of keep-alive messages on connection-oriented [`Socket`]s.
444    pub keep_alive: Option<bool>,
445
446    /// Set value for the `SO_LINGER` option on this socket.
447    ///
448    /// If linger is not None, a close(2) or shutdown(2)
449    /// will not return until all queued messages for the socket have
450    /// been successfully sent or the linger timeout has been reached.
451    /// Otherwise, the call returns immediately and the closing is done in
452    /// the background. When the socket is closed as part of exit(2),
453    /// it always lingers in the background.
454    ///
455    /// ## Notes
456    ///
457    /// On most OSs the duration only has a precision of seconds and will be silently truncated.
458    ///
459    /// On Apple platforms (e.g. macOS, iOS, etc) this uses `SO_LINGER_SEC`.
460    pub linger: Option<Duration>,
461
462    #[cfg(not(target_os = "redox"))]
463    #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
464    /// Set value for the SO_OOBINLINE option on this [`Socket`].
465    ///
466    /// If this option is enabled,
467    /// out-of-band data is directly placed into the receive data stream.
468    /// Otherwise, out-of-band data is passed only when the `MSG_OOB` flag
469    /// is set during receiving. As per [RFC6093], TCP [`Socket`]s using the Urgent
470    /// mechanism are encouraged to set this flag.
471    ///
472    /// [RFC6093]: https://datatracker.ietf.org/doc/html/rfc6093
473    pub out_of_band_inline: Option<bool>,
474
475    #[cfg(all(target_family = "unix", target_os = "linux"))]
476    #[cfg_attr(docsrs, doc(cfg(all(target_family = "unix", target_os = "linux"))))]
477    /// Set value for the `SO_PASSCRED` option on this [`Socket`].
478    ///
479    /// If this option is enabled, enables the receiving of the `SCM_CREDENTIALS` control messages.
480    pub passcred: Option<bool>,
481
482    /// Set value for the `SO_RCVBUF` option on this [`Socket`].
483    ///
484    /// Changes the size of the operating system’s receive buffer associated with the [`Socket`].
485    pub recv_buffer_size: Option<usize>,
486
487    /// Set value for the `SO_RCVTIMEO` option on this [`Socket`].
488    ///
489    /// If timeout is None, then read and recv calls will block indefinitely.
490    pub read_timeout: Option<Duration>,
491
492    /// Set value for the `SO_REUSEADDR` option on this [`Socket`].
493    ///
494    /// This indicates that further calls to bind may allow reuse of local addresses.
495    /// For IPv4 [`Socket`]s this means that a [`Socket`] may bind even when there’s a [`Socket`] already
496    /// listening on this port.
497    pub reuse_address: Option<bool>,
498
499    /// Set value for the `SO_SNDBUF` option on this [`Socket`].
500    ///
501    /// Changes the size of the operating system’s send buffer
502    /// associated with the [`Socket`].
503    pub send_buffer_size: Option<usize>,
504
505    /// Set value for the SO_SNDTIMEO option on this [`Socket`].
506    ///
507    /// If timeout is None, then write and send calls will block indefinitely.
508    pub write_timeout: Option<Duration>,
509
510    #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
511    #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "espidf")))))]
512    /// Set the value of the `IP_HDRINCL` option on this [`Socket`].
513    ///
514    /// If enabled, the user supplies an IP header in front of the user data.
515    /// Valid only for [`Type::Raw`] [`Socket`]s; see [raw(7)] for more information.
516    ///
517    /// When this flag is enabled, the values set by
518    /// `IP_OPTIONS`, [`IP_TTL`], and [`IP_TOS`] are ignored.
519    ///
520    /// [raw(7)]: https://man7.org/linux/man-pages/man7/raw.7.html
521    /// [`IP_TTL`]: SocketOptions::ttl
522    /// [`IP_TOS`]: SocketOptions::tos
523    pub header_included: Option<bool>,
524
525    #[cfg(not(any(
526        target_os = "redox",
527        target_os = "espidf",
528        target_os = "openbsd",
529        target_os = "freebsd",
530        target_os = "dragonfly",
531        target_os = "netbsd"
532    )))]
533    #[cfg_attr(
534        docsrs,
535        doc(cfg(not(any(
536            target_os = "redox",
537            target_os = "espidf",
538            target_os = "openbsd",
539            target_os = "freebsd",
540            target_os = "dragonfly",
541            target_os = "netbsd"
542        ))))
543    )]
544    /// Set the value of the `IP_HDRINCL` option on this [`Socket`].
545    ///
546    /// If enabled, the user supplies an IP header in front of the user data.
547    /// Valid only for [`Type::Raw`] [`Socket`]s; see [raw(7)] for more information.
548    ///
549    /// When this flag is enabled, the values set by `IP_OPTIONS` are ignored.
550    ///
551    /// [raw(7)]: https://man7.org/linux/man-pages/man7/raw.7.html
552    pub header_included_v6: Option<bool>,
553
554    #[cfg(target_os = "linux")]
555    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
556    /// Set the value of the `IP_TRANSPARENT` option on this [`Socket`].
557    ///
558    /// Setting this boolean option enables transparent proxying on this [`Socket`].
559    ///
560    /// This [`Socket`] option allows the calling application to bind to a
561    /// nonlocal IP address and operate both as a client and a server with
562    /// the foreign address as the local endpoint.
563    ///
564    /// ## NOTE
565    ///
566    /// This requires that routing be set up in a way that packets
567    /// going to the foreign address are routed through the TProxy box
568    /// (i.e., the system hosting the application that employs the `IP_TRANSPARENT` socket option).
569    /// Enabling this [`Socket`] option requires superuser privileges (the `CAP_NET_ADMIN` capability).
570    ///
571    /// TProxy redirection with the iptables `TPROXY` target also requires
572    /// that this option be set on the redirected socket.
573    pub ip_transparent: Option<bool>,
574
575    #[cfg(target_os = "linux")]
576    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
577    /// Set the value of the `IPV6_TRANSPARENT` option on this [`Socket`].
578    ///
579    /// This is the IPv6 counterpart of [`ip_transparent`] (which sets
580    /// `IP_TRANSPARENT`, governing IPv4 sockets only). A dual-stack TProxy
581    /// listener must set both: `IP_TRANSPARENT` for the IPv4 path and
582    /// `IPV6_TRANSPARENT` for the IPv6 path. See [`ip_transparent`] for the
583    /// routing and privilege (`CAP_NET_ADMIN`) requirements, which are
584    /// identical.
585    ///
586    /// [`ip_transparent`]: SocketOptions::ip_transparent
587    pub ip_transparent_v6: Option<bool>,
588
589    /// Set the value of the `IP_TTL` option for this [`Socket`].
590    ///
591    /// This value sets the time-to-live field that
592    /// is used in every packet sent from this [`Socket`].
593    pub ttl: Option<u32>,
594
595    #[cfg(not(any(
596        target_os = "fuchsia",
597        target_os = "redox",
598        target_os = "solaris",
599        target_os = "illumos",
600        target_os = "haiku",
601    )))]
602    #[cfg_attr(
603        docsrs,
604        doc(cfg(not(any(
605            target_os = "fuchsia",
606            target_os = "redox",
607            target_os = "solaris",
608            target_os = "illumos",
609            target_os = "haiku",
610        ))))
611    )]
612    /// Set the value of the `IP_TOS` option for this [`Socket`].
613    ///
614    /// This value sets the type-of-service field that is used in every packet sent from this [`Socket`].
615    ///
616    /// ## NOTE
617    ///
618    /// <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options>
619    /// documents that not all versions of windows support `IP_TOS`.
620    pub tos: Option<u32>,
621
622    #[cfg(not(any(
623        target_os = "aix",
624        target_os = "dragonfly",
625        target_os = "fuchsia",
626        target_os = "hurd",
627        target_os = "illumos",
628        target_os = "netbsd",
629        target_os = "openbsd",
630        target_os = "redox",
631        target_os = "solaris",
632        target_os = "haiku",
633        target_os = "nto",
634        target_os = "espidf",
635        target_os = "vita",
636    )))]
637    #[cfg_attr(
638        docsrs,
639        doc(cfg(not(any(
640            target_os = "aix",
641            target_os = "dragonfly",
642            target_os = "fuchsia",
643            target_os = "hurd",
644            target_os = "illumos",
645            target_os = "netbsd",
646            target_os = "openbsd",
647            target_os = "redox",
648            target_os = "solaris",
649            target_os = "haiku",
650            target_os = "nto",
651            target_os = "espidf",
652            target_os = "vita",
653        ))))
654    )]
655    /// Set the value of the `IP_RECVTOS` option for this [`Socket`].
656    ///
657    /// If enabled, the `IP_TOS` ancillary message is passed with incoming packets.
658    /// It contains a byte which specifies the Type of Service/Precedence field of the packet header.
659    pub recv_tos: Option<bool>,
660
661    /// Set the value of the `IPV6_MULTICAST_HOPS` option for this [`Socket`].
662    ///
663    /// Indicates the number of "routers" multicast packets will transit for this [`Socket`].
664    /// The default value is 1 which means that multicast packets don’t leave the local network unless
665    /// explicitly requested.
666    pub multicast_hops_v6: Option<u32>,
667
668    #[cfg(target_os = "linux")]
669    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
670    /// Set the value of the `IP_MULTICAST_ALL` option for this [`Socket`].
671    ///
672    /// This option can be used to modify the delivery policy of
673    /// multicast messages. The argument is a boolean (defaults to true).
674    /// If set to true, the socket will receive messages from all the groups
675    /// that have been joined globally on the whole system.
676    /// Otherwise, it will deliver messages only from the groups
677    /// that have been explicitly joined
678    /// (for example via the `IP_ADD_MEMBERSHIP` option)
679    /// on this particular socket.
680    pub multicast_all_v4: Option<bool>,
681
682    #[cfg(target_os = "linux")]
683    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
684    /// Set the value of the `IPV6_MULTICAST_ALL` option for this [`Socket`].
685    ///
686    /// This option can be used to modify the delivery policy of multicast messages.
687    /// The argument is a boolean (defaults to true). If set to true,
688    /// the socket will receive messages from all the groups that have been
689    /// joined globally on the whole system. Otherwise, it will deliver messages
690    /// only from the groups that have been explicitly joined (for example via the
691    /// `IPV6_ADD_MEMBERSHIP` option) on this particular socket.
692    pub multicast_all_v6: Option<bool>,
693
694    /// Set the value of the `IP_MULTICAST_IF` option for this [`Socket`].
695    ///
696    /// If enabled, multicast packets will be looped back to the local socket.
697    /// Note that this may not have any affect on IPv6 sockets.
698    pub multicast_interface_v4: Option<Ipv4Addr>,
699
700    /// Set the value of the `IPV6_MULTICAST_IF` option for this [`Socket`].
701    ///
702    /// Specifies the interface to use for routing multicast packets.
703    /// Unlike ipv4, this is generally required in ipv6 contexts where
704    /// network routing prefixes may overlap.
705    pub multicast_interface_v6: Option<u32>,
706
707    /// Set the value of the `IP_MULTICAST_LOOP` option for this [`Socket`].
708    ///
709    /// If enabled, multicast packets will be looped back to the local [`Socket`].
710    /// Note that this may not have any affect on IPv6 [`Socket`]s.
711    pub multicast_loop_v4: Option<bool>,
712
713    /// Set the value of the `IPV6_MULTICAST_LOOP` option for this [`Socket`].
714    ///
715    /// Controls whether this [`Socket`] sees the multicast packets
716    /// it sends itself. Note that this may not have any affect on IPv4 [`Socket`]s.
717    pub multicast_loop_v6: Option<bool>,
718
719    /// Set the value of the `IP_MULTICAST_TTL` option for this [`Socket`].
720    ///
721    /// Indicates the time-to-live value of outgoing multicast packets
722    /// for this [`Socket`]. The default value is 1 which means that multicast
723    /// packets don’t leave the local network unless explicitly requested.
724    ///
725    /// Note that this may not have any affect on IPv6 [`Socket`]s.
726    pub multicast_ttl_v4: Option<u32>,
727
728    /// Set the value for the `IPV6_UNICAST_HOPS` option on this [`Socket`].
729    ///
730    /// Specifies the hop limit for ipv6 unicast packets
731    pub unicast_hops_v6: Option<u32>,
732
733    /// Set the value for the IPV6_V6ONLY option on this [`Socket`].
734    ///
735    /// If this is set to true then the socket is restricted to
736    /// sending and receiving IPv6 packets only.
737    /// In this case two IPv4 and IPv6 applications can bind the same port at the same time.
738    ///
739    /// If this is set to false then the socket can be used to send
740    /// and receive packets from an IPv4-mapped IPv6 address.
741    pub only_v6: Option<bool>,
742
743    #[cfg(not(any(
744        target_os = "dragonfly",
745        target_os = "fuchsia",
746        target_os = "illumos",
747        target_os = "netbsd",
748        target_os = "openbsd",
749        target_os = "redox",
750        target_os = "solaris",
751        target_os = "haiku",
752        target_os = "hurd",
753        target_os = "espidf",
754        target_os = "vita",
755    )))]
756    #[cfg_attr(
757        docsrs,
758        doc(cfg(not(any(
759            target_os = "dragonfly",
760            target_os = "fuchsia",
761            target_os = "illumos",
762            target_os = "netbsd",
763            target_os = "openbsd",
764            target_os = "redox",
765            target_os = "solaris",
766            target_os = "haiku",
767            target_os = "hurd",
768            target_os = "espidf",
769            target_os = "vita",
770        ))))
771    )]
772    /// Set the value of the `IPV6_RECVTCLASS` option for this [`Socket`].
773    ///
774    /// If enabled, the `IPV6_TCLASS` ancillary message is passed
775    /// with incoming packets. It contains a byte which specifies
776    /// the traffic class field of the packet header.
777    pub recv_tclass_v6: Option<bool>,
778
779    #[cfg(any(
780        target_os = "android",
781        target_os = "dragonfly",
782        target_os = "freebsd",
783        target_os = "fuchsia",
784        target_os = "linux",
785        target_os = "macos",
786        target_os = "netbsd",
787        target_os = "openbsd"
788    ))]
789    #[cfg_attr(
790        docsrs,
791        doc(cfg(any(
792            target_os = "android",
793            target_os = "dragonfly",
794            target_os = "freebsd",
795            target_os = "fuchsia",
796            target_os = "linux",
797            target_os = "macos",
798            target_os = "netbsd",
799            target_os = "openbsd"
800        )))
801    )]
802    /// Set the value of the `IPV6_TCLASS` option for this [`Socket`].
803    ///
804    /// Specifies the traffic class field that is used in every packets
805    /// sent from this [`Socket`].
806    pub tclass_v6: Option<u32>,
807
808    #[cfg(not(any(
809        target_os = "windows",
810        target_os = "dragonfly",
811        target_os = "fuchsia",
812        target_os = "illumos",
813        target_os = "netbsd",
814        target_os = "openbsd",
815        target_os = "redox",
816        target_os = "solaris",
817        target_os = "haiku",
818        target_os = "hurd",
819        target_os = "espidf",
820        target_os = "vita",
821    )))]
822    #[cfg_attr(
823        docsrs,
824        doc(cfg(not(any(
825            target_os = "windows",
826            target_os = "dragonfly",
827            target_os = "fuchsia",
828            target_os = "illumos",
829            target_os = "netbsd",
830            target_os = "openbsd",
831            target_os = "redox",
832            target_os = "solaris",
833            target_os = "haiku",
834            target_os = "hurd",
835            target_os = "espidf",
836            target_os = "vita",
837        ))))
838    )]
839    /// Set the value of the `IPV6_RECVHOPLIMIT` option for this [`Socket`].
840    ///
841    /// The received hop limit is returned as ancillary data by `recvmsg()`
842    /// only if the application has enabled the `IPV6_RECVHOPLIMIT` [`Socket`] option.
843    pub recv_hoplimit_v6: Option<bool>,
844
845    /// Set parameters configuring TCP keepalive probes for this [`Socket`].
846    ///
847    /// The supported parameters depend on the operating system, and are
848    /// configured using the [`TcpKeepAlive`] struct. At a minimum, all systems
849    /// support configuring the [keepalive time]: the time after which the OS
850    /// will start sending keepalive messages on an idle connection.
851    ///
852    /// [keepalive time]: TcpKeepAlive::time
853    ///
854    /// # Notes
855    ///
856    /// * This will enable `SO_KEEPALIVE` on this [`Socket`], if it is not already
857    ///   enabled.
858    /// * On some platforms, such as Windows, any keepalive parameters *not*
859    ///   configured by the `TcpKeepalive` struct passed to this function may be
860    ///   overwritten with their default values. Therefore, this function should
861    ///   either only be called once per [`Socket`], or the same parameters should
862    ///   be passed every time it is called.
863    pub tcp_keep_alive: Option<TcpKeepAlive>,
864
865    /// Set the value of the `TCP_NODELAY` option on this [`Socket`].
866    ///
867    /// If set, this option disables the Nagle algorithm.
868    /// This means that segments are always sent as soon as possible,
869    /// even if there is only a small amount of data. When not set,
870    /// data is buffered until there is a sufficient amount to send out,
871    /// thereby avoiding the frequent sending of small packets.
872    pub tcp_no_delay: Option<bool>,
873
874    #[cfg(all(target_family = "unix", not(target_os = "redox")))]
875    #[cfg_attr(
876        docsrs,
877        doc(cfg(all(target_family = "unix", not(target_os = "redox"))))
878    )]
879    /// Sets the value of the `TCP_MAXSEG` option on this [`Socket`].
880    ///
881    /// The `TCP_MAXSEG` option denotes the TCP Maximum Segment Size
882    /// and is only available on TCP [`Socket`]s.
883    pub tcp_max_segments: Option<u32>,
884
885    #[cfg(any(target_os = "freebsd", target_os = "linux"))]
886    #[cfg_attr(docsrs, doc(cfg(any(target_os = "freebsd", target_os = "linux"))))]
887    /// Set the value of the `TCP_CONGESTION` option for this [`Socket`].
888    ///
889    /// Specifies the TCP congestion control algorithm to use for this socket.
890    ///
891    /// The value must be a valid TCP congestion control algorithm name of the
892    /// platform. For example, Linux may supports "reno", "cubic".
893    pub tcp_congestion: Option<String>,
894
895    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
896    #[cfg_attr(
897        docsrs,
898        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
899    )]
900    /// Sets the value for the `SO_MARK` option on this [`Socket`].
901    ///
902    /// This value sets the socket mark field for each packet sent through this [`Socket`].
903    /// Changing the mark can be used for mark-based routing without netfilter or for packet filtering.
904    ///
905    /// On Linux this function requires the CAP_NET_ADMIN capability.
906    pub mark: Option<u32>,
907
908    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
909    #[cfg_attr(
910        docsrs,
911        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
912    )]
913    /// Set the value of the `TCP_CORK` option on this [`Socket`].
914    ///
915    /// If set, don't send out partial frames. All queued partial frames are
916    /// sent when the option is cleared again. There is a 200 millisecond ceiling on
917    /// the time for which output is corked by `TCP_CORK`. If this ceiling is reached,
918    /// then queued data is automatically transmitted.
919    pub tcp_cork: Option<bool>,
920
921    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
922    #[cfg_attr(
923        docsrs,
924        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
925    )]
926    /// Set the value of the `TCP_QUICKACK` option on this [`Socket`].
927    ///
928    /// If set, acks are sent immediately, rather than delayed if needed in accordance to normal
929    /// TCP operation. This flag is not permanent, it only enables a switch to or from quickack mode.
930    /// Subsequent operation of the TCP protocol will once again enter/leave quickack mode depending on
931    /// internal protocol processing and factors such as delayed ack timeouts occurring and data transfer.
932    pub tcp_quick_ack: Option<bool>,
933
934    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
935    #[cfg_attr(
936        docsrs,
937        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
938    )]
939    /// Set the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this [`Socket`].
940    ///
941    /// If set, the kernel will dynamically detect a thin-stream connection
942    /// if there are less than four packets in flight.
943    /// With less than four packets in flight the normal TCP fast retransmission will not be effective.
944    /// The kernel will modify the retransmission to avoid the very high latencies that thin stream
945    /// suffer because of exponential backoff.
946    pub tcp_thin_linear_timeouts: Option<bool>,
947
948    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
949    #[cfg_attr(
950        docsrs,
951        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
952    )]
953    /// Set the value of the `TCP_USER_TIMEOUT` option on this [`Socket`].
954    ///
955    /// If set, this specifies the maximum amount of time that transmitted data may remain
956    /// unacknowledged or buffered data may remain untransmitted before TCP will forcibly close the
957    /// corresponding connection.
958    ///
959    /// Setting `timeout` to `None` or a zero duration causes the system default timeouts to
960    /// be used. If `timeout` in milliseconds is larger than `c_uint::MAX`, the timeout is clamped
961    /// to `c_uint::MAX`. For example, when `c_uint` is a 32-bit value, this limits the timeout to
962    /// approximately 49.71 days.
963    pub tcp_user_timeout: Option<Duration>,
964
965    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
966    #[cfg_attr(
967        docsrs,
968        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
969    )]
970    /// Set value for the `IP_FREEBIND` option on this [`Socket`].
971    ///
972    /// If enabled, this boolean option allows binding to an IP address that is
973    /// nonlocal or does not (yet) exist.  This permits listening on a [`Socket`],
974    /// without requiring the underlying network interface or the specified
975    /// dynamic IP address to be up at the time that the application is trying
976    /// to bind to it.
977    pub freebind: Option<bool>,
978
979    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
980    #[cfg_attr(
981        docsrs,
982        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
983    )]
984    /// Set value for the `IPV6_FREEBIND` option on this [`Socket`].
985    ///
986    /// This is an IPv6 counterpart of `IP_FREEBIND` [`Socket`] option on
987    /// Android/Linux. For more information about this option, see
988    /// [`set_freebind`].
989    ///
990    /// [`set_freebind`]: SocketOptions::freebind
991    pub freebind_ipv6: Option<bool>,
992
993    #[cfg(target_os = "linux")]
994    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
995    /// Set value for the `SO_INCOMING_CPU` option on this [`Socket`].
996    ///
997    /// Sets the CPU affinity of the [`Socket`].
998    pub cpu_affinity: Option<usize>,
999
1000    #[cfg(all(
1001        target_family = "unix",
1002        not(any(target_os = "solaris", target_os = "illumos"))
1003    ))]
1004    #[cfg_attr(
1005        docsrs,
1006        doc(cfg(all(
1007            target_family = "unix",
1008            not(any(target_os = "solaris", target_os = "illumos"))
1009        )))
1010    )]
1011    /// Set value for the `SO_REUSEPORT` option on this [`Socket`].
1012    ///
1013    /// This indicates that further calls to `bind` may allow reuse of local
1014    /// addresses. For IPv4 [`Socket`]s this means that a [`Socket`] may bind even when
1015    /// there's a [`Socket`] already listening on this port.
1016    pub reuse_port: Option<bool>,
1017
1018    #[cfg(target_os = "linux")]
1019    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
1020    /// Set value for the `DCCP_SOCKOPT_SERVICE` option on this [`Socket`].
1021    ///
1022    /// Sets the DCCP service. The specification mandates use of service codes.
1023    /// If this [`Socket`] option is not set, the [`Socket`] will fall back to 0 (which
1024    /// means that no meaningful service code is present). On active [`Socket`]s
1025    /// this is set before [`connect`]. On passive [`Socket`]s up to 32 service
1026    /// codes can be set before calling [`bind`]
1027    ///
1028    /// [`connect`]: Socket::connect
1029    /// [`bind`]: Socket::bind
1030    pub dccp_service: Option<u32>,
1031
1032    #[cfg(target_os = "linux")]
1033    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
1034    /// Set value for the `DCCP_SOCKOPT_CCID` option on this [`Socket`].
1035    ///
1036    /// This option sets both the TX and RX CCIDs at the same time.
1037    pub dccp_ccid: Option<u8>,
1038
1039    #[cfg(target_os = "linux")]
1040    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
1041    /// Set value for the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this [`Socket`].
1042    ///
1043    /// Enables a listening [`Socket`] to hold timewait state when closing the
1044    /// connection. This option must be set after `accept` returns.
1045    pub dccp_server_timewait: Option<bool>,
1046
1047    #[cfg(target_os = "linux")]
1048    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
1049    /// Set value for the `DCCP_SOCKOPT_SEND_CSCOV` option on this [`Socket`].
1050    ///
1051    /// Both this option and `DCCP_SOCKOPT_RECV_CSCOV` are used for setting the
1052    /// partial checksum coverage. The default is that checksums always cover
1053    /// the entire packet and that only fully covered application data is
1054    /// accepted by the receiver. Hence, when using this feature on the sender,
1055    /// it must be enabled at the receiver too, with suitable choice of CsCov.
1056    pub dccp_send_cscov: Option<u32>,
1057
1058    #[cfg(target_os = "linux")]
1059    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
1060    /// Set the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this [`Socket`].
1061    ///
1062    /// This option is only useful when combined with [`dccp_send_cscov`].
1063    ///
1064    /// [`dccp_send_cscov`]: Socket::dccp_send_cscov
1065    pub dccp_recv_cscov: Option<u32>,
1066
1067    #[cfg(target_os = "linux")]
1068    #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
1069    /// Set value for the `DCCP_SOCKOPT_QPOLICY_TXQLEN` option on this [`Socket`].
1070    ///
1071    /// This option sets the maximum length of the output queue. A zero value is
1072    /// interpreted as unbounded queue length.
1073    pub dccp_qpolicy_txqlen: Option<u32>,
1074}
1075
1076impl SocketOptions {
1077    pub fn try_build_socket(&self, domain: Domain) -> io::Result<Socket> {
1078        let socket = Socket::new(
1079            domain.into(),
1080            self.r#type.into(),
1081            self.protocol.map(Into::into),
1082        )?;
1083
1084        if let Some(broadcast) = self.broadcast {
1085            socket.set_broadcast(broadcast)?;
1086        }
1087        if let Some(keep_alive) = self.keep_alive {
1088            socket.set_keepalive(keep_alive)?;
1089        }
1090        if let Some(linger) = self.linger {
1091            socket.set_linger(Some(linger))?;
1092        }
1093        #[cfg(not(target_os = "redox"))]
1094        if let Some(oob) = self.out_of_band_inline {
1095            socket.set_out_of_band_inline(oob)?;
1096        }
1097        #[cfg(all(target_family = "unix", target_os = "linux"))]
1098        if let Some(passcred) = self.passcred {
1099            socket.set_passcred(passcred)?;
1100        }
1101        if let Some(n) = self.recv_buffer_size {
1102            socket.set_recv_buffer_size(n)?;
1103        }
1104        if let Some(duration) = self.read_timeout {
1105            socket.set_read_timeout(Some(duration))?;
1106        }
1107        if let Some(reuse) = self.reuse_address {
1108            socket.set_reuse_address(reuse)?;
1109        }
1110        if let Some(n) = self.send_buffer_size {
1111            socket.set_send_buffer_size(n)?;
1112        }
1113        if let Some(duration) = self.write_timeout {
1114            socket.set_write_timeout(Some(duration))?;
1115        }
1116        #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
1117        if let Some(header_included) = self.header_included {
1118            socket.set_header_included_v4(header_included)?;
1119        }
1120        #[cfg(not(any(
1121            target_os = "redox",
1122            target_os = "espidf",
1123            target_os = "openbsd",
1124            target_os = "freebsd",
1125            target_os = "dragonfly",
1126            target_os = "netbsd"
1127        )))]
1128        if let Some(header_included) = self.header_included {
1129            socket.set_header_included_v6(header_included)?;
1130        }
1131        #[cfg(target_os = "linux")]
1132        if let Some(transparent) = self.ip_transparent {
1133            socket.set_ip_transparent_v4(transparent)?;
1134        }
1135        #[cfg(target_os = "linux")]
1136        if let Some(transparent) = self.ip_transparent_v6 {
1137            socket.set_ip_transparent_v6(transparent)?;
1138        }
1139        if let Some(ttl) = self.ttl {
1140            socket.set_ttl_v4(ttl)?;
1141        }
1142        #[cfg(not(any(
1143            target_os = "fuchsia",
1144            target_os = "redox",
1145            target_os = "solaris",
1146            target_os = "illumos",
1147            target_os = "haiku",
1148        )))]
1149        if let Some(tos) = self.tos {
1150            socket.set_tos_v4(tos)?;
1151        }
1152        #[cfg(not(any(
1153            target_os = "aix",
1154            target_os = "dragonfly",
1155            target_os = "fuchsia",
1156            target_os = "hurd",
1157            target_os = "illumos",
1158            target_os = "netbsd",
1159            target_os = "openbsd",
1160            target_os = "redox",
1161            target_os = "solaris",
1162            target_os = "haiku",
1163            target_os = "nto",
1164            target_os = "espidf",
1165            target_os = "vita",
1166        )))]
1167        if let Some(recv_tos) = self.recv_tos {
1168            socket.set_recv_tos_v4(recv_tos)?;
1169        }
1170        if let Some(loop_v4) = self.multicast_loop_v4 {
1171            socket.set_multicast_loop_v4(loop_v4)?;
1172        }
1173        if let Some(loop_v6) = self.multicast_loop_v6 {
1174            socket.set_multicast_loop_v6(loop_v6)?;
1175        }
1176        if let Some(ttl) = self.multicast_ttl_v4 {
1177            socket.set_multicast_ttl_v4(ttl)?;
1178        }
1179        if let Some(hops) = self.multicast_hops_v6 {
1180            socket.set_multicast_hops_v6(hops)?;
1181        }
1182        #[cfg(target_os = "linux")]
1183        if let Some(all) = self.multicast_all_v4 {
1184            socket.set_multicast_all_v4(all)?;
1185        }
1186        #[cfg(target_os = "linux")]
1187        if let Some(all) = self.multicast_all_v6 {
1188            socket.set_multicast_all_v6(all)?;
1189        }
1190        if let Some(interface) = self.multicast_interface_v4.as_ref() {
1191            socket.set_multicast_if_v4(interface)?;
1192        }
1193        if let Some(interface) = self.multicast_interface_v6 {
1194            socket.set_multicast_if_v6(interface)?;
1195        }
1196        if let Some(hops) = self.unicast_hops_v6 {
1197            socket.set_unicast_hops_v6(hops)?;
1198        }
1199        if let Some(only_v6) = self.only_v6 {
1200            socket.set_only_v6(only_v6)?;
1201        }
1202
1203        #[cfg(not(any(
1204            target_os = "windows",
1205            target_os = "dragonfly",
1206            target_os = "fuchsia",
1207            target_os = "illumos",
1208            target_os = "netbsd",
1209            target_os = "openbsd",
1210            target_os = "redox",
1211            target_os = "solaris",
1212            target_os = "haiku",
1213            target_os = "hurd",
1214            target_os = "espidf",
1215            target_os = "vita",
1216        )))]
1217        if let Some(recv) = self.recv_hoplimit_v6 {
1218            socket.set_recv_hoplimit_v6(recv)?;
1219        }
1220
1221        #[cfg(not(any(
1222            target_os = "windows",
1223            target_os = "dragonfly",
1224            target_os = "fuchsia",
1225            target_os = "illumos",
1226            target_os = "netbsd",
1227            target_os = "openbsd",
1228            target_os = "redox",
1229            target_os = "solaris",
1230            target_os = "haiku",
1231            target_os = "hurd",
1232            target_os = "espidf",
1233            target_os = "vita",
1234        )))]
1235        if let Some(recv) = self.recv_tclass_v6 {
1236            socket.set_recv_tclass_v6(recv)?;
1237        }
1238
1239        #[cfg(any(
1240            target_os = "android",
1241            target_os = "dragonfly",
1242            target_os = "freebsd",
1243            target_os = "fuchsia",
1244            target_os = "linux",
1245            target_os = "macos",
1246            target_os = "netbsd",
1247            target_os = "openbsd"
1248        ))]
1249        if let Some(tclass_v6) = self.tclass_v6 {
1250            socket.set_tclass_v6(tclass_v6)?;
1251        }
1252
1253        if let Some(keep_alive) = self.tcp_keep_alive.clone() {
1254            socket.set_tcp_keepalive(&keep_alive.into_socket_keep_alive())?;
1255        }
1256        if let Some(no_delay) = self.tcp_no_delay {
1257            socket.set_tcp_nodelay(no_delay)?;
1258        }
1259
1260        #[cfg(all(target_family = "unix", not(target_os = "redox")))]
1261        if let Some(mss) = self.tcp_max_segments {
1262            socket.set_tcp_mss(mss)?;
1263        }
1264
1265        #[cfg(any(target_os = "freebsd", target_os = "linux"))]
1266        if let Some(algo) = self.tcp_congestion.as_ref() {
1267            socket.set_tcp_congestion(algo.as_bytes())?;
1268        }
1269
1270        #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1271        {
1272            if let Some(mark) = self.mark {
1273                socket.set_mark(mark)?;
1274            }
1275            if let Some(cork) = self.tcp_cork {
1276                socket.set_tcp_cork(cork)?;
1277            }
1278            if let Some(quickack) = self.tcp_quick_ack {
1279                socket.set_tcp_quickack(quickack)?;
1280            }
1281            if let Some(timeouts) = self.tcp_thin_linear_timeouts {
1282                socket.set_tcp_thin_linear_timeouts(timeouts)?;
1283            }
1284            if let Some(tcp_user_timeout) = self.tcp_user_timeout {
1285                socket.set_tcp_user_timeout(Some(tcp_user_timeout))?;
1286            }
1287            if let Some(freebind) = self.freebind {
1288                socket.set_freebind_v4(freebind)?;
1289            }
1290            if let Some(freebind_ipv6) = self.freebind_ipv6 {
1291                socket.set_freebind_v6(freebind_ipv6)?;
1292            }
1293        }
1294
1295        #[cfg(target_os = "linux")]
1296        if let Some(cpu) = self.cpu_affinity {
1297            socket.set_cpu_affinity(cpu)?;
1298        }
1299
1300        #[cfg(all(
1301            target_family = "unix",
1302            not(any(target_os = "solaris", target_os = "illumos"))
1303        ))]
1304        if let Some(reuse) = self.reuse_port {
1305            socket.set_reuse_port(reuse)?;
1306        }
1307
1308        #[cfg(target_os = "linux")]
1309        {
1310            if let Some(service) = self.dccp_service {
1311                socket.set_dccp_service(service)?;
1312            }
1313            if let Some(ccid) = self.dccp_ccid {
1314                socket.set_dccp_ccid(ccid)?;
1315            }
1316            if let Some(timewait) = self.dccp_server_timewait {
1317                socket.set_dccp_server_timewait(timewait)?;
1318            }
1319            if let Some(send_cscov) = self.dccp_send_cscov {
1320                socket.set_dccp_send_cscov(send_cscov)?;
1321            }
1322            if let Some(recv_cscov) = self.dccp_recv_cscov {
1323                socket.set_dccp_recv_cscov(recv_cscov)?;
1324            }
1325            if let Some(txqlen) = self.dccp_qpolicy_txqlen {
1326                socket.set_dccp_qpolicy_txqlen(txqlen)?;
1327            }
1328        }
1329
1330        if let Some(addr) = self.address {
1331            let std_addr: SocketAddr = addr.into();
1332            let socket_addr: SockAddr = std_addr.into();
1333            socket.bind(&socket_addr)?;
1334        }
1335
1336        #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1337        if let Some(ref device) = self.device {
1338            socket.bind_device(Some(device.as_bytes()))?;
1339        }
1340
1341        Ok(socket)
1342    }
1343}