socket2/sys/
unix.rs

1// Copyright 2015 The Rust Project Developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::cmp::min;
10use std::ffi::OsStr;
11#[cfg(not(target_os = "redox"))]
12use std::io::IoSlice;
13use std::marker::PhantomData;
14use std::mem::{self, size_of, MaybeUninit};
15use std::net::Shutdown;
16use std::net::{Ipv4Addr, Ipv6Addr};
17#[cfg(all(
18    feature = "all",
19    any(
20        target_os = "ios",
21        target_os = "visionos",
22        target_os = "macos",
23        target_os = "tvos",
24        target_os = "watchos",
25        target_os = "illumos",
26        target_os = "solaris",
27    )
28))]
29use std::num::NonZeroU32;
30#[cfg(all(
31    feature = "all",
32    any(
33        target_os = "aix",
34        target_os = "android",
35        target_os = "freebsd",
36        target_os = "ios",
37        target_os = "visionos",
38        target_os = "linux",
39        target_os = "macos",
40        target_os = "tvos",
41        target_os = "watchos",
42    )
43))]
44use std::num::NonZeroUsize;
45use std::os::unix::ffi::OsStrExt;
46#[cfg(all(
47    feature = "all",
48    any(
49        target_os = "aix",
50        target_os = "android",
51        target_os = "freebsd",
52        target_os = "ios",
53        target_os = "visionos",
54        target_os = "linux",
55        target_os = "macos",
56        target_os = "tvos",
57        target_os = "watchos",
58    )
59))]
60use std::os::unix::io::RawFd;
61use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd};
62#[cfg(feature = "all")]
63use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
64use std::path::Path;
65use std::ptr;
66use std::time::{Duration, Instant};
67use std::{io, slice};
68
69#[cfg(not(any(
70    target_os = "ios",
71    target_os = "visionos",
72    target_os = "macos",
73    target_os = "tvos",
74    target_os = "watchos",
75)))]
76use libc::ssize_t;
77use libc::{in6_addr, in_addr};
78
79use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
80#[cfg(not(target_os = "redox"))]
81use crate::{MsgHdr, MsgHdrMut, RecvFlags};
82
83pub(crate) use libc::c_int;
84
85// Used in `Domain`.
86pub(crate) use libc::{AF_INET, AF_INET6, AF_UNIX};
87// Used in `Type`.
88#[cfg(all(feature = "all", target_os = "linux"))]
89pub(crate) use libc::SOCK_DCCP;
90#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
91pub(crate) use libc::SOCK_RAW;
92#[cfg(all(feature = "all", not(target_os = "espidf")))]
93pub(crate) use libc::SOCK_SEQPACKET;
94pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
95// Used in `Protocol`.
96#[cfg(all(feature = "all", target_os = "linux"))]
97pub(crate) use libc::IPPROTO_DCCP;
98#[cfg(target_os = "linux")]
99pub(crate) use libc::IPPROTO_MPTCP;
100#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
101pub(crate) use libc::IPPROTO_SCTP;
102#[cfg(all(
103    feature = "all",
104    any(
105        target_os = "android",
106        target_os = "freebsd",
107        target_os = "fuchsia",
108        target_os = "linux",
109    )
110))]
111pub(crate) use libc::IPPROTO_UDPLITE;
112pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
113// Used in `SockAddr`.
114#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
115pub(crate) use libc::IPPROTO_DIVERT;
116pub(crate) use libc::{
117    sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t,
118};
119// Used in `RecvFlags`.
120#[cfg(not(any(target_os = "redox", target_os = "espidf")))]
121pub(crate) use libc::MSG_TRUNC;
122#[cfg(not(target_os = "redox"))]
123pub(crate) use libc::SO_OOBINLINE;
124// Used in `Socket`.
125#[cfg(not(target_os = "nto"))]
126pub(crate) use libc::ipv6_mreq as Ipv6Mreq;
127#[cfg(all(
128    feature = "all",
129    not(any(
130        target_os = "dragonfly",
131        target_os = "fuchsia",
132        target_os = "hurd",
133        target_os = "illumos",
134        target_os = "netbsd",
135        target_os = "openbsd",
136        target_os = "redox",
137        target_os = "solaris",
138        target_os = "haiku",
139        target_os = "espidf",
140        target_os = "vita",
141    ))
142))]
143pub(crate) use libc::IPV6_RECVHOPLIMIT;
144#[cfg(not(any(
145    target_os = "dragonfly",
146    target_os = "fuchsia",
147    target_os = "hurd",
148    target_os = "illumos",
149    target_os = "netbsd",
150    target_os = "openbsd",
151    target_os = "redox",
152    target_os = "solaris",
153    target_os = "haiku",
154    target_os = "espidf",
155    target_os = "vita",
156)))]
157pub(crate) use libc::IPV6_RECVTCLASS;
158#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
159pub(crate) use libc::IP_HDRINCL;
160#[cfg(not(any(
161    target_os = "aix",
162    target_os = "dragonfly",
163    target_os = "fuchsia",
164    target_os = "illumos",
165    target_os = "netbsd",
166    target_os = "openbsd",
167    target_os = "redox",
168    target_os = "solaris",
169    target_os = "haiku",
170    target_os = "hurd",
171    target_os = "nto",
172    target_os = "espidf",
173    target_os = "vita",
174)))]
175pub(crate) use libc::IP_RECVTOS;
176#[cfg(not(any(
177    target_os = "fuchsia",
178    target_os = "redox",
179    target_os = "solaris",
180    target_os = "haiku",
181    target_os = "illumos",
182)))]
183pub(crate) use libc::IP_TOS;
184#[cfg(not(any(
185    target_os = "ios",
186    target_os = "visionos",
187    target_os = "macos",
188    target_os = "tvos",
189    target_os = "watchos",
190)))]
191pub(crate) use libc::SO_LINGER;
192#[cfg(any(
193    target_os = "ios",
194    target_os = "visionos",
195    target_os = "macos",
196    target_os = "tvos",
197    target_os = "watchos",
198))]
199pub(crate) use libc::SO_LINGER_SEC as SO_LINGER;
200#[cfg(target_os = "linux")]
201pub(crate) use libc::SO_PASSCRED;
202pub(crate) use libc::{
203    ip_mreq as IpMreq, linger, IPPROTO_IP, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF,
204    IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP,
205    IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET,
206    SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF,
207    SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
208};
209#[cfg(not(any(
210    target_os = "dragonfly",
211    target_os = "haiku",
212    target_os = "hurd",
213    target_os = "netbsd",
214    target_os = "openbsd",
215    target_os = "redox",
216    target_os = "fuchsia",
217    target_os = "nto",
218    target_os = "espidf",
219    target_os = "vita",
220)))]
221pub(crate) use libc::{
222    ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
223};
224#[cfg(not(any(
225    target_os = "dragonfly",
226    target_os = "freebsd",
227    target_os = "haiku",
228    target_os = "illumos",
229    target_os = "ios",
230    target_os = "visionos",
231    target_os = "macos",
232    target_os = "netbsd",
233    target_os = "nto",
234    target_os = "openbsd",
235    target_os = "solaris",
236    target_os = "tvos",
237    target_os = "watchos",
238)))]
239pub(crate) use libc::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP};
240#[cfg(any(
241    target_os = "dragonfly",
242    target_os = "freebsd",
243    target_os = "haiku",
244    target_os = "illumos",
245    target_os = "ios",
246    target_os = "visionos",
247    target_os = "macos",
248    target_os = "netbsd",
249    target_os = "openbsd",
250    target_os = "solaris",
251    target_os = "tvos",
252    target_os = "watchos",
253))]
254pub(crate) use libc::{
255    IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP, IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP,
256};
257#[cfg(all(
258    feature = "all",
259    any(
260        target_os = "android",
261        target_os = "dragonfly",
262        target_os = "freebsd",
263        target_os = "fuchsia",
264        target_os = "illumos",
265        target_os = "ios",
266        target_os = "visionos",
267        target_os = "linux",
268        target_os = "macos",
269        target_os = "netbsd",
270        target_os = "tvos",
271        target_os = "watchos",
272    )
273))]
274pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL};
275
276// See this type in the Windows file.
277pub(crate) type Bool = c_int;
278
279#[cfg(any(
280    target_os = "ios",
281    target_os = "visionos",
282    target_os = "macos",
283    target_os = "nto",
284    target_os = "tvos",
285    target_os = "watchos",
286))]
287use libc::TCP_KEEPALIVE as KEEPALIVE_TIME;
288#[cfg(not(any(
289    target_os = "haiku",
290    target_os = "ios",
291    target_os = "visionos",
292    target_os = "macos",
293    target_os = "nto",
294    target_os = "openbsd",
295    target_os = "tvos",
296    target_os = "watchos",
297    target_os = "vita",
298)))]
299use libc::TCP_KEEPIDLE as KEEPALIVE_TIME;
300
301/// Helper macro to execute a system call that returns an `io::Result`.
302macro_rules! syscall {
303    ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
304        #[allow(unused_unsafe)]
305        let res = unsafe { libc::$fn($($arg, )*) };
306        if res == -1 {
307            Err(std::io::Error::last_os_error())
308        } else {
309            Ok(res)
310        }
311    }};
312}
313
314/// Maximum size of a buffer passed to system call like `recv` and `send`.
315#[cfg(not(any(
316    target_os = "ios",
317    target_os = "visionos",
318    target_os = "macos",
319    target_os = "tvos",
320    target_os = "watchos",
321)))]
322const MAX_BUF_LEN: usize = ssize_t::MAX as usize;
323
324// The maximum read limit on most posix-like systems is `SSIZE_MAX`, with the
325// man page quoting that if the count of bytes to read is greater than
326// `SSIZE_MAX` the result is "unspecified".
327//
328// On macOS, however, apparently the 64-bit libc is either buggy or
329// intentionally showing odd behavior by rejecting any read with a size larger
330// than or equal to INT_MAX. To handle both of these the read size is capped on
331// both platforms.
332#[cfg(any(
333    target_os = "ios",
334    target_os = "visionos",
335    target_os = "macos",
336    target_os = "tvos",
337    target_os = "watchos",
338))]
339const MAX_BUF_LEN: usize = c_int::MAX as usize - 1;
340
341// TCP_CA_NAME_MAX isn't defined in user space include files(not in libc)
342#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
343const TCP_CA_NAME_MAX: usize = 16;
344
345#[cfg(any(
346    all(
347        target_os = "linux",
348        any(
349            target_env = "gnu",
350            all(target_env = "uclibc", target_pointer_width = "64")
351        )
352    ),
353    target_os = "android",
354))]
355type IovLen = usize;
356
357#[cfg(any(
358    all(
359        target_os = "linux",
360        any(
361            target_env = "musl",
362            target_env = "ohos",
363            all(target_env = "uclibc", target_pointer_width = "32")
364        )
365    ),
366    target_os = "aix",
367    target_os = "dragonfly",
368    target_os = "freebsd",
369    target_os = "fuchsia",
370    target_os = "haiku",
371    target_os = "hurd",
372    target_os = "illumos",
373    target_os = "ios",
374    target_os = "visionos",
375    target_os = "macos",
376    target_os = "netbsd",
377    target_os = "nto",
378    target_os = "openbsd",
379    target_os = "solaris",
380    target_os = "tvos",
381    target_os = "watchos",
382    target_os = "espidf",
383    target_os = "vita",
384))]
385type IovLen = c_int;
386
387/// Unix only API.
388impl Domain {
389    /// Domain for low-level packet interface, corresponding to `AF_PACKET`.
390    #[cfg(all(
391        feature = "all",
392        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
393    ))]
394    #[cfg_attr(
395        docsrs,
396        doc(cfg(all(
397            feature = "all",
398            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
399        )))
400    )]
401    pub const PACKET: Domain = Domain(libc::AF_PACKET);
402
403    /// Domain for low-level VSOCK interface, corresponding to `AF_VSOCK`.
404    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
405    #[cfg_attr(
406        docsrs,
407        doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
408    )]
409    pub const VSOCK: Domain = Domain(libc::AF_VSOCK);
410}
411
412impl_debug!(
413    Domain,
414    libc::AF_INET,
415    libc::AF_INET6,
416    libc::AF_UNIX,
417    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
418    #[cfg_attr(
419        docsrs,
420        doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
421    )]
422    libc::AF_PACKET,
423    #[cfg(any(target_os = "android", target_os = "linux"))]
424    #[cfg_attr(docsrs, doc(cfg(any(target_os = "android", target_os = "linux"))))]
425    libc::AF_VSOCK,
426    libc::AF_UNSPEC, // = 0.
427);
428
429/// Unix only API.
430impl Type {
431    /// Set `SOCK_NONBLOCK` on the `Type`.
432    #[cfg(all(
433        feature = "all",
434        any(
435            target_os = "android",
436            target_os = "dragonfly",
437            target_os = "freebsd",
438            target_os = "fuchsia",
439            target_os = "illumos",
440            target_os = "linux",
441            target_os = "netbsd",
442            target_os = "openbsd"
443        )
444    ))]
445    #[cfg_attr(
446        docsrs,
447        doc(cfg(all(
448            feature = "all",
449            any(
450                target_os = "android",
451                target_os = "dragonfly",
452                target_os = "freebsd",
453                target_os = "fuchsia",
454                target_os = "illumos",
455                target_os = "linux",
456                target_os = "netbsd",
457                target_os = "openbsd"
458            )
459        )))
460    )]
461    pub const fn nonblocking(self) -> Type {
462        Type(self.0 | libc::SOCK_NONBLOCK)
463    }
464
465    /// Set `SOCK_CLOEXEC` on the `Type`.
466    #[cfg(all(
467        feature = "all",
468        any(
469            target_os = "android",
470            target_os = "dragonfly",
471            target_os = "freebsd",
472            target_os = "fuchsia",
473            target_os = "hurd",
474            target_os = "illumos",
475            target_os = "linux",
476            target_os = "netbsd",
477            target_os = "openbsd",
478            target_os = "redox",
479            target_os = "solaris",
480        )
481    ))]
482    #[cfg_attr(
483        docsrs,
484        doc(cfg(all(
485            feature = "all",
486            any(
487                target_os = "android",
488                target_os = "dragonfly",
489                target_os = "freebsd",
490                target_os = "fuchsia",
491                target_os = "hurd",
492                target_os = "illumos",
493                target_os = "linux",
494                target_os = "netbsd",
495                target_os = "openbsd",
496                target_os = "redox",
497                target_os = "solaris",
498            )
499        )))
500    )]
501    pub const fn cloexec(self) -> Type {
502        self._cloexec()
503    }
504
505    #[cfg(any(
506        target_os = "android",
507        target_os = "dragonfly",
508        target_os = "freebsd",
509        target_os = "fuchsia",
510        target_os = "hurd",
511        target_os = "illumos",
512        target_os = "linux",
513        target_os = "netbsd",
514        target_os = "openbsd",
515        target_os = "redox",
516        target_os = "solaris",
517    ))]
518    pub(crate) const fn _cloexec(self) -> Type {
519        Type(self.0 | libc::SOCK_CLOEXEC)
520    }
521}
522
523impl_debug!(
524    Type,
525    libc::SOCK_STREAM,
526    libc::SOCK_DGRAM,
527    #[cfg(all(feature = "all", target_os = "linux"))]
528    libc::SOCK_DCCP,
529    #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
530    libc::SOCK_RAW,
531    #[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "espidf")))]
532    libc::SOCK_RDM,
533    #[cfg(not(target_os = "espidf"))]
534    libc::SOCK_SEQPACKET,
535    /* TODO: add these optional bit OR-ed flags:
536    #[cfg(any(
537        target_os = "android",
538        target_os = "dragonfly",
539        target_os = "freebsd",
540        target_os = "fuchsia",
541        target_os = "linux",
542        target_os = "netbsd",
543        target_os = "openbsd"
544    ))]
545    libc::SOCK_NONBLOCK,
546    #[cfg(any(
547        target_os = "android",
548        target_os = "dragonfly",
549        target_os = "freebsd",
550        target_os = "fuchsia",
551        target_os = "linux",
552        target_os = "netbsd",
553        target_os = "openbsd"
554    ))]
555    libc::SOCK_CLOEXEC,
556    */
557);
558
559impl_debug!(
560    Protocol,
561    libc::IPPROTO_ICMP,
562    libc::IPPROTO_ICMPV6,
563    libc::IPPROTO_TCP,
564    libc::IPPROTO_UDP,
565    #[cfg(target_os = "linux")]
566    libc::IPPROTO_MPTCP,
567    #[cfg(all(feature = "all", target_os = "linux"))]
568    libc::IPPROTO_DCCP,
569    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
570    libc::IPPROTO_SCTP,
571    #[cfg(all(
572        feature = "all",
573        any(
574            target_os = "android",
575            target_os = "freebsd",
576            target_os = "fuchsia",
577            target_os = "linux",
578        )
579    ))]
580    libc::IPPROTO_UDPLITE,
581    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
582    libc::IPPROTO_DIVERT,
583);
584
585/// Unix-only API.
586#[cfg(not(target_os = "redox"))]
587impl RecvFlags {
588    /// Check if the message terminates a record.
589    ///
590    /// Not all socket types support the notion of records. For socket types
591    /// that do support it (such as [`SEQPACKET`]), a record is terminated by
592    /// sending a message with the end-of-record flag set.
593    ///
594    /// On Unix this corresponds to the `MSG_EOR` flag.
595    ///
596    /// [`SEQPACKET`]: Type::SEQPACKET
597    #[cfg(not(target_os = "espidf"))]
598    pub const fn is_end_of_record(self) -> bool {
599        self.0 & libc::MSG_EOR != 0
600    }
601
602    /// Check if the message contains out-of-band data.
603    ///
604    /// This is useful for protocols where you receive out-of-band data
605    /// mixed in with the normal data stream.
606    ///
607    /// On Unix this corresponds to the `MSG_OOB` flag.
608    pub const fn is_out_of_band(self) -> bool {
609        self.0 & libc::MSG_OOB != 0
610    }
611
612    /// Check if the confirm flag is set.
613    ///
614    /// This is used by SocketCAN to indicate a frame was sent via the
615    /// socket it is received on. This flag can be interpreted as a
616    /// 'transmission confirmation'.
617    ///
618    /// On Unix this corresponds to the `MSG_CONFIRM` flag.
619    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
620    #[cfg_attr(
621        docsrs,
622        doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
623    )]
624    pub const fn is_confirm(self) -> bool {
625        self.0 & libc::MSG_CONFIRM != 0
626    }
627
628    /// Check if the don't route flag is set.
629    ///
630    /// This is used by SocketCAN to indicate a frame was created
631    /// on the local host.
632    ///
633    /// On Unix this corresponds to the `MSG_DONTROUTE` flag.
634    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
635    #[cfg_attr(
636        docsrs,
637        doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
638    )]
639    pub const fn is_dontroute(self) -> bool {
640        self.0 & libc::MSG_DONTROUTE != 0
641    }
642}
643
644#[cfg(not(target_os = "redox"))]
645impl std::fmt::Debug for RecvFlags {
646    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
647        let mut s = f.debug_struct("RecvFlags");
648        #[cfg(not(target_os = "espidf"))]
649        s.field("is_end_of_record", &self.is_end_of_record());
650        s.field("is_out_of_band", &self.is_out_of_band());
651        #[cfg(not(target_os = "espidf"))]
652        s.field("is_truncated", &self.is_truncated());
653        #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
654        s.field("is_confirm", &self.is_confirm());
655        #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
656        s.field("is_dontroute", &self.is_dontroute());
657        s.finish()
658    }
659}
660
661#[repr(transparent)]
662pub struct MaybeUninitSlice<'a> {
663    vec: libc::iovec,
664    _lifetime: PhantomData<&'a mut [MaybeUninit<u8>]>,
665}
666
667unsafe impl<'a> Send for MaybeUninitSlice<'a> {}
668
669unsafe impl<'a> Sync for MaybeUninitSlice<'a> {}
670
671impl<'a> MaybeUninitSlice<'a> {
672    pub(crate) fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
673        MaybeUninitSlice {
674            vec: libc::iovec {
675                iov_base: buf.as_mut_ptr().cast(),
676                iov_len: buf.len(),
677            },
678            _lifetime: PhantomData,
679        }
680    }
681
682    pub(crate) fn as_slice(&self) -> &[MaybeUninit<u8>] {
683        unsafe { slice::from_raw_parts(self.vec.iov_base.cast(), self.vec.iov_len) }
684    }
685
686    pub(crate) fn as_mut_slice(&mut self) -> &mut [MaybeUninit<u8>] {
687        unsafe { slice::from_raw_parts_mut(self.vec.iov_base.cast(), self.vec.iov_len) }
688    }
689}
690
691/// Returns the offset of the `sun_path` member of the passed unix socket address.
692pub(crate) fn offset_of_path(storage: &libc::sockaddr_un) -> usize {
693    let base = storage as *const _ as usize;
694    let path = ptr::addr_of!(storage.sun_path) as usize;
695    path - base
696}
697
698#[allow(unsafe_op_in_unsafe_fn)]
699pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
700    // SAFETY: a `sockaddr_storage` of all zeros is valid.
701    let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
702    let len = {
703        let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<libc::sockaddr_un>() };
704
705        let bytes = path.as_os_str().as_bytes();
706        let too_long = match bytes.first() {
707            None => false,
708            // linux abstract namespaces aren't null-terminated
709            Some(&0) => bytes.len() > storage.sun_path.len(),
710            Some(_) => bytes.len() >= storage.sun_path.len(),
711        };
712        if too_long {
713            return Err(io::Error::new(
714                io::ErrorKind::InvalidInput,
715                "path must be shorter than SUN_LEN",
716            ));
717        }
718
719        storage.sun_family = libc::AF_UNIX as sa_family_t;
720        // SAFETY: `bytes` and `addr.sun_path` are not overlapping and
721        // both point to valid memory.
722        // `storage` was initialized to zero above, so the path is
723        // already NULL terminated.
724        unsafe {
725            ptr::copy_nonoverlapping(
726                bytes.as_ptr(),
727                storage.sun_path.as_mut_ptr().cast(),
728                bytes.len(),
729            );
730        }
731
732        let sun_path_offset = offset_of_path(storage);
733        sun_path_offset
734            + bytes.len()
735            + match bytes.first() {
736                Some(&0) | None => 0,
737                Some(_) => 1,
738            }
739    };
740    Ok(unsafe { SockAddr::new(storage, len as socklen_t) })
741}
742
743// Used in `MsgHdr`.
744#[cfg(not(target_os = "redox"))]
745pub(crate) use libc::msghdr;
746
747#[cfg(not(target_os = "redox"))]
748pub(crate) fn set_msghdr_name(msg: &mut msghdr, name: &SockAddr) {
749    msg.msg_name = name.as_ptr() as *mut _;
750    msg.msg_namelen = name.len();
751}
752
753#[cfg(not(target_os = "redox"))]
754#[allow(clippy::unnecessary_cast)] // IovLen type can be `usize`.
755pub(crate) fn set_msghdr_iov(msg: &mut msghdr, ptr: *mut libc::iovec, len: usize) {
756    msg.msg_iov = ptr;
757    msg.msg_iovlen = min(len, IovLen::MAX as usize) as IovLen;
758}
759
760#[cfg(not(target_os = "redox"))]
761pub(crate) fn set_msghdr_control(msg: &mut msghdr, ptr: *mut libc::c_void, len: usize) {
762    msg.msg_control = ptr;
763    msg.msg_controllen = len as _;
764}
765
766#[cfg(not(target_os = "redox"))]
767pub(crate) fn set_msghdr_flags(msg: &mut msghdr, flags: libc::c_int) {
768    msg.msg_flags = flags;
769}
770
771#[cfg(not(target_os = "redox"))]
772pub(crate) fn msghdr_flags(msg: &msghdr) -> RecvFlags {
773    RecvFlags(msg.msg_flags)
774}
775
776#[cfg(not(target_os = "redox"))]
777pub(crate) fn msghdr_control_len(msg: &msghdr) -> usize {
778    msg.msg_controllen as _
779}
780
781/// Unix only API.
782impl SockAddr {
783    /// Constructs a `SockAddr` with the family `AF_VSOCK` and the provided CID/port.
784    ///
785    /// # Errors
786    ///
787    /// This function can never fail. In a future version of this library it will be made
788    /// infallible.
789    #[allow(unsafe_op_in_unsafe_fn)]
790    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
791    #[cfg_attr(
792        docsrs,
793        doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
794    )]
795    pub fn vsock(cid: u32, port: u32) -> SockAddr {
796        // SAFETY: a `sockaddr_storage` of all zeros is valid.
797        let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
798        {
799            let storage: &mut libc::sockaddr_vm =
800                unsafe { &mut *((&mut storage as *mut sockaddr_storage).cast()) };
801            storage.svm_family = libc::AF_VSOCK as sa_family_t;
802            storage.svm_cid = cid;
803            storage.svm_port = port;
804        }
805        unsafe { SockAddr::new(storage, mem::size_of::<libc::sockaddr_vm>() as socklen_t) }
806    }
807
808    /// Returns this address VSOCK CID/port if it is in the `AF_VSOCK` family,
809    /// otherwise return `None`.
810    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
811    #[cfg_attr(
812        docsrs,
813        doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
814    )]
815    pub fn as_vsock_address(&self) -> Option<(u32, u32)> {
816        if self.family() == libc::AF_VSOCK as sa_family_t {
817            // Safety: if the ss_family field is AF_VSOCK then storage must be a sockaddr_vm.
818            let addr = unsafe { &*(self.as_ptr() as *const libc::sockaddr_vm) };
819            Some((addr.svm_cid, addr.svm_port))
820        } else {
821            None
822        }
823    }
824
825    /// Returns true if this address is an unnamed address from the `AF_UNIX` family (for local
826    /// interprocess communication), false otherwise.
827    pub fn is_unnamed(&self) -> bool {
828        self.as_sockaddr_un()
829            .map(|storage| {
830                self.len() == offset_of_path(storage) as _
831                    // On some non-linux platforms a zeroed path is returned for unnamed.
832                    // Abstract addresses only exist on Linux.
833                    // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented.
834                    // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978
835                    || (cfg!(not(any(target_os = "linux", target_os = "android")))
836                    && storage.sun_path[0] == 0)
837            })
838            .unwrap_or_default()
839    }
840
841    /// Returns the underlying `sockaddr_un` object if this addres is from the `AF_UNIX` family,
842    /// otherwise returns `None`.
843    pub(crate) fn as_sockaddr_un(&self) -> Option<&libc::sockaddr_un> {
844        self.is_unix().then(|| {
845            // SAFETY: if unix socket, i.e. the `ss_family` field is `AF_UNIX` then storage must be
846            // a `sockaddr_un`.
847            unsafe { &*self.as_ptr().cast::<libc::sockaddr_un>() }
848        })
849    }
850
851    /// Get the length of the path bytes of the address, not including the terminating or initial
852    /// (for abstract names) null byte.
853    ///
854    /// Should not be called on unnamed addresses.
855    fn path_len(&self, storage: &libc::sockaddr_un) -> usize {
856        debug_assert!(!self.is_unnamed());
857        self.len() as usize - offset_of_path(storage) - 1
858    }
859
860    /// Get a u8 slice for the bytes of the pathname or abstract name.
861    ///
862    /// Should not be called on unnamed addresses.
863    fn path_bytes(&self, storage: &libc::sockaddr_un, abstract_name: bool) -> &[u8] {
864        debug_assert!(!self.is_unnamed());
865        // SAFETY: the pointed objects of type `i8` have the same memory layout as `u8`. The path is
866        // the last field in the storage and so its length is equal to
867        //          TOTAL_LENGTH - OFFSET_OF_PATH -1
868        // Where the 1 is either a terminating null if we have a pathname address, or the initial
869        // null byte, if it's an abstract name address. In the latter case, the path bytes start
870        // after the initial null byte, hence the `offset`.
871        // There is no safe way to convert a `&[i8]` to `&[u8]`
872        unsafe {
873            slice::from_raw_parts(
874                (storage.sun_path.as_ptr() as *const u8).offset(abstract_name as isize),
875                self.path_len(storage),
876            )
877        }
878    }
879
880    /// Returns this address as Unix `SocketAddr` if it is an `AF_UNIX` pathname
881    /// address, otherwise returns `None`.
882    pub fn as_unix(&self) -> Option<std::os::unix::net::SocketAddr> {
883        let path = self.as_pathname()?;
884        // SAFETY: we can represent this as a valid pathname, then so can the
885        // standard library.
886        Some(std::os::unix::net::SocketAddr::from_pathname(path).unwrap())
887    }
888
889    /// Returns this address as a `Path` reference if it is an `AF_UNIX`
890    /// pathname address, otherwise returns `None`.
891    pub fn as_pathname(&self) -> Option<&Path> {
892        self.as_sockaddr_un().and_then(|storage| {
893            (self.len() > offset_of_path(storage) as _ && storage.sun_path[0] != 0).then(|| {
894                let path_slice = self.path_bytes(storage, false);
895                Path::new::<OsStr>(OsStrExt::from_bytes(path_slice))
896            })
897        })
898    }
899
900    /// Returns this address as a slice of bytes representing an abstract address if it is an
901    /// `AF_UNIX` abstract address, otherwise returns `None`.
902    ///
903    /// Abstract addresses are a Linux extension, so this method returns `None` on all non-Linux
904    /// platforms.
905    pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
906        // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented.
907        // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978
908        #[cfg(any(target_os = "linux", target_os = "android"))]
909        {
910            self.as_sockaddr_un().and_then(|storage| {
911                (self.len() > offset_of_path(storage) as _ && storage.sun_path[0] == 0)
912                    .then(|| self.path_bytes(storage, true))
913            })
914        }
915        #[cfg(not(any(target_os = "linux", target_os = "android")))]
916        None
917    }
918}
919
920pub(crate) type Socket = c_int;
921
922pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
923    crate::socket::Inner::from_raw_fd(socket)
924}
925
926pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket {
927    socket.as_raw_fd()
928}
929
930pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket {
931    socket.into_raw_fd()
932}
933
934pub(crate) fn socket(family: c_int, ty: c_int, protocol: c_int) -> io::Result<Socket> {
935    syscall!(socket(family, ty, protocol))
936}
937
938#[cfg(all(feature = "all", unix))]
939#[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
940pub(crate) fn socketpair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<[Socket; 2]> {
941    let mut fds = [0, 0];
942    syscall!(socketpair(family, ty, protocol, fds.as_mut_ptr())).map(|_| fds)
943}
944
945pub(crate) fn bind(fd: Socket, addr: &SockAddr) -> io::Result<()> {
946    syscall!(bind(fd, addr.as_ptr(), addr.len() as _)).map(|_| ())
947}
948
949pub(crate) fn connect(fd: Socket, addr: &SockAddr) -> io::Result<()> {
950    syscall!(connect(fd, addr.as_ptr(), addr.len())).map(|_| ())
951}
952
953pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> {
954    let start = Instant::now();
955
956    let mut pollfd = libc::pollfd {
957        fd: socket.as_raw(),
958        events: libc::POLLIN | libc::POLLOUT,
959        revents: 0,
960    };
961
962    loop {
963        let elapsed = start.elapsed();
964        if elapsed >= timeout {
965            return Err(io::ErrorKind::TimedOut.into());
966        }
967
968        let timeout = (timeout - elapsed).as_millis();
969        let timeout = timeout.clamp(1, c_int::MAX as u128) as c_int;
970
971        match syscall!(poll(&mut pollfd, 1, timeout)) {
972            Ok(0) => return Err(io::ErrorKind::TimedOut.into()),
973            Ok(_) => {
974                // Error or hang up indicates an error (or failure to connect).
975                if (pollfd.revents & libc::POLLHUP) != 0 || (pollfd.revents & libc::POLLERR) != 0 {
976                    match socket.take_error() {
977                        Ok(Some(err)) | Err(err) => return Err(err),
978                        Ok(None) => {
979                            return Err(io::Error::new(
980                                io::ErrorKind::Other,
981                                "no error set after POLLHUP",
982                            ))
983                        }
984                    }
985                }
986                return Ok(());
987            }
988            // Got interrupted, try again.
989            Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue,
990            Err(err) => return Err(err),
991        }
992    }
993}
994
995pub(crate) fn listen(fd: Socket, backlog: c_int) -> io::Result<()> {
996    syscall!(listen(fd, backlog)).map(|_| ())
997}
998
999pub(crate) fn accept(fd: Socket) -> io::Result<(Socket, SockAddr)> {
1000    // Safety: `accept` initialises the `SockAddr` for us.
1001    unsafe { SockAddr::try_init(|storage, len| syscall!(accept(fd, storage.cast(), len))) }
1002}
1003
1004pub(crate) fn getsockname(fd: Socket) -> io::Result<SockAddr> {
1005    // Safety: `accept` initialises the `SockAddr` for us.
1006    unsafe { SockAddr::try_init(|storage, len| syscall!(getsockname(fd, storage.cast(), len))) }
1007        .map(|(_, addr)| addr)
1008}
1009
1010pub(crate) fn getpeername(fd: Socket) -> io::Result<SockAddr> {
1011    // Safety: `accept` initialises the `SockAddr` for us.
1012    unsafe { SockAddr::try_init(|storage, len| syscall!(getpeername(fd, storage.cast(), len))) }
1013        .map(|(_, addr)| addr)
1014}
1015
1016pub(crate) fn try_clone(fd: Socket) -> io::Result<Socket> {
1017    syscall!(fcntl(fd, libc::F_DUPFD_CLOEXEC, 0))
1018}
1019
1020#[cfg(all(feature = "all", unix, not(target_os = "vita")))]
1021pub(crate) fn nonblocking(fd: Socket) -> io::Result<bool> {
1022    let file_status_flags = fcntl_get(fd, libc::F_GETFL)?;
1023    Ok((file_status_flags & libc::O_NONBLOCK) != 0)
1024}
1025
1026#[cfg(all(feature = "all", target_os = "vita"))]
1027pub(crate) fn nonblocking(fd: Socket) -> io::Result<bool> {
1028    unsafe {
1029        getsockopt::<Bool>(fd, libc::SOL_SOCKET, libc::SO_NONBLOCK).map(|non_block| non_block != 0)
1030    }
1031}
1032
1033#[cfg(not(target_os = "vita"))]
1034pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> {
1035    if nonblocking {
1036        fcntl_add(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK)
1037    } else {
1038        fcntl_remove(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK)
1039    }
1040}
1041
1042#[cfg(target_os = "vita")]
1043pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> {
1044    unsafe {
1045        setsockopt(
1046            fd,
1047            libc::SOL_SOCKET,
1048            libc::SO_NONBLOCK,
1049            nonblocking as libc::c_int,
1050        )
1051    }
1052}
1053
1054pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> {
1055    let how = match how {
1056        Shutdown::Write => libc::SHUT_WR,
1057        Shutdown::Read => libc::SHUT_RD,
1058        Shutdown::Both => libc::SHUT_RDWR,
1059    };
1060    syscall!(shutdown(fd, how)).map(|_| ())
1061}
1062
1063pub(crate) fn recv(fd: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int) -> io::Result<usize> {
1064    syscall!(recv(
1065        fd,
1066        buf.as_mut_ptr().cast(),
1067        min(buf.len(), MAX_BUF_LEN),
1068        flags,
1069    ))
1070    .map(|n| n as usize)
1071}
1072
1073pub(crate) fn recv_from(
1074    fd: Socket,
1075    buf: &mut [MaybeUninit<u8>],
1076    flags: c_int,
1077) -> io::Result<(usize, SockAddr)> {
1078    // Safety: `recvfrom` initialises the `SockAddr` for us.
1079    unsafe {
1080        SockAddr::try_init(|addr, addrlen| {
1081            syscall!(recvfrom(
1082                fd,
1083                buf.as_mut_ptr().cast(),
1084                min(buf.len(), MAX_BUF_LEN),
1085                flags,
1086                addr.cast(),
1087                addrlen
1088            ))
1089            .map(|n| n as usize)
1090        })
1091    }
1092}
1093
1094pub(crate) fn peek_sender(fd: Socket) -> io::Result<SockAddr> {
1095    // Unix-like platforms simply truncate the returned data, so this implementation is trivial.
1096    // However, for Windows this requires suppressing the `WSAEMSGSIZE` error,
1097    // so that requires a different approach.
1098    // NOTE: macOS does not populate `sockaddr` if you pass a zero-sized buffer.
1099    let (_, sender) = recv_from(fd, &mut [MaybeUninit::uninit(); 8], MSG_PEEK)?;
1100    Ok(sender)
1101}
1102
1103#[cfg(not(target_os = "redox"))]
1104pub(crate) fn recv_vectored(
1105    fd: Socket,
1106    bufs: &mut [crate::MaybeUninitSlice<'_>],
1107    flags: c_int,
1108) -> io::Result<(usize, RecvFlags)> {
1109    let mut msg = MsgHdrMut::new().with_buffers(bufs);
1110    let n = recvmsg(fd, &mut msg, flags)?;
1111    Ok((n, msg.flags()))
1112}
1113
1114#[cfg(not(target_os = "redox"))]
1115pub(crate) fn recv_from_vectored(
1116    fd: Socket,
1117    bufs: &mut [crate::MaybeUninitSlice<'_>],
1118    flags: c_int,
1119) -> io::Result<(usize, RecvFlags, SockAddr)> {
1120    let mut msg = MsgHdrMut::new().with_buffers(bufs);
1121    // SAFETY: `recvmsg` initialises the address storage and we set the length
1122    // manually.
1123    let (n, addr) = unsafe {
1124        SockAddr::try_init(|storage, len| {
1125            msg.inner.msg_name = storage.cast();
1126            msg.inner.msg_namelen = *len;
1127            let n = recvmsg(fd, &mut msg, flags)?;
1128            // Set the correct address length.
1129            *len = msg.inner.msg_namelen;
1130            Ok(n)
1131        })?
1132    };
1133    Ok((n, msg.flags(), addr))
1134}
1135
1136#[cfg(not(target_os = "redox"))]
1137pub(crate) fn recvmsg(
1138    fd: Socket,
1139    msg: &mut MsgHdrMut<'_, '_, '_>,
1140    flags: c_int,
1141) -> io::Result<usize> {
1142    syscall!(recvmsg(fd, &mut msg.inner, flags)).map(|n| n as usize)
1143}
1144
1145pub(crate) fn send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result<usize> {
1146    syscall!(send(
1147        fd,
1148        buf.as_ptr().cast(),
1149        min(buf.len(), MAX_BUF_LEN),
1150        flags,
1151    ))
1152    .map(|n| n as usize)
1153}
1154
1155#[cfg(not(target_os = "redox"))]
1156pub(crate) fn send_vectored(fd: Socket, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result<usize> {
1157    let msg = MsgHdr::new().with_buffers(bufs);
1158    sendmsg(fd, &msg, flags)
1159}
1160
1161pub(crate) fn send_to(fd: Socket, buf: &[u8], addr: &SockAddr, flags: c_int) -> io::Result<usize> {
1162    syscall!(sendto(
1163        fd,
1164        buf.as_ptr().cast(),
1165        min(buf.len(), MAX_BUF_LEN),
1166        flags,
1167        addr.as_ptr(),
1168        addr.len(),
1169    ))
1170    .map(|n| n as usize)
1171}
1172
1173#[cfg(not(target_os = "redox"))]
1174pub(crate) fn send_to_vectored(
1175    fd: Socket,
1176    bufs: &[IoSlice<'_>],
1177    addr: &SockAddr,
1178    flags: c_int,
1179) -> io::Result<usize> {
1180    let msg = MsgHdr::new().with_addr(addr).with_buffers(bufs);
1181    sendmsg(fd, &msg, flags)
1182}
1183
1184#[cfg(not(target_os = "redox"))]
1185pub(crate) fn sendmsg(fd: Socket, msg: &MsgHdr<'_, '_, '_>, flags: c_int) -> io::Result<usize> {
1186    syscall!(sendmsg(fd, &msg.inner, flags)).map(|n| n as usize)
1187}
1188
1189/// Wrapper around `getsockopt` to deal with platform specific timeouts.
1190pub(crate) fn timeout_opt(fd: Socket, opt: c_int, val: c_int) -> io::Result<Option<Duration>> {
1191    unsafe { getsockopt(fd, opt, val).map(from_timeval) }
1192}
1193
1194const fn from_timeval(duration: libc::timeval) -> Option<Duration> {
1195    if duration.tv_sec == 0 && duration.tv_usec == 0 {
1196        None
1197    } else {
1198        let sec = duration.tv_sec as u64;
1199        let nsec = (duration.tv_usec as u32) * 1000;
1200        Some(Duration::new(sec, nsec))
1201    }
1202}
1203
1204/// Wrapper around `setsockopt` to deal with platform specific timeouts.
1205pub(crate) fn set_timeout_opt(
1206    fd: Socket,
1207    opt: c_int,
1208    val: c_int,
1209    duration: Option<Duration>,
1210) -> io::Result<()> {
1211    let duration = into_timeval(duration);
1212    unsafe { setsockopt(fd, opt, val, duration) }
1213}
1214
1215fn into_timeval(duration: Option<Duration>) -> libc::timeval {
1216    match duration {
1217        // https://github.com/rust-lang/libc/issues/1848
1218        #[cfg_attr(target_env = "musl", allow(deprecated))]
1219        Some(duration) => libc::timeval {
1220            tv_sec: min(duration.as_secs(), libc::time_t::MAX as u64) as libc::time_t,
1221            tv_usec: duration.subsec_micros() as libc::suseconds_t,
1222        },
1223        None => libc::timeval {
1224            tv_sec: 0,
1225            tv_usec: 0,
1226        },
1227    }
1228}
1229
1230#[cfg(all(
1231    feature = "all",
1232    not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita"))
1233))]
1234#[cfg_attr(
1235    docsrs,
1236    doc(cfg(all(
1237        feature = "all",
1238        not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita"))
1239    )))
1240)]
1241pub(crate) fn keepalive_time(fd: Socket) -> io::Result<Duration> {
1242    unsafe {
1243        getsockopt::<c_int>(fd, IPPROTO_TCP, KEEPALIVE_TIME)
1244            .map(|secs| Duration::from_secs(secs as u64))
1245    }
1246}
1247
1248#[allow(unused_variables)]
1249pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
1250    #[cfg(not(any(
1251        target_os = "haiku",
1252        target_os = "openbsd",
1253        target_os = "nto",
1254        target_os = "vita"
1255    )))]
1256    if let Some(time) = keepalive.time {
1257        let secs = into_secs(time);
1258        unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
1259    }
1260
1261    #[cfg(any(
1262        target_os = "aix",
1263        target_os = "android",
1264        target_os = "dragonfly",
1265        target_os = "freebsd",
1266        target_os = "fuchsia",
1267        target_os = "hurd",
1268        target_os = "illumos",
1269        target_os = "ios",
1270        target_os = "visionos",
1271        target_os = "linux",
1272        target_os = "macos",
1273        target_os = "netbsd",
1274        target_os = "tvos",
1275        target_os = "watchos",
1276    ))]
1277    {
1278        if let Some(interval) = keepalive.interval {
1279            let secs = into_secs(interval);
1280            unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, secs)? }
1281        }
1282
1283        if let Some(retries) = keepalive.retries {
1284            unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, retries as c_int)? }
1285        }
1286    }
1287
1288    #[cfg(target_os = "nto")]
1289    if let Some(time) = keepalive.time {
1290        let secs = into_timeval(Some(time));
1291        unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
1292    }
1293
1294    Ok(())
1295}
1296
1297#[cfg(not(any(
1298    target_os = "haiku",
1299    target_os = "openbsd",
1300    target_os = "nto",
1301    target_os = "vita"
1302)))]
1303fn into_secs(duration: Duration) -> c_int {
1304    min(duration.as_secs(), c_int::MAX as u64) as c_int
1305}
1306
1307/// Get the flags using `cmd`.
1308#[cfg(not(target_os = "vita"))]
1309fn fcntl_get(fd: Socket, cmd: c_int) -> io::Result<c_int> {
1310    syscall!(fcntl(fd, cmd))
1311}
1312
1313/// Add `flag` to the current set flags of `F_GETFD`.
1314#[cfg(not(target_os = "vita"))]
1315fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
1316    let previous = fcntl_get(fd, get_cmd)?;
1317    let new = previous | flag;
1318    if new != previous {
1319        syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
1320    } else {
1321        // Flag was already set.
1322        Ok(())
1323    }
1324}
1325
1326/// Remove `flag` to the current set flags of `F_GETFD`.
1327#[cfg(not(target_os = "vita"))]
1328fn fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
1329    let previous = fcntl_get(fd, get_cmd)?;
1330    let new = previous & !flag;
1331    if new != previous {
1332        syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
1333    } else {
1334        // Flag was already set.
1335        Ok(())
1336    }
1337}
1338
1339/// Caller must ensure `T` is the correct type for `opt` and `val`.
1340pub(crate) unsafe fn getsockopt<T>(fd: Socket, opt: c_int, val: c_int) -> io::Result<T> {
1341    let mut payload: MaybeUninit<T> = MaybeUninit::uninit();
1342    let mut len = size_of::<T>() as libc::socklen_t;
1343    syscall!(getsockopt(
1344        fd,
1345        opt,
1346        val,
1347        payload.as_mut_ptr().cast(),
1348        &mut len,
1349    ))
1350    .map(|_| {
1351        debug_assert_eq!(len as usize, size_of::<T>());
1352        // Safety: `getsockopt` initialised `payload` for us.
1353        payload.assume_init()
1354    })
1355}
1356
1357/// Caller must ensure `T` is the correct type for `opt` and `val`.
1358pub(crate) unsafe fn setsockopt<T>(
1359    fd: Socket,
1360    opt: c_int,
1361    val: c_int,
1362    payload: T,
1363) -> io::Result<()> {
1364    let payload = ptr::addr_of!(payload).cast();
1365    syscall!(setsockopt(
1366        fd,
1367        opt,
1368        val,
1369        payload,
1370        mem::size_of::<T>() as libc::socklen_t,
1371    ))
1372    .map(|_| ())
1373}
1374
1375pub(crate) const fn to_in_addr(addr: &Ipv4Addr) -> in_addr {
1376    // `s_addr` is stored as BE on all machines, and the array is in BE order.
1377    // So the native endian conversion method is used so that it's never
1378    // swapped.
1379    in_addr {
1380        s_addr: u32::from_ne_bytes(addr.octets()),
1381    }
1382}
1383
1384pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr {
1385    Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
1386}
1387
1388pub(crate) const fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr {
1389    in6_addr {
1390        s6_addr: addr.octets(),
1391    }
1392}
1393
1394pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
1395    Ipv6Addr::from(addr.s6_addr)
1396}
1397
1398#[cfg(not(any(
1399    target_os = "aix",
1400    target_os = "haiku",
1401    target_os = "illumos",
1402    target_os = "netbsd",
1403    target_os = "openbsd",
1404    target_os = "redox",
1405    target_os = "solaris",
1406    target_os = "nto",
1407    target_os = "espidf",
1408    target_os = "vita",
1409)))]
1410pub(crate) const fn to_mreqn(
1411    multiaddr: &Ipv4Addr,
1412    interface: &crate::socket::InterfaceIndexOrAddress,
1413) -> libc::ip_mreqn {
1414    match interface {
1415        crate::socket::InterfaceIndexOrAddress::Index(interface) => libc::ip_mreqn {
1416            imr_multiaddr: to_in_addr(multiaddr),
1417            imr_address: to_in_addr(&Ipv4Addr::UNSPECIFIED),
1418            imr_ifindex: *interface as _,
1419        },
1420        crate::socket::InterfaceIndexOrAddress::Address(interface) => libc::ip_mreqn {
1421            imr_multiaddr: to_in_addr(multiaddr),
1422            imr_address: to_in_addr(interface),
1423            imr_ifindex: 0,
1424        },
1425    }
1426}
1427
1428#[cfg(all(
1429    feature = "all",
1430    any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1431))]
1432pub(crate) fn original_dst(fd: Socket) -> io::Result<SockAddr> {
1433    // Safety: `getsockopt` initialises the `SockAddr` for us.
1434    unsafe {
1435        SockAddr::try_init(|storage, len| {
1436            syscall!(getsockopt(
1437                fd,
1438                libc::SOL_IP,
1439                libc::SO_ORIGINAL_DST,
1440                storage.cast(),
1441                len
1442            ))
1443        })
1444    }
1445    .map(|(_, addr)| addr)
1446}
1447
1448/// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
1449///
1450/// This value contains the original destination IPv6 address of the connection
1451/// redirected using `ip6tables` `REDIRECT` or `TPROXY`.
1452#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1453pub(crate) fn original_dst_ipv6(fd: Socket) -> io::Result<SockAddr> {
1454    // Safety: `getsockopt` initialises the `SockAddr` for us.
1455    unsafe {
1456        SockAddr::try_init(|storage, len| {
1457            syscall!(getsockopt(
1458                fd,
1459                libc::SOL_IPV6,
1460                libc::IP6T_SO_ORIGINAL_DST,
1461                storage.cast(),
1462                len
1463            ))
1464        })
1465    }
1466    .map(|(_, addr)| addr)
1467}
1468
1469/// Unix only API.
1470impl crate::Socket {
1471    /// Accept a new incoming connection from this listener.
1472    ///
1473    /// This function directly corresponds to the `accept4(2)` function.
1474    ///
1475    /// This function will block the calling thread until a new connection is
1476    /// established. When established, the corresponding `Socket` and the remote
1477    /// peer's address will be returned.
1478    #[doc = man_links!(unix: accept4(2))]
1479    #[cfg(all(
1480        feature = "all",
1481        any(
1482            target_os = "android",
1483            target_os = "dragonfly",
1484            target_os = "freebsd",
1485            target_os = "fuchsia",
1486            target_os = "illumos",
1487            target_os = "linux",
1488            target_os = "netbsd",
1489            target_os = "openbsd",
1490        )
1491    ))]
1492    #[cfg_attr(
1493        docsrs,
1494        doc(cfg(all(
1495            feature = "all",
1496            any(
1497                target_os = "android",
1498                target_os = "dragonfly",
1499                target_os = "freebsd",
1500                target_os = "fuchsia",
1501                target_os = "illumos",
1502                target_os = "linux",
1503                target_os = "netbsd",
1504                target_os = "openbsd",
1505            )
1506        )))
1507    )]
1508    pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
1509        self._accept4(flags)
1510    }
1511
1512    #[cfg(any(
1513        target_os = "android",
1514        target_os = "dragonfly",
1515        target_os = "freebsd",
1516        target_os = "fuchsia",
1517        target_os = "illumos",
1518        target_os = "linux",
1519        target_os = "netbsd",
1520        target_os = "openbsd",
1521    ))]
1522    pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
1523        // Safety: `accept4` initialises the `SockAddr` for us.
1524        unsafe {
1525            SockAddr::try_init(|storage, len| {
1526                syscall!(accept4(self.as_raw(), storage.cast(), len, flags))
1527                    .map(crate::Socket::from_raw)
1528            })
1529        }
1530    }
1531
1532    /// Sets `CLOEXEC` on the socket.
1533    ///
1534    /// # Notes
1535    ///
1536    /// On supported platforms you can use [`Type::cloexec`].
1537    #[cfg_attr(
1538        any(
1539            target_os = "ios",
1540            target_os = "visionos",
1541            target_os = "macos",
1542            target_os = "tvos",
1543            target_os = "watchos"
1544        ),
1545        allow(rustdoc::broken_intra_doc_links)
1546    )]
1547    #[cfg(all(feature = "all", not(target_os = "vita")))]
1548    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
1549    pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
1550        self._set_cloexec(close_on_exec)
1551    }
1552
1553    #[cfg(not(target_os = "vita"))]
1554    pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
1555        if close_on_exec {
1556            fcntl_add(
1557                self.as_raw(),
1558                libc::F_GETFD,
1559                libc::F_SETFD,
1560                libc::FD_CLOEXEC,
1561            )
1562        } else {
1563            fcntl_remove(
1564                self.as_raw(),
1565                libc::F_GETFD,
1566                libc::F_SETFD,
1567                libc::FD_CLOEXEC,
1568            )
1569        }
1570    }
1571
1572    /// Sets `SO_NOSIGPIPE` on the socket.
1573    #[cfg(all(
1574        feature = "all",
1575        any(
1576            target_os = "ios",
1577            target_os = "visionos",
1578            target_os = "macos",
1579            target_os = "tvos",
1580            target_os = "watchos",
1581        )
1582    ))]
1583    #[cfg_attr(
1584        docsrs,
1585        doc(cfg(all(
1586            feature = "all",
1587            any(
1588                target_os = "ios",
1589                target_os = "visionos",
1590                target_os = "macos",
1591                target_os = "tvos",
1592                target_os = "watchos",
1593            )
1594        )))
1595    )]
1596    pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
1597        self._set_nosigpipe(nosigpipe)
1598    }
1599
1600    #[cfg(any(
1601        target_os = "ios",
1602        target_os = "visionos",
1603        target_os = "macos",
1604        target_os = "tvos",
1605        target_os = "watchos",
1606    ))]
1607    pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
1608        unsafe {
1609            setsockopt(
1610                self.as_raw(),
1611                libc::SOL_SOCKET,
1612                libc::SO_NOSIGPIPE,
1613                nosigpipe as c_int,
1614            )
1615        }
1616    }
1617
1618    /// Gets the value of the `TCP_MAXSEG` option on this socket.
1619    ///
1620    /// For more information about this option, see [`set_mss`].
1621    ///
1622    /// [`set_mss`]: crate::Socket::set_mss
1623    #[cfg(all(feature = "all", not(target_os = "redox")))]
1624    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
1625    pub fn mss(&self) -> io::Result<u32> {
1626        unsafe {
1627            getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_MAXSEG)
1628                .map(|mss| mss as u32)
1629        }
1630    }
1631
1632    /// Sets the value of the `TCP_MAXSEG` option on this socket.
1633    ///
1634    /// The `TCP_MAXSEG` option denotes the TCP Maximum Segment Size and is only
1635    /// available on TCP sockets.
1636    #[cfg(all(feature = "all", not(target_os = "redox")))]
1637    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
1638    pub fn set_mss(&self, mss: u32) -> io::Result<()> {
1639        unsafe {
1640            setsockopt(
1641                self.as_raw(),
1642                libc::IPPROTO_TCP,
1643                libc::TCP_MAXSEG,
1644                mss as c_int,
1645            )
1646        }
1647    }
1648
1649    /// Returns `true` if `listen(2)` was called on this socket by checking the
1650    /// `SO_ACCEPTCONN` option on this socket.
1651    #[cfg(all(
1652        feature = "all",
1653        any(
1654            target_os = "aix",
1655            target_os = "android",
1656            target_os = "freebsd",
1657            target_os = "fuchsia",
1658            target_os = "linux",
1659        )
1660    ))]
1661    #[cfg_attr(
1662        docsrs,
1663        doc(cfg(all(
1664            feature = "all",
1665            any(
1666                target_os = "aix",
1667                target_os = "android",
1668                target_os = "freebsd",
1669                target_os = "fuchsia",
1670                target_os = "linux",
1671            )
1672        )))
1673    )]
1674    pub fn is_listener(&self) -> io::Result<bool> {
1675        unsafe {
1676            getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_ACCEPTCONN)
1677                .map(|v| v != 0)
1678        }
1679    }
1680
1681    /// Returns the [`Domain`] of this socket by checking the `SO_DOMAIN` option
1682    /// on this socket.
1683    #[cfg(all(
1684        feature = "all",
1685        any(
1686            target_os = "android",
1687            // TODO: add FreeBSD.
1688            // target_os = "freebsd",
1689            target_os = "fuchsia",
1690            target_os = "linux",
1691        )
1692    ))]
1693    #[cfg_attr(docsrs, doc(cfg(all(
1694        feature = "all",
1695        any(
1696            target_os = "android",
1697            // TODO: add FreeBSD.
1698            // target_os = "freebsd",
1699            target_os = "fuchsia",
1700            target_os = "linux",
1701        )
1702    ))))]
1703    pub fn domain(&self) -> io::Result<Domain> {
1704        unsafe { getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_DOMAIN).map(Domain) }
1705    }
1706
1707    /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL`
1708    /// option on this socket.
1709    #[cfg(all(
1710        feature = "all",
1711        any(
1712            target_os = "android",
1713            target_os = "freebsd",
1714            target_os = "fuchsia",
1715            target_os = "linux",
1716        )
1717    ))]
1718    #[cfg_attr(
1719        docsrs,
1720        doc(cfg(all(
1721            feature = "all",
1722            any(
1723                target_os = "android",
1724                target_os = "freebsd",
1725                target_os = "fuchsia",
1726                target_os = "linux",
1727            )
1728        )))
1729    )]
1730    pub fn protocol(&self) -> io::Result<Option<Protocol>> {
1731        unsafe {
1732            getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_PROTOCOL).map(|v| match v
1733            {
1734                0 => None,
1735                p => Some(Protocol(p)),
1736            })
1737        }
1738    }
1739
1740    /// Gets the value for the `SO_MARK` option on this socket.
1741    ///
1742    /// This value gets the socket mark field for each packet sent through
1743    /// this socket.
1744    ///
1745    /// On Linux this function requires the `CAP_NET_ADMIN` capability.
1746    #[cfg(all(
1747        feature = "all",
1748        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1749    ))]
1750    #[cfg_attr(
1751        docsrs,
1752        doc(cfg(all(
1753            feature = "all",
1754            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1755        )))
1756    )]
1757    pub fn mark(&self) -> io::Result<u32> {
1758        unsafe {
1759            getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_MARK)
1760                .map(|mark| mark as u32)
1761        }
1762    }
1763
1764    /// Sets the value for the `SO_MARK` option on this socket.
1765    ///
1766    /// This value sets the socket mark field for each packet sent through
1767    /// this socket. Changing the mark can be used for mark-based routing
1768    /// without netfilter or for packet filtering.
1769    ///
1770    /// On Linux this function requires the `CAP_NET_ADMIN` capability.
1771    #[cfg(all(
1772        feature = "all",
1773        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1774    ))]
1775    #[cfg_attr(
1776        docsrs,
1777        doc(cfg(all(
1778            feature = "all",
1779            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1780        )))
1781    )]
1782    pub fn set_mark(&self, mark: u32) -> io::Result<()> {
1783        unsafe {
1784            setsockopt::<c_int>(
1785                self.as_raw(),
1786                libc::SOL_SOCKET,
1787                libc::SO_MARK,
1788                mark as c_int,
1789            )
1790        }
1791    }
1792
1793    /// Get the value of the `TCP_CORK` option on this socket.
1794    ///
1795    /// For more information about this option, see [`set_cork`].
1796    ///
1797    /// [`set_cork`]: crate::Socket::set_cork
1798    #[cfg(all(
1799        feature = "all",
1800        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1801    ))]
1802    #[cfg_attr(
1803        docsrs,
1804        doc(cfg(all(
1805            feature = "all",
1806            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1807        )))
1808    )]
1809    pub fn cork(&self) -> io::Result<bool> {
1810        unsafe {
1811            getsockopt::<Bool>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_CORK)
1812                .map(|cork| cork != 0)
1813        }
1814    }
1815
1816    /// Set the value of the `TCP_CORK` option on this socket.
1817    ///
1818    /// If set, don't send out partial frames. All queued partial frames are
1819    /// sent when the option is cleared again. There is a 200 millisecond ceiling on
1820    /// the time for which output is corked by `TCP_CORK`. If this ceiling is reached,
1821    /// then queued data is automatically transmitted.
1822    #[cfg(all(
1823        feature = "all",
1824        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1825    ))]
1826    #[cfg_attr(
1827        docsrs,
1828        doc(cfg(all(
1829            feature = "all",
1830            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1831        )))
1832    )]
1833    pub fn set_cork(&self, cork: bool) -> io::Result<()> {
1834        unsafe {
1835            setsockopt(
1836                self.as_raw(),
1837                libc::IPPROTO_TCP,
1838                libc::TCP_CORK,
1839                cork as c_int,
1840            )
1841        }
1842    }
1843
1844    /// Get the value of the `TCP_QUICKACK` option on this socket.
1845    ///
1846    /// For more information about this option, see [`set_quickack`].
1847    ///
1848    /// [`set_quickack`]: crate::Socket::set_quickack
1849    #[cfg(all(
1850        feature = "all",
1851        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1852    ))]
1853    #[cfg_attr(
1854        docsrs,
1855        doc(cfg(all(
1856            feature = "all",
1857            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1858        )))
1859    )]
1860    pub fn quickack(&self) -> io::Result<bool> {
1861        unsafe {
1862            getsockopt::<Bool>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_QUICKACK)
1863                .map(|quickack| quickack != 0)
1864        }
1865    }
1866
1867    /// Set the value of the `TCP_QUICKACK` option on this socket.
1868    ///
1869    /// If set, acks are sent immediately, rather than delayed if needed in accordance to normal
1870    /// TCP operation. This flag is not permanent, it only enables a switch to or from quickack mode.
1871    /// Subsequent operation of the TCP protocol will once again enter/leave quickack mode depending on
1872    /// internal protocol processing and factors such as delayed ack timeouts occurring and data transfer.
1873    #[cfg(all(
1874        feature = "all",
1875        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1876    ))]
1877    #[cfg_attr(
1878        docsrs,
1879        doc(cfg(all(
1880            feature = "all",
1881            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1882        )))
1883    )]
1884    pub fn set_quickack(&self, quickack: bool) -> io::Result<()> {
1885        unsafe {
1886            setsockopt(
1887                self.as_raw(),
1888                libc::IPPROTO_TCP,
1889                libc::TCP_QUICKACK,
1890                quickack as c_int,
1891            )
1892        }
1893    }
1894
1895    /// Get the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket.
1896    ///
1897    /// For more information about this option, see [`set_thin_linear_timeouts`].
1898    ///
1899    /// [`set_thin_linear_timeouts`]: crate::Socket::set_thin_linear_timeouts
1900    #[cfg(all(
1901        feature = "all",
1902        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1903    ))]
1904    #[cfg_attr(
1905        docsrs,
1906        doc(cfg(all(
1907            feature = "all",
1908            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1909        )))
1910    )]
1911    pub fn thin_linear_timeouts(&self) -> io::Result<bool> {
1912        unsafe {
1913            getsockopt::<Bool>(
1914                self.as_raw(),
1915                libc::IPPROTO_TCP,
1916                libc::TCP_THIN_LINEAR_TIMEOUTS,
1917            )
1918            .map(|timeouts| timeouts != 0)
1919        }
1920    }
1921
1922    /// Set the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket.
1923    ///
1924    /// If set, the kernel will dynamically detect a thin-stream connection if there are less than four packets in flight.
1925    /// With less than four packets in flight the normal TCP fast retransmission will not be effective.
1926    /// The kernel will modify the retransmission to avoid the very high latencies that thin stream suffer because of exponential backoff.
1927    #[cfg(all(
1928        feature = "all",
1929        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1930    ))]
1931    #[cfg_attr(
1932        docsrs,
1933        doc(cfg(all(
1934            feature = "all",
1935            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1936        )))
1937    )]
1938    pub fn set_thin_linear_timeouts(&self, timeouts: bool) -> io::Result<()> {
1939        unsafe {
1940            setsockopt(
1941                self.as_raw(),
1942                libc::IPPROTO_TCP,
1943                libc::TCP_THIN_LINEAR_TIMEOUTS,
1944                timeouts as c_int,
1945            )
1946        }
1947    }
1948
1949    /// Gets the value for the `SO_BINDTODEVICE` option on this socket.
1950    ///
1951    /// This value gets the socket binded device's interface name.
1952    #[cfg(all(
1953        feature = "all",
1954        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1955    ))]
1956    #[cfg_attr(
1957        docsrs,
1958        doc(cfg(all(
1959            feature = "all",
1960            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1961        )))
1962    )]
1963    pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
1964        // TODO: replace with `MaybeUninit::uninit_array` once stable.
1965        let mut buf: [MaybeUninit<u8>; libc::IFNAMSIZ] =
1966            unsafe { MaybeUninit::uninit().assume_init() };
1967        let mut len = buf.len() as libc::socklen_t;
1968        syscall!(getsockopt(
1969            self.as_raw(),
1970            libc::SOL_SOCKET,
1971            libc::SO_BINDTODEVICE,
1972            buf.as_mut_ptr().cast(),
1973            &mut len,
1974        ))?;
1975        if len == 0 {
1976            Ok(None)
1977        } else {
1978            let buf = &buf[..len as usize - 1];
1979            // TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
1980            Ok(Some(unsafe { &*(buf as *const [_] as *const [u8]) }.into()))
1981        }
1982    }
1983
1984    /// Sets the value for the `SO_BINDTODEVICE` option on this socket.
1985    ///
1986    /// If a socket is bound to an interface, only packets received from that
1987    /// particular interface are processed by the socket. Note that this only
1988    /// works for some socket types, particularly `AF_INET` sockets.
1989    ///
1990    /// If `interface` is `None` or an empty string it removes the binding.
1991    #[cfg(all(
1992        feature = "all",
1993        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1994    ))]
1995    #[cfg_attr(
1996        docsrs,
1997        doc(cfg(all(
1998            feature = "all",
1999            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2000        )))
2001    )]
2002    pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
2003        let (value, len) = if let Some(interface) = interface {
2004            (interface.as_ptr(), interface.len())
2005        } else {
2006            (ptr::null(), 0)
2007        };
2008        syscall!(setsockopt(
2009            self.as_raw(),
2010            libc::SOL_SOCKET,
2011            libc::SO_BINDTODEVICE,
2012            value.cast(),
2013            len as libc::socklen_t,
2014        ))
2015        .map(|_| ())
2016    }
2017
2018    /// Sets the value for the `SO_SETFIB` option on this socket.
2019    ///
2020    /// Bind socket to the specified forwarding table (VRF) on a FreeBSD.
2021    #[cfg(all(feature = "all", target_os = "freebsd"))]
2022    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "freebsd"))))]
2023    pub fn set_fib(&self, fib: u32) -> io::Result<()> {
2024        syscall!(setsockopt(
2025            self.as_raw(),
2026            libc::SOL_SOCKET,
2027            libc::SO_SETFIB,
2028            (&fib as *const u32).cast(),
2029            mem::size_of::<u32>() as libc::socklen_t,
2030        ))
2031        .map(|_| ())
2032    }
2033
2034    /// This method is deprecated, use [`crate::Socket::bind_device_by_index_v4`].
2035    #[cfg(all(
2036        feature = "all",
2037        any(
2038            target_os = "ios",
2039            target_os = "visionos",
2040            target_os = "macos",
2041            target_os = "tvos",
2042            target_os = "watchos",
2043        )
2044    ))]
2045    #[cfg_attr(
2046        docsrs,
2047        doc(cfg(all(
2048            feature = "all",
2049            any(
2050                target_os = "ios",
2051                target_os = "visionos",
2052                target_os = "macos",
2053                target_os = "tvos",
2054                target_os = "watchos",
2055            )
2056        )))
2057    )]
2058    #[deprecated = "Use `Socket::bind_device_by_index_v4` instead"]
2059    pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
2060        self.bind_device_by_index_v4(interface)
2061    }
2062
2063    /// Sets the value for `IP_BOUND_IF` option on this socket.
2064    ///
2065    /// If a socket is bound to an interface, only packets received from that
2066    /// particular interface are processed by the socket.
2067    ///
2068    /// If `interface` is `None`, the binding is removed. If the `interface`
2069    /// index is not valid, an error is returned.
2070    ///
2071    /// One can use [`libc::if_nametoindex`] to convert an interface alias to an
2072    /// index.
2073    #[cfg(all(
2074        feature = "all",
2075        any(
2076            target_os = "ios",
2077            target_os = "visionos",
2078            target_os = "macos",
2079            target_os = "tvos",
2080            target_os = "watchos",
2081            target_os = "illumos",
2082            target_os = "solaris",
2083        )
2084    ))]
2085    #[cfg_attr(
2086        docsrs,
2087        doc(cfg(all(
2088            feature = "all",
2089            any(
2090                target_os = "ios",
2091                target_os = "visionos",
2092                target_os = "macos",
2093                target_os = "tvos",
2094                target_os = "watchos",
2095            )
2096        )))
2097    )]
2098    pub fn bind_device_by_index_v4(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
2099        let index = interface.map_or(0, NonZeroU32::get);
2100        unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) }
2101    }
2102
2103    /// Sets the value for `IPV6_BOUND_IF` option on this socket.
2104    ///
2105    /// If a socket is bound to an interface, only packets received from that
2106    /// particular interface are processed by the socket.
2107    ///
2108    /// If `interface` is `None`, the binding is removed. If the `interface`
2109    /// index is not valid, an error is returned.
2110    ///
2111    /// One can use [`libc::if_nametoindex`] to convert an interface alias to an
2112    /// index.
2113    #[cfg(all(
2114        feature = "all",
2115        any(
2116            target_os = "ios",
2117            target_os = "visionos",
2118            target_os = "macos",
2119            target_os = "tvos",
2120            target_os = "watchos",
2121            target_os = "illumos",
2122            target_os = "solaris",
2123        )
2124    ))]
2125    #[cfg_attr(
2126        docsrs,
2127        doc(cfg(all(
2128            feature = "all",
2129            any(
2130                target_os = "ios",
2131                target_os = "visionos",
2132                target_os = "macos",
2133                target_os = "tvos",
2134                target_os = "watchos",
2135            )
2136        )))
2137    )]
2138    pub fn bind_device_by_index_v6(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
2139        let index = interface.map_or(0, NonZeroU32::get);
2140        unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF, index) }
2141    }
2142
2143    /// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index
2144    /// for the interface to which the socket is bound.
2145    ///
2146    /// Returns `None` if the socket is not bound to any interface, otherwise
2147    /// returns an interface index.
2148    #[cfg(all(
2149        feature = "all",
2150        any(
2151            target_os = "ios",
2152            target_os = "visionos",
2153            target_os = "macos",
2154            target_os = "tvos",
2155            target_os = "watchos",
2156            target_os = "illumos",
2157            target_os = "solaris",
2158        )
2159    ))]
2160    #[cfg_attr(
2161        docsrs,
2162        doc(cfg(all(
2163            feature = "all",
2164            any(
2165                target_os = "ios",
2166                target_os = "visionos",
2167                target_os = "macos",
2168                target_os = "tvos",
2169                target_os = "watchos",
2170            )
2171        )))
2172    )]
2173    pub fn device_index_v4(&self) -> io::Result<Option<NonZeroU32>> {
2174        let index =
2175            unsafe { getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF)? };
2176        Ok(NonZeroU32::new(index))
2177    }
2178
2179    /// This method is deprecated, use [`crate::Socket::device_index_v4`].
2180    #[cfg(all(
2181        feature = "all",
2182        any(
2183            target_os = "ios",
2184            target_os = "visionos",
2185            target_os = "macos",
2186            target_os = "tvos",
2187            target_os = "watchos",
2188        )
2189    ))]
2190    #[cfg_attr(
2191        docsrs,
2192        doc(cfg(all(
2193            feature = "all",
2194            any(
2195                target_os = "ios",
2196                target_os = "visionos",
2197                target_os = "macos",
2198                target_os = "tvos",
2199                target_os = "watchos",
2200            )
2201        )))
2202    )]
2203    #[deprecated = "Use `Socket::device_index_v4` instead"]
2204    pub fn device_index(&self) -> io::Result<Option<NonZeroU32>> {
2205        self.device_index_v4()
2206    }
2207
2208    /// Gets the value for `IPV6_BOUND_IF` option on this socket, i.e. the index
2209    /// for the interface to which the socket is bound.
2210    ///
2211    /// Returns `None` if the socket is not bound to any interface, otherwise
2212    /// returns an interface index.
2213    #[cfg(all(
2214        feature = "all",
2215        any(
2216            target_os = "ios",
2217            target_os = "visionos",
2218            target_os = "macos",
2219            target_os = "tvos",
2220            target_os = "watchos",
2221            target_os = "illumos",
2222            target_os = "solaris",
2223        )
2224    ))]
2225    #[cfg_attr(
2226        docsrs,
2227        doc(cfg(all(
2228            feature = "all",
2229            any(
2230                target_os = "ios",
2231                target_os = "visionos",
2232                target_os = "macos",
2233                target_os = "tvos",
2234                target_os = "watchos",
2235            )
2236        )))
2237    )]
2238    pub fn device_index_v6(&self) -> io::Result<Option<NonZeroU32>> {
2239        let index = unsafe {
2240            getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF)?
2241        };
2242        Ok(NonZeroU32::new(index))
2243    }
2244
2245    /// Get the value of the `SO_INCOMING_CPU` option on this socket.
2246    ///
2247    /// For more information about this option, see [`set_cpu_affinity`].
2248    ///
2249    /// [`set_cpu_affinity`]: crate::Socket::set_cpu_affinity
2250    #[cfg(all(feature = "all", target_os = "linux"))]
2251    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2252    pub fn cpu_affinity(&self) -> io::Result<usize> {
2253        unsafe {
2254            getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_INCOMING_CPU)
2255                .map(|cpu| cpu as usize)
2256        }
2257    }
2258
2259    /// Set value for the `SO_INCOMING_CPU` option on this socket.
2260    ///
2261    /// Sets the CPU affinity of the socket.
2262    #[cfg(all(feature = "all", target_os = "linux"))]
2263    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2264    pub fn set_cpu_affinity(&self, cpu: usize) -> io::Result<()> {
2265        unsafe {
2266            setsockopt(
2267                self.as_raw(),
2268                libc::SOL_SOCKET,
2269                libc::SO_INCOMING_CPU,
2270                cpu as c_int,
2271            )
2272        }
2273    }
2274
2275    /// Get the value of the `SO_REUSEPORT` option on this socket.
2276    ///
2277    /// For more information about this option, see [`set_reuse_port`].
2278    ///
2279    /// [`set_reuse_port`]: crate::Socket::set_reuse_port
2280    #[cfg(all(
2281        feature = "all",
2282        not(any(target_os = "solaris", target_os = "illumos"))
2283    ))]
2284    #[cfg_attr(
2285        docsrs,
2286        doc(cfg(all(
2287            feature = "all",
2288            unix,
2289            not(any(target_os = "solaris", target_os = "illumos"))
2290        )))
2291    )]
2292    pub fn reuse_port(&self) -> io::Result<bool> {
2293        unsafe {
2294            getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT)
2295                .map(|reuse| reuse != 0)
2296        }
2297    }
2298
2299    /// Set value for the `SO_REUSEPORT` option on this socket.
2300    ///
2301    /// This indicates that further calls to `bind` may allow reuse of local
2302    /// addresses. For IPv4 sockets this means that a socket may bind even when
2303    /// there's a socket already listening on this port.
2304    #[cfg(all(
2305        feature = "all",
2306        not(any(target_os = "solaris", target_os = "illumos"))
2307    ))]
2308    #[cfg_attr(
2309        docsrs,
2310        doc(cfg(all(
2311            feature = "all",
2312            unix,
2313            not(any(target_os = "solaris", target_os = "illumos"))
2314        )))
2315    )]
2316    pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> {
2317        unsafe {
2318            setsockopt(
2319                self.as_raw(),
2320                libc::SOL_SOCKET,
2321                libc::SO_REUSEPORT,
2322                reuse as c_int,
2323            )
2324        }
2325    }
2326
2327    /// Get the value of the `SO_REUSEPORT_LB` option on this socket.
2328    ///
2329    /// For more information about this option, see [`set_reuse_port_lb`].
2330    ///
2331    /// [`set_reuse_port_lb`]: crate::Socket::set_reuse_port_lb
2332    #[cfg(all(feature = "all", target_os = "freebsd"))]
2333    pub fn reuse_port_lb(&self) -> io::Result<bool> {
2334        unsafe {
2335            getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT_LB)
2336                .map(|reuse| reuse != 0)
2337        }
2338    }
2339
2340    /// Set value for the `SO_REUSEPORT_LB` option on this socket.
2341    ///
2342    /// This allows multiple programs or threads to bind to the same port and
2343    /// incoming connections will be load balanced using a hash function.
2344    #[cfg(all(feature = "all", target_os = "freebsd"))]
2345    pub fn set_reuse_port_lb(&self, reuse: bool) -> io::Result<()> {
2346        unsafe {
2347            setsockopt(
2348                self.as_raw(),
2349                libc::SOL_SOCKET,
2350                libc::SO_REUSEPORT_LB,
2351                reuse as c_int,
2352            )
2353        }
2354    }
2355
2356    /// Get the value of the `IP_FREEBIND` option on this socket.
2357    ///
2358    /// For more information about this option, see [`set_freebind`].
2359    ///
2360    /// [`set_freebind`]: crate::Socket::set_freebind
2361    #[cfg(all(
2362        feature = "all",
2363        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2364    ))]
2365    #[cfg_attr(
2366        docsrs,
2367        doc(cfg(all(
2368            feature = "all",
2369            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2370        )))
2371    )]
2372    pub fn freebind(&self) -> io::Result<bool> {
2373        unsafe {
2374            getsockopt::<c_int>(self.as_raw(), libc::SOL_IP, libc::IP_FREEBIND)
2375                .map(|freebind| freebind != 0)
2376        }
2377    }
2378
2379    /// Set value for the `IP_FREEBIND` option on this socket.
2380    ///
2381    /// If enabled, this boolean option allows binding to an IP address that is
2382    /// nonlocal or does not (yet) exist.  This permits listening on a socket,
2383    /// without requiring the underlying network interface or the specified
2384    /// dynamic IP address to be up at the time that the application is trying
2385    /// to bind to it.
2386    #[cfg(all(
2387        feature = "all",
2388        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2389    ))]
2390    #[cfg_attr(
2391        docsrs,
2392        doc(cfg(all(
2393            feature = "all",
2394            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2395        )))
2396    )]
2397    pub fn set_freebind(&self, freebind: bool) -> io::Result<()> {
2398        unsafe {
2399            setsockopt(
2400                self.as_raw(),
2401                libc::SOL_IP,
2402                libc::IP_FREEBIND,
2403                freebind as c_int,
2404            )
2405        }
2406    }
2407
2408    /// Get the value of the `IPV6_FREEBIND` option on this socket.
2409    ///
2410    /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
2411    /// Android/Linux. For more information about this option, see
2412    /// [`set_freebind`].
2413    ///
2414    /// [`set_freebind`]: crate::Socket::set_freebind
2415    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
2416    #[cfg_attr(
2417        docsrs,
2418        doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
2419    )]
2420    pub fn freebind_ipv6(&self) -> io::Result<bool> {
2421        unsafe {
2422            getsockopt::<c_int>(self.as_raw(), libc::SOL_IPV6, libc::IPV6_FREEBIND)
2423                .map(|freebind| freebind != 0)
2424        }
2425    }
2426
2427    /// Set value for the `IPV6_FREEBIND` option on this socket.
2428    ///
2429    /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
2430    /// Android/Linux. For more information about this option, see
2431    /// [`set_freebind`].
2432    ///
2433    /// [`set_freebind`]: crate::Socket::set_freebind
2434    ///
2435    /// # Examples
2436    ///
2437    /// On Linux:
2438    ///
2439    /// ```
2440    /// use socket2::{Domain, Socket, Type};
2441    /// use std::io::{self, Error, ErrorKind};
2442    ///
2443    /// fn enable_freebind(socket: &Socket) -> io::Result<()> {
2444    ///     match socket.domain()? {
2445    ///         Domain::IPV4 => socket.set_freebind(true)?,
2446    ///         Domain::IPV6 => socket.set_freebind_ipv6(true)?,
2447    ///         _ => return Err(Error::new(ErrorKind::Other, "unsupported domain")),
2448    ///     };
2449    ///     Ok(())
2450    /// }
2451    ///
2452    /// # fn main() -> io::Result<()> {
2453    /// #     let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
2454    /// #     enable_freebind(&socket)
2455    /// # }
2456    /// ```
2457    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
2458    #[cfg_attr(
2459        docsrs,
2460        doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
2461    )]
2462    pub fn set_freebind_ipv6(&self, freebind: bool) -> io::Result<()> {
2463        unsafe {
2464            setsockopt(
2465                self.as_raw(),
2466                libc::SOL_IPV6,
2467                libc::IPV6_FREEBIND,
2468                freebind as c_int,
2469            )
2470        }
2471    }
2472
2473    /// Copies data between a `file` and this socket using the `sendfile(2)`
2474    /// system call. Because this copying is done within the kernel,
2475    /// `sendfile()` is more efficient than the combination of `read(2)` and
2476    /// `write(2)`, which would require transferring data to and from user
2477    /// space.
2478    ///
2479    /// Different OSs support different kinds of `file`s, see the OS
2480    /// documentation for what kind of files are supported. Generally *regular*
2481    /// files are supported by all OSs.
2482    #[doc = man_links!(unix: sendfile(2))]
2483    ///
2484    /// The `offset` is the absolute offset into the `file` to use as starting
2485    /// point.
2486    ///
2487    /// Depending on the OS this function *may* change the offset of `file`. For
2488    /// the best results reset the offset of the file before using it again.
2489    ///
2490    /// The `length` determines how many bytes to send, where a length of `None`
2491    /// means it will try to send all bytes.
2492    #[cfg(all(
2493        feature = "all",
2494        any(
2495            target_os = "aix",
2496            target_os = "android",
2497            target_os = "freebsd",
2498            target_os = "ios",
2499            target_os = "visionos",
2500            target_os = "linux",
2501            target_os = "macos",
2502            target_os = "tvos",
2503            target_os = "watchos",
2504        )
2505    ))]
2506    #[cfg_attr(
2507        docsrs,
2508        doc(cfg(all(
2509            feature = "all",
2510            any(
2511                target_os = "aix",
2512                target_os = "android",
2513                target_os = "freebsd",
2514                target_os = "ios",
2515                target_os = "visionos",
2516                target_os = "linux",
2517                target_os = "macos",
2518                target_os = "tvos",
2519                target_os = "watchos",
2520            )
2521        )))
2522    )]
2523    pub fn sendfile<F>(
2524        &self,
2525        file: &F,
2526        offset: usize,
2527        length: Option<NonZeroUsize>,
2528    ) -> io::Result<usize>
2529    where
2530        F: AsRawFd,
2531    {
2532        self._sendfile(file.as_raw_fd(), offset as _, length)
2533    }
2534
2535    #[cfg(all(
2536        feature = "all",
2537        any(
2538            target_os = "ios",
2539            target_os = "visionos",
2540            target_os = "macos",
2541            target_os = "tvos",
2542            target_os = "watchos",
2543        )
2544    ))]
2545    fn _sendfile(
2546        &self,
2547        file: RawFd,
2548        offset: libc::off_t,
2549        length: Option<NonZeroUsize>,
2550    ) -> io::Result<usize> {
2551        // On macOS `length` is value-result parameter. It determines the number
2552        // of bytes to write and returns the number of bytes written.
2553        let mut length = match length {
2554            Some(n) => n.get() as libc::off_t,
2555            // A value of `0` means send all bytes.
2556            None => 0,
2557        };
2558        syscall!(sendfile(
2559            file,
2560            self.as_raw(),
2561            offset,
2562            &mut length,
2563            ptr::null_mut(),
2564            0,
2565        ))
2566        .map(|_| length as usize)
2567    }
2568
2569    #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
2570    fn _sendfile(
2571        &self,
2572        file: RawFd,
2573        offset: libc::off_t,
2574        length: Option<NonZeroUsize>,
2575    ) -> io::Result<usize> {
2576        let count = match length {
2577            Some(n) => n.get() as libc::size_t,
2578            // The maximum the Linux kernel will write in a single call.
2579            None => 0x7ffff000, // 2,147,479,552 bytes.
2580        };
2581        let mut offset = offset;
2582        syscall!(sendfile(self.as_raw(), file, &mut offset, count)).map(|n| n as usize)
2583    }
2584
2585    #[cfg(all(feature = "all", target_os = "freebsd"))]
2586    fn _sendfile(
2587        &self,
2588        file: RawFd,
2589        offset: libc::off_t,
2590        length: Option<NonZeroUsize>,
2591    ) -> io::Result<usize> {
2592        let nbytes = match length {
2593            Some(n) => n.get() as libc::size_t,
2594            // A value of `0` means send all bytes.
2595            None => 0,
2596        };
2597        let mut sbytes: libc::off_t = 0;
2598        syscall!(sendfile(
2599            file,
2600            self.as_raw(),
2601            offset,
2602            nbytes,
2603            ptr::null_mut(),
2604            &mut sbytes,
2605            0,
2606        ))
2607        .map(|_| sbytes as usize)
2608    }
2609
2610    #[cfg(all(feature = "all", target_os = "aix"))]
2611    fn _sendfile(
2612        &self,
2613        file: RawFd,
2614        offset: libc::off_t,
2615        length: Option<NonZeroUsize>,
2616    ) -> io::Result<usize> {
2617        let nbytes = match length {
2618            Some(n) => n.get() as i64,
2619            None => -1,
2620        };
2621        let mut params = libc::sf_parms {
2622            header_data: ptr::null_mut(),
2623            header_length: 0,
2624            file_descriptor: file,
2625            file_size: 0,
2626            file_offset: offset as u64,
2627            file_bytes: nbytes,
2628            trailer_data: ptr::null_mut(),
2629            trailer_length: 0,
2630            bytes_sent: 0,
2631        };
2632        // AIX doesn't support SF_REUSE, socket will be closed after successful transmission.
2633        syscall!(send_file(
2634            &mut self.as_raw() as *mut _,
2635            &mut params as *mut _,
2636            libc::SF_CLOSE as libc::c_uint,
2637        ))
2638        .map(|_| params.bytes_sent as usize)
2639    }
2640
2641    /// Set the value of the `TCP_USER_TIMEOUT` option on this socket.
2642    ///
2643    /// If set, this specifies the maximum amount of time that transmitted data may remain
2644    /// unacknowledged or buffered data may remain untransmitted before TCP will forcibly close the
2645    /// corresponding connection.
2646    ///
2647    /// Setting `timeout` to `None` or a zero duration causes the system default timeouts to
2648    /// be used. If `timeout` in milliseconds is larger than `c_uint::MAX`, the timeout is clamped
2649    /// to `c_uint::MAX`. For example, when `c_uint` is a 32-bit value, this limits the timeout to
2650    /// approximately 49.71 days.
2651    #[cfg(all(
2652        feature = "all",
2653        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2654    ))]
2655    #[cfg_attr(
2656        docsrs,
2657        doc(cfg(all(
2658            feature = "all",
2659            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2660        )))
2661    )]
2662    pub fn set_tcp_user_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
2663        let timeout = timeout.map_or(0, |to| {
2664            min(to.as_millis(), libc::c_uint::MAX as u128) as libc::c_uint
2665        });
2666        unsafe {
2667            setsockopt(
2668                self.as_raw(),
2669                libc::IPPROTO_TCP,
2670                libc::TCP_USER_TIMEOUT,
2671                timeout,
2672            )
2673        }
2674    }
2675
2676    /// Get the value of the `TCP_USER_TIMEOUT` option on this socket.
2677    ///
2678    /// For more information about this option, see [`set_tcp_user_timeout`].
2679    ///
2680    /// [`set_tcp_user_timeout`]: crate::Socket::set_tcp_user_timeout
2681    #[cfg(all(
2682        feature = "all",
2683        any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2684    ))]
2685    #[cfg_attr(
2686        docsrs,
2687        doc(cfg(all(
2688            feature = "all",
2689            any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2690        )))
2691    )]
2692    pub fn tcp_user_timeout(&self) -> io::Result<Option<Duration>> {
2693        unsafe {
2694            getsockopt::<libc::c_uint>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT)
2695                .map(|millis| {
2696                    if millis == 0 {
2697                        None
2698                    } else {
2699                        Some(Duration::from_millis(millis as u64))
2700                    }
2701                })
2702        }
2703    }
2704
2705    /// Attach Berkeley Packet Filter(BPF) on this socket.
2706    ///
2707    /// BPF allows a user-space program to attach a filter onto any socket
2708    /// and allow or disallow certain types of data to come through the socket.
2709    ///
2710    /// For more information about this option, see [filter](https://www.kernel.org/doc/html/v5.12/networking/filter.html)
2711    #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
2712    pub fn attach_filter(&self, filters: &[libc::sock_filter]) -> io::Result<()> {
2713        let prog = libc::sock_fprog {
2714            len: filters.len() as u16,
2715            filter: filters.as_ptr() as *mut _,
2716        };
2717
2718        unsafe {
2719            setsockopt(
2720                self.as_raw(),
2721                libc::SOL_SOCKET,
2722                libc::SO_ATTACH_FILTER,
2723                prog,
2724            )
2725        }
2726    }
2727
2728    /// Detach Berkeley Packet Filter(BPF) from this socket.
2729    ///
2730    /// For more information about this option, see [`attach_filter`]
2731    ///
2732    /// [`attach_filter`]: crate::Socket::attach_filter
2733    #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
2734    pub fn detach_filter(&self) -> io::Result<()> {
2735        unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) }
2736    }
2737
2738    /// Gets the value for the `SO_COOKIE` option on this socket.
2739    ///
2740    /// The socket cookie is a unique, kernel-managed identifier tied to each socket.
2741    /// Therefore, there is no corresponding `set` helper.
2742    ///
2743    /// For more information about this option, see [Linux patch](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5daab9db7b65df87da26fd8cfa695fb9546a1ddb)
2744    #[cfg(all(feature = "all", target_os = "linux"))]
2745    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2746    pub fn cookie(&self) -> io::Result<u64> {
2747        unsafe { getsockopt::<libc::c_ulonglong>(self.as_raw(), libc::SOL_SOCKET, libc::SO_COOKIE) }
2748    }
2749
2750    /// Get the value of the `IPV6_TCLASS` option for this socket.
2751    ///
2752    /// For more information about this option, see [`set_tclass_v6`].
2753    ///
2754    /// [`set_tclass_v6`]: crate::Socket::set_tclass_v6
2755    #[cfg(all(
2756        feature = "all",
2757        any(
2758            target_os = "android",
2759            target_os = "dragonfly",
2760            target_os = "freebsd",
2761            target_os = "fuchsia",
2762            target_os = "linux",
2763            target_os = "macos",
2764            target_os = "netbsd",
2765            target_os = "openbsd"
2766        )
2767    ))]
2768    #[cfg_attr(
2769        docsrs,
2770        doc(cfg(all(
2771            feature = "all",
2772            any(
2773                target_os = "android",
2774                target_os = "dragonfly",
2775                target_os = "freebsd",
2776                target_os = "fuchsia",
2777                target_os = "linux",
2778                target_os = "macos",
2779                target_os = "netbsd",
2780                target_os = "openbsd"
2781            )
2782        )))
2783    )]
2784    pub fn tclass_v6(&self) -> io::Result<u32> {
2785        unsafe {
2786            getsockopt::<c_int>(self.as_raw(), IPPROTO_IPV6, libc::IPV6_TCLASS)
2787                .map(|tclass| tclass as u32)
2788        }
2789    }
2790
2791    /// Set the value of the `IPV6_TCLASS` option for this socket.
2792    ///
2793    /// Specifies the traffic class field that is used in every packets
2794    /// sent from this socket.
2795    #[cfg(all(
2796        feature = "all",
2797        any(
2798            target_os = "android",
2799            target_os = "dragonfly",
2800            target_os = "freebsd",
2801            target_os = "fuchsia",
2802            target_os = "linux",
2803            target_os = "macos",
2804            target_os = "netbsd",
2805            target_os = "openbsd"
2806        )
2807    ))]
2808    #[cfg_attr(
2809        docsrs,
2810        doc(cfg(all(
2811            feature = "all",
2812            any(
2813                target_os = "android",
2814                target_os = "dragonfly",
2815                target_os = "freebsd",
2816                target_os = "fuchsia",
2817                target_os = "linux",
2818                target_os = "macos",
2819                target_os = "netbsd",
2820                target_os = "openbsd"
2821            )
2822        )))
2823    )]
2824    pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> {
2825        unsafe {
2826            setsockopt(
2827                self.as_raw(),
2828                IPPROTO_IPV6,
2829                libc::IPV6_TCLASS,
2830                tclass as c_int,
2831            )
2832        }
2833    }
2834
2835    /// Get the value of the `TCP_CONGESTION` option for this socket.
2836    ///
2837    /// For more information about this option, see [`set_tcp_congestion`].
2838    ///
2839    /// [`set_tcp_congestion`]: crate::Socket::set_tcp_congestion
2840    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
2841    #[cfg_attr(
2842        docsrs,
2843        doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
2844    )]
2845    pub fn tcp_congestion(&self) -> io::Result<Vec<u8>> {
2846        let mut payload: [u8; TCP_CA_NAME_MAX] = [0; TCP_CA_NAME_MAX];
2847        let mut len = payload.len() as libc::socklen_t;
2848        syscall!(getsockopt(
2849            self.as_raw(),
2850            IPPROTO_TCP,
2851            libc::TCP_CONGESTION,
2852            payload.as_mut_ptr().cast(),
2853            &mut len,
2854        ))
2855        .map(|_| payload[..len as usize].to_vec())
2856    }
2857
2858    /// Set the value of the `TCP_CONGESTION` option for this socket.
2859    ///
2860    /// Specifies the TCP congestion control algorithm to use for this socket.
2861    ///
2862    /// The value must be a valid TCP congestion control algorithm name of the
2863    /// platform. For example, Linux may supports "reno", "cubic".
2864    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
2865    #[cfg_attr(
2866        docsrs,
2867        doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
2868    )]
2869    pub fn set_tcp_congestion(&self, tcp_ca_name: &[u8]) -> io::Result<()> {
2870        syscall!(setsockopt(
2871            self.as_raw(),
2872            IPPROTO_TCP,
2873            libc::TCP_CONGESTION,
2874            tcp_ca_name.as_ptr() as *const _,
2875            tcp_ca_name.len() as libc::socklen_t,
2876        ))
2877        .map(|_| ())
2878    }
2879
2880    /// Set value for the `DCCP_SOCKOPT_SERVICE` option on this socket.
2881    ///
2882    /// Sets the DCCP service. The specification mandates use of service codes.
2883    /// If this socket option is not set, the socket will fall back to 0 (which
2884    /// means that no meaningful service code is present). On active sockets
2885    /// this is set before [`connect`]. On passive sockets up to 32 service
2886    /// codes can be set before calling [`bind`]
2887    ///
2888    /// [`connect`]: crate::Socket::connect
2889    /// [`bind`]: crate::Socket::bind
2890    #[cfg(all(feature = "all", target_os = "linux"))]
2891    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2892    pub fn set_dccp_service(&self, code: u32) -> io::Result<()> {
2893        unsafe {
2894            setsockopt(
2895                self.as_raw(),
2896                libc::SOL_DCCP,
2897                libc::DCCP_SOCKOPT_SERVICE,
2898                code,
2899            )
2900        }
2901    }
2902
2903    /// Get the value of the `DCCP_SOCKOPT_SERVICE` option on this socket.
2904    ///
2905    /// For more information about this option see [`set_dccp_service`]
2906    ///
2907    /// [`set_dccp_service`]: crate::Socket::set_dccp_service
2908    #[cfg(all(feature = "all", target_os = "linux"))]
2909    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2910    pub fn dccp_service(&self) -> io::Result<u32> {
2911        unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SERVICE) }
2912    }
2913
2914    /// Set value for the `DCCP_SOCKOPT_CCID` option on this socket.
2915    ///
2916    /// This option sets both the TX and RX CCIDs at the same time.
2917    #[cfg(all(feature = "all", target_os = "linux"))]
2918    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2919    pub fn set_dccp_ccid(&self, ccid: u8) -> io::Result<()> {
2920        unsafe { setsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_CCID, ccid) }
2921    }
2922
2923    /// Get the value of the `DCCP_SOCKOPT_TX_CCID` option on this socket.
2924    ///
2925    /// For more information about this option see [`set_dccp_ccid`].
2926    ///
2927    /// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid
2928    #[cfg(all(feature = "all", target_os = "linux"))]
2929    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2930    pub fn dccp_tx_ccid(&self) -> io::Result<u32> {
2931        unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_TX_CCID) }
2932    }
2933
2934    /// Get the value of the `DCCP_SOCKOPT_RX_CCID` option on this socket.
2935    ///
2936    /// For more information about this option see [`set_dccp_ccid`].
2937    ///
2938    /// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid
2939    #[cfg(all(feature = "all", target_os = "linux"))]
2940    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2941    pub fn dccp_xx_ccid(&self) -> io::Result<u32> {
2942        unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RX_CCID) }
2943    }
2944
2945    /// Set value for the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
2946    ///
2947    /// Enables a listening socket to hold timewait state when closing the
2948    /// connection. This option must be set after `accept` returns.
2949    #[cfg(all(feature = "all", target_os = "linux"))]
2950    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2951    pub fn set_dccp_server_timewait(&self, hold_timewait: bool) -> io::Result<()> {
2952        unsafe {
2953            setsockopt(
2954                self.as_raw(),
2955                libc::SOL_DCCP,
2956                libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
2957                hold_timewait as c_int,
2958            )
2959        }
2960    }
2961
2962    /// Get the value of the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
2963    ///
2964    /// For more information see [`set_dccp_server_timewait`]
2965    ///
2966    /// [`set_dccp_server_timewait`]: crate::Socket::set_dccp_server_timewait
2967    #[cfg(all(feature = "all", target_os = "linux"))]
2968    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2969    pub fn dccp_server_timewait(&self) -> io::Result<bool> {
2970        unsafe {
2971            getsockopt(
2972                self.as_raw(),
2973                libc::SOL_DCCP,
2974                libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
2975            )
2976        }
2977    }
2978
2979    /// Set value for the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
2980    ///
2981    /// Both this option and `DCCP_SOCKOPT_RECV_CSCOV` are used for setting the
2982    /// partial checksum coverage. The default is that checksums always cover
2983    /// the entire packet and that only fully covered application data is
2984    /// accepted by the receiver. Hence, when using this feature on the sender,
2985    /// it must be enabled at the receiver too, with suitable choice of CsCov.
2986    #[cfg(all(feature = "all", target_os = "linux"))]
2987    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
2988    pub fn set_dccp_send_cscov(&self, level: u32) -> io::Result<()> {
2989        unsafe {
2990            setsockopt(
2991                self.as_raw(),
2992                libc::SOL_DCCP,
2993                libc::DCCP_SOCKOPT_SEND_CSCOV,
2994                level,
2995            )
2996        }
2997    }
2998
2999    /// Get the value of the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
3000    ///
3001    /// For more information on this option see [`set_dccp_send_cscov`].
3002    ///
3003    /// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
3004    #[cfg(all(feature = "all", target_os = "linux"))]
3005    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3006    pub fn dccp_send_cscov(&self) -> io::Result<u32> {
3007        unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SEND_CSCOV) }
3008    }
3009
3010    /// Set the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
3011    ///
3012    /// This option is only useful when combined with [`set_dccp_send_cscov`].
3013    ///
3014    /// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
3015    #[cfg(all(feature = "all", target_os = "linux"))]
3016    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3017    pub fn set_dccp_recv_cscov(&self, level: u32) -> io::Result<()> {
3018        unsafe {
3019            setsockopt(
3020                self.as_raw(),
3021                libc::SOL_DCCP,
3022                libc::DCCP_SOCKOPT_RECV_CSCOV,
3023                level,
3024            )
3025        }
3026    }
3027
3028    /// Get the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
3029    ///
3030    /// For more information on this option see [`set_dccp_recv_cscov`].
3031    ///
3032    /// [`set_dccp_recv_cscov`]: crate::Socket::set_dccp_recv_cscov
3033    #[cfg(all(feature = "all", target_os = "linux"))]
3034    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3035    pub fn dccp_recv_cscov(&self) -> io::Result<u32> {
3036        unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RECV_CSCOV) }
3037    }
3038
3039    /// Set value for the `DCCP_SOCKOPT_QPOLICY_TXQLEN` option on this socket.
3040    ///
3041    /// This option sets the maximum length of the output queue. A zero value is
3042    /// interpreted as unbounded queue length.
3043    #[cfg(all(feature = "all", target_os = "linux"))]
3044    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3045    pub fn set_dccp_qpolicy_txqlen(&self, length: u32) -> io::Result<()> {
3046        unsafe {
3047            setsockopt(
3048                self.as_raw(),
3049                libc::SOL_DCCP,
3050                libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
3051                length,
3052            )
3053        }
3054    }
3055
3056    /// Get the value of the `DCCP_SOCKOPT_QPOLICY_TXQLEN` on this socket.
3057    ///
3058    /// For more information on this option see [`set_dccp_qpolicy_txqlen`].
3059    ///
3060    /// [`set_dccp_qpolicy_txqlen`]: crate::Socket::set_dccp_qpolicy_txqlen
3061    #[cfg(all(feature = "all", target_os = "linux"))]
3062    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3063    pub fn dccp_qpolicy_txqlen(&self) -> io::Result<u32> {
3064        unsafe {
3065            getsockopt(
3066                self.as_raw(),
3067                libc::SOL_DCCP,
3068                libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
3069            )
3070        }
3071    }
3072
3073    /// Get the value of the `DCCP_SOCKOPT_AVAILABLE_CCIDS` option on this socket.
3074    ///
3075    /// Returns the list of CCIDs supported by the endpoint.
3076    ///
3077    /// The parameter `N` is used to get the maximum number of supported
3078    /// endpoints. The [documentation] recommends a minimum of four at the time
3079    /// of writing.
3080    ///
3081    /// [documentation]: https://www.kernel.org/doc/html/latest/networking/dccp.html
3082    #[cfg(all(feature = "all", target_os = "linux"))]
3083    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3084    pub fn dccp_available_ccids<const N: usize>(&self) -> io::Result<CcidEndpoints<N>> {
3085        let mut endpoints = [0; N];
3086        let mut length = endpoints.len() as libc::socklen_t;
3087        syscall!(getsockopt(
3088            self.as_raw(),
3089            libc::SOL_DCCP,
3090            libc::DCCP_SOCKOPT_AVAILABLE_CCIDS,
3091            endpoints.as_mut_ptr().cast(),
3092            &mut length,
3093        ))?;
3094        Ok(CcidEndpoints { endpoints, length })
3095    }
3096
3097    /// Get the value of the `DCCP_SOCKOPT_GET_CUR_MPS` option on this socket.
3098    ///
3099    /// This option retrieves the current maximum packet size (application
3100    /// payload size) in bytes.
3101    #[cfg(all(feature = "all", target_os = "linux"))]
3102    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3103    pub fn dccp_cur_mps(&self) -> io::Result<u32> {
3104        unsafe {
3105            getsockopt(
3106                self.as_raw(),
3107                libc::SOL_DCCP,
3108                libc::DCCP_SOCKOPT_GET_CUR_MPS,
3109            )
3110        }
3111    }
3112}
3113
3114/// See [`Socket::dccp_available_ccids`].
3115#[cfg(all(feature = "all", target_os = "linux"))]
3116#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3117#[derive(Debug)]
3118pub struct CcidEndpoints<const N: usize> {
3119    endpoints: [u8; N],
3120    length: u32,
3121}
3122
3123#[cfg(all(feature = "all", target_os = "linux"))]
3124#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
3125impl<const N: usize> std::ops::Deref for CcidEndpoints<N> {
3126    type Target = [u8];
3127
3128    fn deref(&self) -> &[u8] {
3129        &self.endpoints[0..self.length as usize]
3130    }
3131}
3132
3133#[cfg_attr(docsrs, doc(cfg(unix)))]
3134impl AsFd for crate::Socket {
3135    fn as_fd(&self) -> BorrowedFd<'_> {
3136        // SAFETY: lifetime is bound by self.
3137        unsafe { BorrowedFd::borrow_raw(self.as_raw()) }
3138    }
3139}
3140
3141#[cfg_attr(docsrs, doc(cfg(unix)))]
3142impl AsRawFd for crate::Socket {
3143    fn as_raw_fd(&self) -> c_int {
3144        self.as_raw()
3145    }
3146}
3147
3148#[cfg_attr(docsrs, doc(cfg(unix)))]
3149impl From<crate::Socket> for OwnedFd {
3150    fn from(sock: crate::Socket) -> OwnedFd {
3151        // SAFETY: sock.into_raw() always returns a valid fd.
3152        unsafe { OwnedFd::from_raw_fd(sock.into_raw()) }
3153    }
3154}
3155
3156#[cfg_attr(docsrs, doc(cfg(unix)))]
3157impl IntoRawFd for crate::Socket {
3158    fn into_raw_fd(self) -> c_int {
3159        self.into_raw()
3160    }
3161}
3162
3163#[cfg_attr(docsrs, doc(cfg(unix)))]
3164impl From<OwnedFd> for crate::Socket {
3165    fn from(fd: OwnedFd) -> crate::Socket {
3166        // SAFETY: `OwnedFd` ensures the fd is valid.
3167        unsafe { crate::Socket::from_raw_fd(fd.into_raw_fd()) }
3168    }
3169}
3170
3171#[cfg_attr(docsrs, doc(cfg(unix)))]
3172impl FromRawFd for crate::Socket {
3173    unsafe fn from_raw_fd(fd: c_int) -> crate::Socket {
3174        crate::Socket::from_raw(fd)
3175    }
3176}
3177
3178#[cfg(feature = "all")]
3179from!(UnixStream, crate::Socket);
3180#[cfg(feature = "all")]
3181from!(UnixListener, crate::Socket);
3182#[cfg(feature = "all")]
3183from!(UnixDatagram, crate::Socket);
3184#[cfg(feature = "all")]
3185from!(crate::Socket, UnixStream);
3186#[cfg(feature = "all")]
3187from!(crate::Socket, UnixListener);
3188#[cfg(feature = "all")]
3189from!(crate::Socket, UnixDatagram);
3190
3191#[test]
3192fn in_addr_convertion() {
3193    let ip = Ipv4Addr::new(127, 0, 0, 1);
3194    let raw = to_in_addr(&ip);
3195    // NOTE: `in_addr` is packed on NetBSD and it's unsafe to borrow.
3196    let a = raw.s_addr;
3197    assert_eq!(a, u32::from_ne_bytes([127, 0, 0, 1]));
3198    assert_eq!(from_in_addr(raw), ip);
3199
3200    let ip = Ipv4Addr::new(127, 34, 4, 12);
3201    let raw = to_in_addr(&ip);
3202    let a = raw.s_addr;
3203    assert_eq!(a, u32::from_ne_bytes([127, 34, 4, 12]));
3204    assert_eq!(from_in_addr(raw), ip);
3205}
3206
3207#[test]
3208fn in6_addr_convertion() {
3209    let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7);
3210    let raw = to_in6_addr(&ip);
3211    let want = [32, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7];
3212    assert_eq!(raw.s6_addr, want);
3213    assert_eq!(from_in6_addr(raw), ip);
3214}