1//! libc syscalls supporting `rustix::net`.
2
3use super::super::c;
4use super::super::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
5#[cfg(unix)]
6use super::addr::SocketAddrUnix;
7use super::ext::{in6_addr_new, in_addr_new};
8#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
9use super::read_sockaddr::initialize_family_to_unspec;
10#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
11use super::read_sockaddr::{maybe_read_sockaddr_os, read_sockaddr_os};
12#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
13use super::send_recv::{RecvFlags, SendFlags};
14#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
15use super::types::{AcceptFlags, AddressFamily, Protocol, Shutdown, SocketFlags, SocketType};
16#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
17use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
18use crate::fd::{BorrowedFd, OwnedFd};
19use crate::io;
20use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
21use crate::utils::as_ptr;
22use core::convert::TryInto;
23use core::mem::{size_of, MaybeUninit};
24#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
25use core::ptr::null_mut;
26
27#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
28pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result<usize> {
29    let nrecv = unsafe {
30        ret_send_recv(c::recv(
31            borrowed_fd(fd),
32            buf.as_mut_ptr().cast(),
33            send_recv_len(buf.len()),
34            flags.bits(),
35        ))?
36    };
37    Ok(nrecv as usize)
38}
39
40#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
41pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize> {
42    let nwritten = unsafe {
43        ret_send_recv(c::send(
44            borrowed_fd(fd),
45            buf.as_ptr().cast(),
46            send_recv_len(buf.len()),
47            flags.bits(),
48        ))?
49    };
50    Ok(nwritten as usize)
51}
52
53#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
54pub(crate) fn recvfrom(
55    fd: BorrowedFd<'_>,
56    buf: &mut [u8],
57    flags: RecvFlags,
58) -> io::Result<(usize, Option<SocketAddrAny>)> {
59    unsafe {
60        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
61        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
62
63        // `recvfrom` does not write to the storage if the socket is
64        // connection-oriented sockets, so we initialize the family field to
65        // `AF_UNSPEC` so that we can detect this case.
66        initialize_family_to_unspec(storage.as_mut_ptr());
67
68        let nread = ret_send_recv(c::recvfrom(
69            borrowed_fd(fd),
70            buf.as_mut_ptr().cast(),
71            send_recv_len(buf.len()),
72            flags.bits(),
73            storage.as_mut_ptr().cast(),
74            &mut len,
75        ))?;
76        Ok((
77            nread as usize,
78            maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
79        ))
80    }
81}
82
83#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
84pub(crate) fn sendto_v4(
85    fd: BorrowedFd<'_>,
86    buf: &[u8],
87    flags: SendFlags,
88    addr: &SocketAddrV4,
89) -> io::Result<usize> {
90    let nwritten = unsafe {
91        ret_send_recv(c::sendto(
92            borrowed_fd(fd),
93            buf.as_ptr().cast(),
94            send_recv_len(buf.len()),
95            flags.bits(),
96            as_ptr(&encode_sockaddr_v4(addr)).cast::<c::sockaddr>(),
97            size_of::<SocketAddrV4>() as _,
98        ))?
99    };
100    Ok(nwritten as usize)
101}
102
103#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
104pub(crate) fn sendto_v6(
105    fd: BorrowedFd<'_>,
106    buf: &[u8],
107    flags: SendFlags,
108    addr: &SocketAddrV6,
109) -> io::Result<usize> {
110    let nwritten = unsafe {
111        ret_send_recv(c::sendto(
112            borrowed_fd(fd),
113            buf.as_ptr().cast(),
114            send_recv_len(buf.len()),
115            flags.bits(),
116            as_ptr(&encode_sockaddr_v6(addr)).cast::<c::sockaddr>(),
117            size_of::<SocketAddrV6>() as _,
118        ))?
119    };
120    Ok(nwritten as usize)
121}
122
123#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
124pub(crate) fn sendto_unix(
125    fd: BorrowedFd<'_>,
126    buf: &[u8],
127    flags: SendFlags,
128    addr: &SocketAddrUnix,
129) -> io::Result<usize> {
130    let nwritten = unsafe {
131        ret_send_recv(c::sendto(
132            borrowed_fd(fd),
133            buf.as_ptr().cast(),
134            send_recv_len(buf.len()),
135            flags.bits(),
136            as_ptr(&addr.unix).cast(),
137            addr.addr_len(),
138        ))?
139    };
140    Ok(nwritten as usize)
141}
142
143#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
144pub(crate) fn socket(
145    domain: AddressFamily,
146    type_: SocketType,
147    protocol: Protocol,
148) -> io::Result<OwnedFd> {
149    unsafe {
150        ret_owned_fd(c::socket(
151            domain.0 as c::c_int,
152            type_.0 as c::c_int,
153            protocol.0,
154        ))
155    }
156}
157
158#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
159pub(crate) fn socket_with(
160    domain: AddressFamily,
161    type_: SocketType,
162    flags: SocketFlags,
163    protocol: Protocol,
164) -> io::Result<OwnedFd> {
165    unsafe {
166        ret_owned_fd(c::socket(
167            domain.0 as c::c_int,
168            type_.0 as c::c_int | flags.bits(),
169            protocol.0,
170        ))
171    }
172}
173
174#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
175pub(crate) fn bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
176    unsafe {
177        ret(c::bind(
178            borrowed_fd(sockfd),
179            as_ptr(&encode_sockaddr_v4(addr)).cast(),
180            size_of::<c::sockaddr_in>() as c::socklen_t,
181        ))
182    }
183}
184
185#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
186pub(crate) fn bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
187    unsafe {
188        ret(c::bind(
189            borrowed_fd(sockfd),
190            as_ptr(&encode_sockaddr_v6(addr)).cast(),
191            size_of::<c::sockaddr_in6>() as c::socklen_t,
192        ))
193    }
194}
195
196#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
197pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
198    unsafe {
199        ret(c::bind(
200            borrowed_fd(sockfd),
201            as_ptr(&addr.unix).cast(),
202            addr.addr_len(),
203        ))
204    }
205}
206
207#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
208pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
209    unsafe {
210        ret(c::connect(
211            borrowed_fd(sockfd),
212            as_ptr(&encode_sockaddr_v4(addr)).cast(),
213            size_of::<c::sockaddr_in>() as c::socklen_t,
214        ))
215    }
216}
217
218#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
219pub(crate) fn connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
220    unsafe {
221        ret(c::connect(
222            borrowed_fd(sockfd),
223            as_ptr(&encode_sockaddr_v6(addr)).cast(),
224            size_of::<c::sockaddr_in6>() as c::socklen_t,
225        ))
226    }
227}
228
229#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
230pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
231    unsafe {
232        ret(c::connect(
233            borrowed_fd(sockfd),
234            as_ptr(&addr.unix).cast(),
235            addr.addr_len(),
236        ))
237    }
238}
239
240#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
241pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
242    unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) }
243}
244
245#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
246pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
247    unsafe {
248        let owned_fd = ret_owned_fd(c::accept(borrowed_fd(sockfd), null_mut(), null_mut()))?;
249        Ok(owned_fd)
250    }
251}
252
253#[cfg(not(any(
254    windows,
255    target_os = "haiku",
256    target_os = "ios",
257    target_os = "macos",
258    target_os = "redox",
259    target_os = "wasi",
260)))]
261pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, flags: AcceptFlags) -> io::Result<OwnedFd> {
262    unsafe {
263        let owned_fd = ret_owned_fd(c::accept4(
264            borrowed_fd(sockfd),
265            null_mut(),
266            null_mut(),
267            flags.bits(),
268        ))?;
269        Ok(owned_fd)
270    }
271}
272
273#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
274pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
275    unsafe {
276        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
277        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
278        let owned_fd = ret_owned_fd(c::accept(
279            borrowed_fd(sockfd),
280            storage.as_mut_ptr().cast(),
281            &mut len,
282        ))?;
283        Ok((
284            owned_fd,
285            maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
286        ))
287    }
288}
289
290#[cfg(not(any(
291    windows,
292    target_os = "haiku",
293    target_os = "ios",
294    target_os = "macos",
295    target_os = "redox",
296    target_os = "wasi",
297)))]
298pub(crate) fn acceptfrom_with(
299    sockfd: BorrowedFd<'_>,
300    flags: AcceptFlags,
301) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
302    unsafe {
303        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
304        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
305        let owned_fd = ret_owned_fd(c::accept4(
306            borrowed_fd(sockfd),
307            storage.as_mut_ptr().cast(),
308            &mut len,
309            flags.bits(),
310        ))?;
311        Ok((
312            owned_fd,
313            maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
314        ))
315    }
316}
317
318/// Darwin lacks `accept4`, but does have `accept`. We define
319/// `AcceptFlags` to have no flags, so we can discard it here.
320#[cfg(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos"))]
321pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: AcceptFlags) -> io::Result<OwnedFd> {
322    accept(sockfd)
323}
324
325/// Darwin lacks `accept4`, but does have `accept`. We define
326/// `AcceptFlags` to have no flags, so we can discard it here.
327#[cfg(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos"))]
328pub(crate) fn acceptfrom_with(
329    sockfd: BorrowedFd<'_>,
330    _flags: AcceptFlags,
331) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
332    acceptfrom(sockfd)
333}
334
335#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
336pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> {
337    unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) }
338}
339
340#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
341pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> {
342    unsafe {
343        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
344        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
345        ret(c::getsockname(
346            borrowed_fd(sockfd),
347            storage.as_mut_ptr().cast(),
348            &mut len,
349        ))?;
350        Ok(read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()))
351    }
352}
353
354#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
355pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> {
356    unsafe {
357        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
358        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
359        ret(c::getpeername(
360            borrowed_fd(sockfd),
361            storage.as_mut_ptr().cast(),
362            &mut len,
363        ))?;
364        Ok(maybe_read_sockaddr_os(
365            storage.as_ptr(),
366            len.try_into().unwrap(),
367        ))
368    }
369}
370
371#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
372pub(crate) fn socketpair(
373    domain: AddressFamily,
374    type_: SocketType,
375    flags: SocketFlags,
376    protocol: Protocol,
377) -> io::Result<(OwnedFd, OwnedFd)> {
378    unsafe {
379        let mut fds = MaybeUninit::<[OwnedFd; 2]>::uninit();
380        ret(c::socketpair(
381            c::c_int::from(domain.0),
382            type_.0 as c::c_int | flags.bits(),
383            protocol.0,
384            fds.as_mut_ptr().cast::<c::c_int>(),
385        ))?;
386
387        let [fd0, fd1] = fds.assume_init();
388        Ok((fd0, fd1))
389    }
390}
391
392#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
393pub(crate) mod sockopt {
394    use super::{c, in6_addr_new, in_addr_new, BorrowedFd};
395    use crate::io;
396    use crate::net::sockopt::Timeout;
397    use crate::net::{Ipv4Addr, Ipv6Addr, SocketType};
398    use crate::utils::as_mut_ptr;
399    use core::convert::TryInto;
400    use core::time::Duration;
401    #[cfg(windows)]
402    use windows_sys::Win32::Foundation::BOOL;
403
404    // TODO: With Rust 1.53 we can use `Duration::ZERO` instead.
405    const DURATION_ZERO: Duration = Duration::from_secs(0);
406
407    #[inline]
408    fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T> {
409        use super::*;
410
411        let mut optlen = core::mem::size_of::<T>().try_into().unwrap();
412        debug_assert!(
413            optlen as usize >= core::mem::size_of::<c::c_int>(),
414            "Socket APIs don't ever use `bool` directly"
415        );
416
417        unsafe {
418            let mut value = core::mem::zeroed::<T>();
419            ret(c::getsockopt(
420                borrowed_fd(fd),
421                level,
422                optname,
423                as_mut_ptr(&mut value).cast(),
424                &mut optlen,
425            ))?;
426            // On Windows at least, `getsockopt` has been observed writing 1
427            // byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though
428            // Windows' documentation says that should write a 4-byte `BOOL`.
429            // So, we initialize the memory to zeros above, and just assert
430            // that `getsockopt` doesn't write too many bytes here.
431            assert!(
432                optlen as usize <= size_of::<T>(),
433                "unexpected getsockopt size"
434            );
435            Ok(value)
436        }
437    }
438
439    #[inline]
440    fn setsockopt<T: Copy>(
441        fd: BorrowedFd<'_>,
442        level: i32,
443        optname: i32,
444        value: T,
445    ) -> io::Result<()> {
446        use super::*;
447
448        let optlen = core::mem::size_of::<T>().try_into().unwrap();
449        debug_assert!(
450            optlen as usize >= core::mem::size_of::<c::c_int>(),
451            "Socket APIs don't ever use `bool` directly"
452        );
453
454        unsafe {
455            ret(c::setsockopt(
456                borrowed_fd(fd),
457                level,
458                optname,
459                as_ptr(&value).cast(),
460                optlen,
461            ))
462        }
463    }
464
465    #[inline]
466    pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> {
467        getsockopt(fd, c::SOL_SOCKET as _, c::SO_TYPE)
468    }
469
470    #[inline]
471    pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> {
472        setsockopt(
473            fd,
474            c::SOL_SOCKET as _,
475            c::SO_REUSEADDR,
476            from_bool(reuseaddr),
477        )
478    }
479
480    #[inline]
481    pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> {
482        setsockopt(
483            fd,
484            c::SOL_SOCKET as _,
485            c::SO_BROADCAST,
486            from_bool(broadcast),
487        )
488    }
489
490    #[inline]
491    pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> {
492        getsockopt(fd, c::SOL_SOCKET as _, c::SO_BROADCAST).map(to_bool)
493    }
494
495    #[inline]
496    pub(crate) fn set_socket_linger(
497        fd: BorrowedFd<'_>,
498        linger: Option<Duration>,
499    ) -> io::Result<()> {
500        // Convert `linger` to seconds, rounding up.
501        let l_linger = if let Some(linger) = linger {
502            let mut l_linger = linger.as_secs();
503            if linger.subsec_nanos() != 0 {
504                l_linger = l_linger.checked_add(1).ok_or(io::Errno::INVAL)?;
505            }
506            l_linger.try_into().map_err(|_e| io::Errno::INVAL)?
507        } else {
508            0
509        };
510        let linger = c::linger {
511            l_onoff: linger.is_some() as _,
512            l_linger,
513        };
514        setsockopt(fd, c::SOL_SOCKET as _, c::SO_LINGER, linger)
515    }
516
517    #[inline]
518    pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> {
519        let linger: c::linger = getsockopt(fd, c::SOL_SOCKET as _, c::SO_LINGER)?;
520        // TODO: With Rust 1.50, this could use `.then`.
521        Ok(if linger.l_onoff != 0 {
522            Some(Duration::from_secs(linger.l_linger as u64))
523        } else {
524            None
525        })
526    }
527
528    #[cfg(any(target_os = "android", target_os = "linux"))]
529    #[inline]
530    pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> {
531        setsockopt(fd, c::SOL_SOCKET as _, c::SO_PASSCRED, from_bool(passcred))
532    }
533
534    #[cfg(any(target_os = "android", target_os = "linux"))]
535    #[inline]
536    pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> {
537        getsockopt(fd, c::SOL_SOCKET as _, c::SO_PASSCRED).map(to_bool)
538    }
539
540    #[inline]
541    pub(crate) fn set_socket_timeout(
542        fd: BorrowedFd<'_>,
543        id: Timeout,
544        timeout: Option<Duration>,
545    ) -> io::Result<()> {
546        let optname = match id {
547            Timeout::Recv => c::SO_RCVTIMEO,
548            Timeout::Send => c::SO_SNDTIMEO,
549        };
550
551        #[cfg(not(windows))]
552        let timeout = match timeout {
553            Some(timeout) => {
554                if timeout == DURATION_ZERO {
555                    return Err(io::Errno::INVAL);
556                }
557
558                // Rust's musl libc bindings deprecated `time_t` while they
559                // transition to 64-bit `time_t`. What we want here is just
560                // "whatever type `timeval`'s `tv_sec` is", so we're ok using
561                // the deprecated type.
562                #[allow(deprecated)]
563                let tv_sec = timeout.as_secs().try_into().unwrap_or(c::time_t::MAX);
564
565                // `subsec_micros` rounds down, so we use `subsec_nanos` and
566                // manually round up.
567                let mut timeout = c::timeval {
568                    tv_sec,
569                    tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
570                };
571                if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
572                    timeout.tv_usec = 1;
573                }
574                timeout
575            }
576            None => c::timeval {
577                tv_sec: 0,
578                tv_usec: 0,
579            },
580        };
581
582        #[cfg(windows)]
583        let timeout: u32 = match timeout {
584            Some(timeout) => {
585                if timeout == DURATION_ZERO {
586                    return Err(io::Errno::INVAL);
587                }
588
589                // `as_millis` rounds down, so we use `as_nanos` and
590                // manually round up.
591                let mut timeout: u32 = ((timeout.as_nanos() + 999_999) / 1_000_000)
592                    .try_into()
593                    .map_err(|_convert_err| io::Errno::INVAL)?;
594                if timeout == 0 {
595                    timeout = 1;
596                }
597                timeout
598            }
599            None => 0,
600        };
601
602        setsockopt(fd, c::SOL_SOCKET, optname, timeout)
603    }
604
605    #[inline]
606    pub(crate) fn get_socket_timeout(
607        fd: BorrowedFd<'_>,
608        id: Timeout,
609    ) -> io::Result<Option<Duration>> {
610        let optname = match id {
611            Timeout::Recv => c::SO_RCVTIMEO,
612            Timeout::Send => c::SO_SNDTIMEO,
613        };
614
615        #[cfg(not(windows))]
616        {
617            let timeout: c::timeval = getsockopt(fd, c::SOL_SOCKET, optname)?;
618            if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
619                Ok(None)
620            } else {
621                Ok(Some(
622                    Duration::from_secs(timeout.tv_sec as u64)
623                        + Duration::from_micros(timeout.tv_usec as u64),
624                ))
625            }
626        }
627
628        #[cfg(windows)]
629        {
630            let timeout: u32 = getsockopt(fd, c::SOL_SOCKET, optname)?;
631            if timeout == 0 {
632                Ok(None)
633            } else {
634                Ok(Some(Duration::from_millis(timeout as u64)))
635            }
636        }
637    }
638
639    #[inline]
640    pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
641        setsockopt(fd, c::IPPROTO_IP as _, c::IP_TTL, ttl)
642    }
643
644    #[inline]
645    pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
646        getsockopt(fd, c::IPPROTO_IP as _, c::IP_TTL)
647    }
648
649    #[inline]
650    pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
651        setsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_V6ONLY, from_bool(only_v6))
652    }
653
654    #[inline]
655    pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
656        getsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_V6ONLY).map(to_bool)
657    }
658
659    #[inline]
660    pub(crate) fn set_ip_multicast_loop(
661        fd: BorrowedFd<'_>,
662        multicast_loop: bool,
663    ) -> io::Result<()> {
664        setsockopt(
665            fd,
666            c::IPPROTO_IP as _,
667            c::IP_MULTICAST_LOOP,
668            from_bool(multicast_loop),
669        )
670    }
671
672    #[inline]
673    pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
674        getsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_LOOP).map(to_bool)
675    }
676
677    #[inline]
678    pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
679        setsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_TTL, multicast_ttl)
680    }
681
682    #[inline]
683    pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
684        getsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_TTL)
685    }
686
687    #[inline]
688    pub(crate) fn set_ipv6_multicast_loop(
689        fd: BorrowedFd<'_>,
690        multicast_loop: bool,
691    ) -> io::Result<()> {
692        setsockopt(
693            fd,
694            c::IPPROTO_IPV6 as _,
695            c::IPV6_MULTICAST_LOOP,
696            from_bool(multicast_loop),
697        )
698    }
699
700    #[inline]
701    pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
702        getsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_MULTICAST_LOOP).map(to_bool)
703    }
704
705    #[inline]
706    pub(crate) fn set_ipv6_multicast_hops(
707        fd: BorrowedFd<'_>,
708        multicast_hops: u32,
709    ) -> io::Result<()> {
710        setsockopt(
711            fd,
712            c::IPPROTO_IP as _,
713            c::IPV6_MULTICAST_LOOP,
714            multicast_hops,
715        )
716    }
717
718    #[inline]
719    pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
720        getsockopt(fd, c::IPPROTO_IP as _, c::IPV6_MULTICAST_LOOP)
721    }
722
723    #[inline]
724    pub(crate) fn set_ip_add_membership(
725        fd: BorrowedFd<'_>,
726        multiaddr: &Ipv4Addr,
727        interface: &Ipv4Addr,
728    ) -> io::Result<()> {
729        let mreq = to_imr(multiaddr, interface);
730        setsockopt(fd, c::IPPROTO_IP as _, c::IP_ADD_MEMBERSHIP, mreq)
731    }
732
733    #[inline]
734    pub(crate) fn set_ipv6_add_membership(
735        fd: BorrowedFd<'_>,
736        multiaddr: &Ipv6Addr,
737        interface: u32,
738    ) -> io::Result<()> {
739        #[cfg(not(any(
740            target_os = "dragonfly",
741            target_os = "freebsd",
742            target_os = "haiku",
743            target_os = "illumos",
744            target_os = "ios",
745            target_os = "l4re",
746            target_os = "macos",
747            target_os = "netbsd",
748            target_os = "openbsd",
749            target_os = "solaris",
750        )))]
751        use c::IPV6_ADD_MEMBERSHIP;
752        #[cfg(any(
753            target_os = "dragonfly",
754            target_os = "freebsd",
755            target_os = "haiku",
756            target_os = "illumos",
757            target_os = "ios",
758            target_os = "l4re",
759            target_os = "macos",
760            target_os = "netbsd",
761            target_os = "openbsd",
762            target_os = "solaris",
763        ))]
764        use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
765
766        let mreq = to_ipv6mr(multiaddr, interface);
767        setsockopt(fd, c::IPPROTO_IPV6 as _, IPV6_ADD_MEMBERSHIP, mreq)
768    }
769
770    #[inline]
771    pub(crate) fn set_ip_drop_membership(
772        fd: BorrowedFd<'_>,
773        multiaddr: &Ipv4Addr,
774        interface: &Ipv4Addr,
775    ) -> io::Result<()> {
776        let mreq = to_imr(multiaddr, interface);
777        setsockopt(fd, c::IPPROTO_IP as _, c::IP_DROP_MEMBERSHIP, mreq)
778    }
779
780    #[inline]
781    pub(crate) fn set_ipv6_drop_membership(
782        fd: BorrowedFd<'_>,
783        multiaddr: &Ipv6Addr,
784        interface: u32,
785    ) -> io::Result<()> {
786        #[cfg(not(any(
787            target_os = "dragonfly",
788            target_os = "freebsd",
789            target_os = "haiku",
790            target_os = "illumos",
791            target_os = "ios",
792            target_os = "l4re",
793            target_os = "macos",
794            target_os = "netbsd",
795            target_os = "openbsd",
796            target_os = "solaris",
797        )))]
798        use c::IPV6_DROP_MEMBERSHIP;
799        #[cfg(any(
800            target_os = "dragonfly",
801            target_os = "freebsd",
802            target_os = "haiku",
803            target_os = "illumos",
804            target_os = "ios",
805            target_os = "l4re",
806            target_os = "macos",
807            target_os = "netbsd",
808            target_os = "openbsd",
809            target_os = "solaris",
810        ))]
811        use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
812
813        let mreq = to_ipv6mr(multiaddr, interface);
814        setsockopt(fd, c::IPPROTO_IPV6 as _, IPV6_DROP_MEMBERSHIP, mreq)
815    }
816
817    #[inline]
818    pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
819        setsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY, from_bool(nodelay))
820    }
821
822    #[inline]
823    pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
824        getsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY).map(to_bool)
825    }
826
827    #[inline]
828    fn to_imr(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
829        c::ip_mreq {
830            imr_multiaddr: to_imr_addr(multiaddr),
831            imr_interface: to_imr_addr(interface),
832        }
833    }
834
835    #[inline]
836    fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
837        in_addr_new(u32::from_ne_bytes(addr.octets()))
838    }
839
840    #[inline]
841    fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
842        c::ipv6_mreq {
843            ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
844            ipv6mr_interface: to_ipv6mr_interface(interface),
845        }
846    }
847
848    #[inline]
849    fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
850        in6_addr_new(multiaddr.octets())
851    }
852
853    #[cfg(target_os = "android")]
854    #[inline]
855    fn to_ipv6mr_interface(interface: u32) -> c::c_int {
856        interface as c::c_int
857    }
858
859    #[cfg(not(target_os = "android"))]
860    #[inline]
861    fn to_ipv6mr_interface(interface: u32) -> c::c_uint {
862        interface as c::c_uint
863    }
864
865    // `getsockopt` and `setsockopt` represent boolean values as integers.
866    #[cfg(not(windows))]
867    type RawSocketBool = c::c_int;
868    #[cfg(windows)]
869    type RawSocketBool = BOOL;
870
871    // Wrap `RawSocketBool` in a newtype to discourage misuse.
872    #[repr(transparent)]
873    #[derive(Copy, Clone)]
874    struct SocketBool(RawSocketBool);
875
876    // Convert from a `bool` to a `SocketBool`.
877    #[inline]
878    fn from_bool(value: bool) -> SocketBool {
879        SocketBool(value as _)
880    }
881
882    // Convert from a `SocketBool` to a `bool`.
883    #[inline]
884    fn to_bool(value: SocketBool) -> bool {
885        value.0 != 0
886    }
887}
888