• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! libc syscalls supporting `rustix::net`.
2 
3 use super::super::c;
4 use super::super::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
5 #[cfg(unix)]
6 use super::addr::SocketAddrUnix;
7 use super::ext::{in6_addr_new, in_addr_new};
8 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
9 use super::read_sockaddr::initialize_family_to_unspec;
10 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
11 use super::read_sockaddr::{maybe_read_sockaddr_os, read_sockaddr_os};
12 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
13 use super::send_recv::{RecvFlags, SendFlags};
14 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
15 use super::types::{AcceptFlags, AddressFamily, Protocol, Shutdown, SocketFlags, SocketType};
16 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
17 use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
18 use crate::fd::{BorrowedFd, OwnedFd};
19 use crate::io;
20 use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
21 use crate::utils::as_ptr;
22 use core::convert::TryInto;
23 use core::mem::{size_of, MaybeUninit};
24 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
25 use core::ptr::null_mut;
26 
27 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result<usize>28 pub(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")))]
send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize>41 pub(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")))]
recvfrom( fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags, ) -> io::Result<(usize, Option<SocketAddrAny>)>54 pub(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")))]
sendto_v4( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrV4, ) -> io::Result<usize>84 pub(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")))]
sendto_v6( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrV6, ) -> io::Result<usize>104 pub(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")))]
sendto_unix( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrUnix, ) -> io::Result<usize>124 pub(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")))]
socket( domain: AddressFamily, type_: SocketType, protocol: Protocol, ) -> io::Result<OwnedFd>144 pub(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")))]
socket_with( domain: AddressFamily, type_: SocketType, flags: SocketFlags, protocol: Protocol, ) -> io::Result<OwnedFd>159 pub(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")))]
bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()>175 pub(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")))]
bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()>186 pub(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")))]
bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()>197 pub(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")))]
connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()>208 pub(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")))]
connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()>219 pub(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")))]
connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()>230 pub(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")))]
listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()>241 pub(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")))]
accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd>246 pub(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 )))]
accept_with(sockfd: BorrowedFd<'_>, flags: AcceptFlags) -> io::Result<OwnedFd>261 pub(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")))]
acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)>274 pub(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 )))]
acceptfrom_with( sockfd: BorrowedFd<'_>, flags: AcceptFlags, ) -> io::Result<(OwnedFd, Option<SocketAddrAny>)>298 pub(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"))]
accept_with(sockfd: BorrowedFd<'_>, _flags: AcceptFlags) -> io::Result<OwnedFd>321 pub(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"))]
acceptfrom_with( sockfd: BorrowedFd<'_>, _flags: AcceptFlags, ) -> io::Result<(OwnedFd, Option<SocketAddrAny>)>328 pub(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")))]
shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()>336 pub(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")))]
getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny>341 pub(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")))]
getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>>355 pub(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")))]
socketpair( domain: AddressFamily, type_: SocketType, flags: SocketFlags, protocol: Protocol, ) -> io::Result<(OwnedFd, OwnedFd)>372 pub(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")))]
393 pub(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]
getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T>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]
setsockopt<T: Copy>( fd: BorrowedFd<'_>, level: i32, optname: i32, value: T, ) -> io::Result<()>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]
get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType>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]
set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()>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]
set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()>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]
get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool>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]
set_socket_linger( fd: BorrowedFd<'_>, linger: Option<Duration>, ) -> io::Result<()>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]
get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>>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]
set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()>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]
get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool>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]
set_socket_timeout( fd: BorrowedFd<'_>, id: Timeout, timeout: Option<Duration>, ) -> io::Result<()>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]
get_socket_timeout( fd: BorrowedFd<'_>, id: Timeout, ) -> io::Result<Option<Duration>>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]
set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()>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]
get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>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]
set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()>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]
get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool>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]
set_ip_multicast_loop( fd: BorrowedFd<'_>, multicast_loop: bool, ) -> io::Result<()>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]
get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>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]
set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()>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]
get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>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]
set_ipv6_multicast_loop( fd: BorrowedFd<'_>, multicast_loop: bool, ) -> io::Result<()>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]
get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>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]
set_ipv6_multicast_hops( fd: BorrowedFd<'_>, multicast_hops: u32, ) -> io::Result<()>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]
get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32>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]
set_ip_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>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]
set_ipv6_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>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]
set_ip_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>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]
set_ipv6_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>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]
set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()>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]
get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool>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]
to_imr(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq828     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]
to_imr_addr(addr: &Ipv4Addr) -> c::in_addr836     fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
837         in_addr_new(u32::from_ne_bytes(addr.octets()))
838     }
839 
840     #[inline]
to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq841     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]
to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr849     fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
850         in6_addr_new(multiaddr.octets())
851     }
852 
853     #[cfg(target_os = "android")]
854     #[inline]
to_ipv6mr_interface(interface: u32) -> c::c_int855     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]
to_ipv6mr_interface(interface: u32) -> c::c_uint861     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]
from_bool(value: bool) -> SocketBool878     fn from_bool(value: bool) -> SocketBool {
879         SocketBool(value as _)
880     }
881 
882     // Convert from a `SocketBool` to a `bool`.
883     #[inline]
to_bool(value: SocketBool) -> bool884     fn to_bool(value: SocketBool) -> bool {
885         value.0 != 0
886     }
887 }
888