• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
9 use std::cmp::min;
10 #[cfg(not(target_os = "redox"))]
11 use std::io::IoSlice;
12 use std::marker::PhantomData;
13 use std::mem::{self, size_of, MaybeUninit};
14 use std::net::Shutdown;
15 use std::net::{Ipv4Addr, Ipv6Addr};
16 #[cfg(all(feature = "all", target_vendor = "apple"))]
17 use std::num::NonZeroU32;
18 #[cfg(all(
19     feature = "all",
20     any(
21         target_os = "android",
22         target_os = "freebsd",
23         target_os = "linux",
24         target_vendor = "apple",
25     )
26 ))]
27 use std::num::NonZeroUsize;
28 #[cfg(feature = "all")]
29 use std::os::unix::ffi::OsStrExt;
30 #[cfg(all(
31     feature = "all",
32     any(
33         target_os = "android",
34         target_os = "freebsd",
35         target_os = "linux",
36         target_vendor = "apple",
37     )
38 ))]
39 use std::os::unix::io::RawFd;
40 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
41 #[cfg(feature = "all")]
42 use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
43 #[cfg(feature = "all")]
44 use std::path::Path;
45 #[cfg(not(all(target_os = "redox", not(feature = "all"))))]
46 use std::ptr;
47 use std::time::{Duration, Instant};
48 use std::{io, slice};
49 
50 #[cfg(not(target_vendor = "apple"))]
51 use libc::ssize_t;
52 use libc::{c_void, in6_addr, in_addr};
53 
54 #[cfg(not(target_os = "redox"))]
55 use crate::RecvFlags;
56 use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
57 
58 pub(crate) use libc::c_int;
59 
60 // Used in `Domain`.
61 pub(crate) use libc::{AF_INET, AF_INET6};
62 // Used in `Type`.
63 #[cfg(all(feature = "all", not(target_os = "redox")))]
64 pub(crate) use libc::SOCK_RAW;
65 #[cfg(feature = "all")]
66 pub(crate) use libc::SOCK_SEQPACKET;
67 pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
68 // Used in `Protocol`.
69 pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
70 // Used in `SockAddr`.
71 pub(crate) use libc::{
72     sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t,
73 };
74 // Used in `RecvFlags`.
75 #[cfg(not(target_os = "redox"))]
76 pub(crate) use libc::{MSG_TRUNC, SO_OOBINLINE};
77 // Used in `Socket`.
78 #[cfg(all(feature = "all", not(target_os = "redox")))]
79 pub(crate) use libc::IP_HDRINCL;
80 #[cfg(not(any(
81     target_os = "dragonfly",
82     target_os = "fuchsia",
83     target_os = "illumos",
84     target_os = "netbsd",
85     target_os = "openbsd",
86     target_os = "redox",
87     target_os = "solaris",
88 )))]
89 pub(crate) use libc::IP_RECVTOS;
90 #[cfg(not(any(
91     target_os = "fuchsia",
92     target_os = "redox",
93     target_os = "solaris",
94     target_os = "illumos",
95 )))]
96 pub(crate) use libc::IP_TOS;
97 #[cfg(not(target_vendor = "apple"))]
98 pub(crate) use libc::SO_LINGER;
99 #[cfg(target_vendor = "apple")]
100 pub(crate) use libc::SO_LINGER_SEC as SO_LINGER;
101 pub(crate) use libc::{
102     ip_mreq as IpMreq, ipv6_mreq as Ipv6Mreq, linger, IPPROTO_IP, IPPROTO_IPV6,
103     IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY,
104     IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
105     IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF,
106     SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
107 };
108 #[cfg(not(any(
109     target_os = "dragonfly",
110     target_os = "haiku",
111     target_os = "netbsd",
112     target_os = "openbsd",
113     target_os = "redox",
114     target_os = "fuchsia",
115 )))]
116 pub(crate) use libc::{
117     ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
118 };
119 #[cfg(not(any(
120     target_os = "dragonfly",
121     target_os = "freebsd",
122     target_os = "haiku",
123     target_os = "illumos",
124     target_os = "netbsd",
125     target_os = "openbsd",
126     target_os = "solaris",
127     target_vendor = "apple"
128 )))]
129 pub(crate) use libc::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP};
130 #[cfg(any(
131     target_os = "dragonfly",
132     target_os = "freebsd",
133     target_os = "haiku",
134     target_os = "illumos",
135     target_os = "netbsd",
136     target_os = "openbsd",
137     target_os = "solaris",
138     target_vendor = "apple",
139 ))]
140 pub(crate) use libc::{
141     IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP, IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP,
142 };
143 #[cfg(all(
144     feature = "all",
145     any(
146         target_os = "android",
147         target_os = "dragonfly",
148         target_os = "freebsd",
149         target_os = "fuchsia",
150         target_os = "illumos",
151         target_os = "linux",
152         target_os = "netbsd",
153         target_vendor = "apple",
154     )
155 ))]
156 pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL};
157 
158 // See this type in the Windows file.
159 pub(crate) type Bool = c_int;
160 
161 #[cfg(target_vendor = "apple")]
162 use libc::TCP_KEEPALIVE as KEEPALIVE_TIME;
163 #[cfg(not(any(target_vendor = "apple", target_os = "haiku", target_os = "openbsd")))]
164 use libc::TCP_KEEPIDLE as KEEPALIVE_TIME;
165 
166 /// Helper macro to execute a system call that returns an `io::Result`.
167 macro_rules! syscall {
168     ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
169         #[allow(unused_unsafe)]
170         let res = unsafe { libc::$fn($($arg, )*) };
171         if res == -1 {
172             Err(std::io::Error::last_os_error())
173         } else {
174             Ok(res)
175         }
176     }};
177 }
178 
179 /// Maximum size of a buffer passed to system call like `recv` and `send`.
180 #[cfg(not(target_vendor = "apple"))]
181 const MAX_BUF_LEN: usize = <ssize_t>::max_value() as usize;
182 
183 // The maximum read limit on most posix-like systems is `SSIZE_MAX`, with the
184 // man page quoting that if the count of bytes to read is greater than
185 // `SSIZE_MAX` the result is "unspecified".
186 //
187 // On macOS, however, apparently the 64-bit libc is either buggy or
188 // intentionally showing odd behavior by rejecting any read with a size larger
189 // than or equal to INT_MAX. To handle both of these the read size is capped on
190 // both platforms.
191 #[cfg(target_vendor = "apple")]
192 const MAX_BUF_LEN: usize = <c_int>::max_value() as usize - 1;
193 
194 #[cfg(any(
195     all(
196         target_os = "linux",
197         any(
198             target_env = "gnu",
199             all(target_env = "uclibc", target_pointer_width = "64")
200         )
201     ),
202     target_os = "android",
203 ))]
204 type IovLen = usize;
205 
206 #[cfg(any(
207     all(
208         target_os = "linux",
209         any(
210             target_env = "musl",
211             all(target_env = "uclibc", target_pointer_width = "32")
212         )
213     ),
214     target_os = "dragonfly",
215     target_os = "freebsd",
216     target_os = "fuchsia",
217     target_os = "haiku",
218     target_os = "illumos",
219     target_os = "netbsd",
220     target_os = "openbsd",
221     target_os = "solaris",
222     target_vendor = "apple",
223 ))]
224 type IovLen = c_int;
225 
226 /// Unix only API.
227 impl Domain {
228     /// Domain for Unix socket communication, corresponding to `AF_UNIX`.
229     #[cfg_attr(docsrs, doc(cfg(unix)))]
230     pub const UNIX: Domain = Domain(libc::AF_UNIX);
231 
232     /// Domain for low-level packet interface, corresponding to `AF_PACKET`.
233     #[cfg(all(
234         feature = "all",
235         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
236     ))]
237     #[cfg_attr(
238         docsrs,
239         doc(cfg(all(
240             feature = "all",
241             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
242         )))
243     )]
244     pub const PACKET: Domain = Domain(libc::AF_PACKET);
245 
246     /// Domain for low-level VSOCK interface, corresponding to `AF_VSOCK`.
247     #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
248     #[cfg_attr(
249         docsrs,
250         doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
251     )]
252     pub const VSOCK: Domain = Domain(libc::AF_VSOCK);
253 }
254 
255 impl_debug!(
256     Domain,
257     libc::AF_INET,
258     libc::AF_INET6,
259     libc::AF_UNIX,
260     #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
261     #[cfg_attr(
262         docsrs,
263         doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
264     )]
265     libc::AF_PACKET,
266     #[cfg(any(target_os = "android", target_os = "linux"))]
267     #[cfg_attr(docsrs, doc(cfg(any(target_os = "android", target_os = "linux"))))]
268     libc::AF_VSOCK,
269     libc::AF_UNSPEC, // = 0.
270 );
271 
272 /// Unix only API.
273 impl Type {
274     /// Set `SOCK_NONBLOCK` on the `Type`.
275     #[cfg(all(
276         feature = "all",
277         any(
278             target_os = "android",
279             target_os = "dragonfly",
280             target_os = "freebsd",
281             target_os = "fuchsia",
282             target_os = "illumos",
283             target_os = "linux",
284             target_os = "netbsd",
285             target_os = "openbsd"
286         )
287     ))]
288     #[cfg_attr(
289         docsrs,
290         doc(cfg(all(
291             feature = "all",
292             any(
293                 target_os = "android",
294                 target_os = "dragonfly",
295                 target_os = "freebsd",
296                 target_os = "fuchsia",
297                 target_os = "illumos",
298                 target_os = "linux",
299                 target_os = "netbsd",
300                 target_os = "openbsd"
301             )
302         )))
303     )]
nonblocking(self) -> Type304     pub const fn nonblocking(self) -> Type {
305         Type(self.0 | libc::SOCK_NONBLOCK)
306     }
307 
308     /// Set `SOCK_CLOEXEC` on the `Type`.
309     #[cfg(all(
310         feature = "all",
311         any(
312             target_os = "android",
313             target_os = "dragonfly",
314             target_os = "freebsd",
315             target_os = "fuchsia",
316             target_os = "illumos",
317             target_os = "linux",
318             target_os = "netbsd",
319             target_os = "openbsd"
320         )
321     ))]
322     #[cfg_attr(
323         docsrs,
324         doc(cfg(all(
325             feature = "all",
326             any(
327                 target_os = "android",
328                 target_os = "dragonfly",
329                 target_os = "freebsd",
330                 target_os = "fuchsia",
331                 target_os = "illumos",
332                 target_os = "linux",
333                 target_os = "netbsd",
334                 target_os = "openbsd"
335             )
336         )))
337     )]
cloexec(self) -> Type338     pub const fn cloexec(self) -> Type {
339         self._cloexec()
340     }
341 
342     #[cfg(any(
343         target_os = "android",
344         target_os = "dragonfly",
345         target_os = "freebsd",
346         target_os = "fuchsia",
347         target_os = "illumos",
348         target_os = "linux",
349         target_os = "netbsd",
350         target_os = "openbsd"
351     ))]
_cloexec(self) -> Type352     pub(crate) const fn _cloexec(self) -> Type {
353         Type(self.0 | libc::SOCK_CLOEXEC)
354     }
355 }
356 
357 impl_debug!(
358     Type,
359     libc::SOCK_STREAM,
360     libc::SOCK_DGRAM,
361     #[cfg(not(target_os = "redox"))]
362     libc::SOCK_RAW,
363     #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
364     libc::SOCK_RDM,
365     libc::SOCK_SEQPACKET,
366     /* TODO: add these optional bit OR-ed flags:
367     #[cfg(any(
368         target_os = "android",
369         target_os = "dragonfly",
370         target_os = "freebsd",
371         target_os = "fuchsia",
372         target_os = "linux",
373         target_os = "netbsd",
374         target_os = "openbsd"
375     ))]
376     libc::SOCK_NONBLOCK,
377     #[cfg(any(
378         target_os = "android",
379         target_os = "dragonfly",
380         target_os = "freebsd",
381         target_os = "fuchsia",
382         target_os = "linux",
383         target_os = "netbsd",
384         target_os = "openbsd"
385     ))]
386     libc::SOCK_CLOEXEC,
387     */
388 );
389 
390 impl_debug!(
391     Protocol,
392     libc::IPPROTO_ICMP,
393     libc::IPPROTO_ICMPV6,
394     libc::IPPROTO_TCP,
395     libc::IPPROTO_UDP,
396 );
397 
398 /// Unix-only API.
399 #[cfg(not(target_os = "redox"))]
400 impl RecvFlags {
401     /// Check if the message terminates a record.
402     ///
403     /// Not all socket types support the notion of records.
404     /// For socket types that do support it (such as [`SEQPACKET`][Type::SEQPACKET]),
405     /// a record is terminated by sending a message with the end-of-record flag set.
406     ///
407     /// On Unix this corresponds to the MSG_EOR flag.
is_end_of_record(self) -> bool408     pub const fn is_end_of_record(self) -> bool {
409         self.0 & libc::MSG_EOR != 0
410     }
411 
412     /// Check if the message contains out-of-band data.
413     ///
414     /// This is useful for protocols where you receive out-of-band data
415     /// mixed in with the normal data stream.
416     ///
417     /// On Unix this corresponds to the MSG_OOB flag.
is_out_of_band(self) -> bool418     pub const fn is_out_of_band(self) -> bool {
419         self.0 & libc::MSG_OOB != 0
420     }
421 }
422 
423 #[cfg(not(target_os = "redox"))]
424 impl std::fmt::Debug for RecvFlags {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result425     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426         f.debug_struct("RecvFlags")
427             .field("is_end_of_record", &self.is_end_of_record())
428             .field("is_out_of_band", &self.is_out_of_band())
429             .field("is_truncated", &self.is_truncated())
430             .finish()
431     }
432 }
433 
434 #[repr(transparent)]
435 pub struct MaybeUninitSlice<'a> {
436     vec: libc::iovec,
437     _lifetime: PhantomData<&'a mut [MaybeUninit<u8>]>,
438 }
439 
440 unsafe impl<'a> Send for MaybeUninitSlice<'a> {}
441 
442 unsafe impl<'a> Sync for MaybeUninitSlice<'a> {}
443 
444 impl<'a> MaybeUninitSlice<'a> {
new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a>445     pub(crate) fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
446         MaybeUninitSlice {
447             vec: libc::iovec {
448                 iov_base: buf.as_mut_ptr().cast(),
449                 iov_len: buf.len(),
450             },
451             _lifetime: PhantomData,
452         }
453     }
454 
as_slice(&self) -> &[MaybeUninit<u8>]455     pub(crate) fn as_slice(&self) -> &[MaybeUninit<u8>] {
456         unsafe { slice::from_raw_parts(self.vec.iov_base.cast(), self.vec.iov_len) }
457     }
458 
as_mut_slice(&mut self) -> &mut [MaybeUninit<u8>]459     pub(crate) fn as_mut_slice(&mut self) -> &mut [MaybeUninit<u8>] {
460         unsafe { slice::from_raw_parts_mut(self.vec.iov_base.cast(), self.vec.iov_len) }
461     }
462 }
463 
464 /// Unix only API.
465 impl SockAddr {
466     /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
467     ///
468     /// # Failure
469     ///
470     /// Returns an error if the path is longer than `SUN_LEN`.
471     #[cfg(feature = "all")]
472     #[cfg_attr(docsrs, doc(cfg(all(unix, feature = "all"))))]
473     #[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
unix<P>(path: P) -> io::Result<SockAddr> where P: AsRef<Path>,474     pub fn unix<P>(path: P) -> io::Result<SockAddr>
475     where
476         P: AsRef<Path>,
477     {
478         unsafe {
479             SockAddr::init(|storage, len| {
480                 // Safety: `SockAddr::init` zeros the address, which is a valid
481                 // representation.
482                 let storage: &mut libc::sockaddr_un = unsafe { &mut *storage.cast() };
483                 let len: &mut socklen_t = unsafe { &mut *len };
484 
485                 let bytes = path.as_ref().as_os_str().as_bytes();
486                 let too_long = match bytes.first() {
487                     None => false,
488                     // linux abstract namespaces aren't null-terminated
489                     Some(&0) => bytes.len() > storage.sun_path.len(),
490                     Some(_) => bytes.len() >= storage.sun_path.len(),
491                 };
492                 if too_long {
493                     return Err(io::Error::new(
494                         io::ErrorKind::InvalidInput,
495                         "path must be shorter than SUN_LEN",
496                     ));
497                 }
498 
499                 storage.sun_family = libc::AF_UNIX as sa_family_t;
500                 // Safety: `bytes` and `addr.sun_path` are not overlapping and
501                 // both point to valid memory.
502                 // `SockAddr::init` zeroes the memory, so the path is already
503                 // null terminated.
504                 unsafe {
505                     ptr::copy_nonoverlapping(
506                         bytes.as_ptr(),
507                         storage.sun_path.as_mut_ptr() as *mut u8,
508                         bytes.len(),
509                     )
510                 };
511 
512                 let base = storage as *const _ as usize;
513                 let path = &storage.sun_path as *const _ as usize;
514                 let sun_path_offset = path - base;
515                 let length = sun_path_offset
516                     + bytes.len()
517                     + match bytes.first() {
518                         Some(&0) | None => 0,
519                         Some(_) => 1,
520                     };
521                 *len = length as socklen_t;
522 
523                 Ok(())
524             })
525         }
526         .map(|(_, addr)| addr)
527     }
528 }
529 
530 impl SockAddr {
531     /// Constructs a `SockAddr` with the family `AF_VSOCK` and the provided CID/port.
532     ///
533     /// # Errors
534     ///
535     /// This function can never fail. In a future version of this library it will be made
536     /// infallible.
537     #[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
538     #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
539     #[cfg_attr(
540         docsrs,
541         doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
542     )]
vsock(cid: u32, port: u32) -> io::Result<SockAddr>543     pub fn vsock(cid: u32, port: u32) -> io::Result<SockAddr> {
544         unsafe {
545             SockAddr::init(|storage, len| {
546                 // Safety: `SockAddr::init` zeros the address, which is a valid
547                 // representation.
548                 let storage: &mut libc::sockaddr_vm = unsafe { &mut *storage.cast() };
549                 let len: &mut socklen_t = unsafe { &mut *len };
550 
551                 storage.svm_family = libc::AF_VSOCK as sa_family_t;
552                 storage.svm_cid = cid;
553                 storage.svm_port = port;
554 
555                 *len = mem::size_of::<libc::sockaddr_vm>() as socklen_t;
556 
557                 Ok(())
558             })
559         }
560         .map(|(_, addr)| addr)
561     }
562 
563     /// Returns this address VSOCK CID/port if it is in the `AF_VSOCK` family,
564     /// otherwise return `None`.
565     #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
566     #[cfg_attr(
567         docsrs,
568         doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
569     )]
vsock_address(&self) -> Option<(u32, u32)>570     pub fn vsock_address(&self) -> Option<(u32, u32)> {
571         if self.family() == libc::AF_VSOCK as sa_family_t {
572             // Safety: if the ss_family field is AF_VSOCK then storage must be a sockaddr_vm.
573             let addr = unsafe { &*(self.as_ptr() as *const libc::sockaddr_vm) };
574             Some((addr.svm_cid, addr.svm_port))
575         } else {
576             None
577         }
578     }
579 }
580 
581 pub(crate) type Socket = c_int;
582 
socket_from_raw(socket: Socket) -> crate::socket::Inner583 pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
584     crate::socket::Inner::from_raw_fd(socket)
585 }
586 
socket_as_raw(socket: &crate::socket::Inner) -> Socket587 pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket {
588     socket.as_raw_fd()
589 }
590 
socket_into_raw(socket: crate::socket::Inner) -> Socket591 pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket {
592     socket.into_raw_fd()
593 }
594 
socket(family: c_int, ty: c_int, protocol: c_int) -> io::Result<Socket>595 pub(crate) fn socket(family: c_int, ty: c_int, protocol: c_int) -> io::Result<Socket> {
596     syscall!(socket(family, ty, protocol))
597 }
598 
599 #[cfg(feature = "all")]
socketpair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<[Socket; 2]>600 pub(crate) fn socketpair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<[Socket; 2]> {
601     let mut fds = [0, 0];
602     syscall!(socketpair(family, ty, protocol, fds.as_mut_ptr())).map(|_| fds)
603 }
604 
bind(fd: Socket, addr: &SockAddr) -> io::Result<()>605 pub(crate) fn bind(fd: Socket, addr: &SockAddr) -> io::Result<()> {
606     syscall!(bind(fd, addr.as_ptr(), addr.len() as _)).map(|_| ())
607 }
608 
connect(fd: Socket, addr: &SockAddr) -> io::Result<()>609 pub(crate) fn connect(fd: Socket, addr: &SockAddr) -> io::Result<()> {
610     syscall!(connect(fd, addr.as_ptr(), addr.len())).map(|_| ())
611 }
612 
poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()>613 pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> {
614     let start = Instant::now();
615 
616     let mut pollfd = libc::pollfd {
617         fd: socket.as_raw(),
618         events: libc::POLLIN | libc::POLLOUT,
619         revents: 0,
620     };
621 
622     loop {
623         let elapsed = start.elapsed();
624         if elapsed >= timeout {
625             return Err(io::ErrorKind::TimedOut.into());
626         }
627 
628         let timeout = (timeout - elapsed).as_millis();
629         let timeout = clamp(timeout, 1, c_int::max_value() as u128) as c_int;
630 
631         match syscall!(poll(&mut pollfd, 1, timeout)) {
632             Ok(0) => return Err(io::ErrorKind::TimedOut.into()),
633             Ok(_) => {
634                 // Error or hang up indicates an error (or failure to connect).
635                 if (pollfd.revents & libc::POLLHUP) != 0 || (pollfd.revents & libc::POLLERR) != 0 {
636                     match socket.take_error() {
637                         Ok(Some(err)) => return Err(err),
638                         Ok(None) => {
639                             return Err(io::Error::new(
640                                 io::ErrorKind::Other,
641                                 "no error set after POLLHUP",
642                             ))
643                         }
644                         Err(err) => return Err(err),
645                     }
646                 }
647                 return Ok(());
648             }
649             // Got interrupted, try again.
650             Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue,
651             Err(err) => return Err(err),
652         }
653     }
654 }
655 
656 // TODO: use clamp from std lib, stable since 1.50.
clamp<T>(value: T, min: T, max: T) -> T where T: Ord,657 fn clamp<T>(value: T, min: T, max: T) -> T
658 where
659     T: Ord,
660 {
661     if value <= min {
662         min
663     } else if value >= max {
664         max
665     } else {
666         value
667     }
668 }
669 
listen(fd: Socket, backlog: c_int) -> io::Result<()>670 pub(crate) fn listen(fd: Socket, backlog: c_int) -> io::Result<()> {
671     syscall!(listen(fd, backlog)).map(|_| ())
672 }
673 
accept(fd: Socket) -> io::Result<(Socket, SockAddr)>674 pub(crate) fn accept(fd: Socket) -> io::Result<(Socket, SockAddr)> {
675     // Safety: `accept` initialises the `SockAddr` for us.
676     unsafe { SockAddr::init(|storage, len| syscall!(accept(fd, storage.cast(), len))) }
677 }
678 
getsockname(fd: Socket) -> io::Result<SockAddr>679 pub(crate) fn getsockname(fd: Socket) -> io::Result<SockAddr> {
680     // Safety: `accept` initialises the `SockAddr` for us.
681     unsafe { SockAddr::init(|storage, len| syscall!(getsockname(fd, storage.cast(), len))) }
682         .map(|(_, addr)| addr)
683 }
684 
getpeername(fd: Socket) -> io::Result<SockAddr>685 pub(crate) fn getpeername(fd: Socket) -> io::Result<SockAddr> {
686     // Safety: `accept` initialises the `SockAddr` for us.
687     unsafe { SockAddr::init(|storage, len| syscall!(getpeername(fd, storage.cast(), len))) }
688         .map(|(_, addr)| addr)
689 }
690 
try_clone(fd: Socket) -> io::Result<Socket>691 pub(crate) fn try_clone(fd: Socket) -> io::Result<Socket> {
692     syscall!(fcntl(fd, libc::F_DUPFD_CLOEXEC, 0))
693 }
694 
set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()>695 pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> {
696     if nonblocking {
697         fcntl_add(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK)
698     } else {
699         fcntl_remove(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK)
700     }
701 }
702 
shutdown(fd: Socket, how: Shutdown) -> io::Result<()>703 pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> {
704     let how = match how {
705         Shutdown::Write => libc::SHUT_WR,
706         Shutdown::Read => libc::SHUT_RD,
707         Shutdown::Both => libc::SHUT_RDWR,
708     };
709     syscall!(shutdown(fd, how)).map(|_| ())
710 }
711 
recv(fd: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int) -> io::Result<usize>712 pub(crate) fn recv(fd: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int) -> io::Result<usize> {
713     syscall!(recv(
714         fd,
715         buf.as_mut_ptr().cast(),
716         min(buf.len(), MAX_BUF_LEN),
717         flags,
718     ))
719     .map(|n| n as usize)
720 }
721 
recv_from( fd: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int, ) -> io::Result<(usize, SockAddr)>722 pub(crate) fn recv_from(
723     fd: Socket,
724     buf: &mut [MaybeUninit<u8>],
725     flags: c_int,
726 ) -> io::Result<(usize, SockAddr)> {
727     // Safety: `recvfrom` initialises the `SockAddr` for us.
728     unsafe {
729         SockAddr::init(|addr, addrlen| {
730             syscall!(recvfrom(
731                 fd,
732                 buf.as_mut_ptr().cast(),
733                 min(buf.len(), MAX_BUF_LEN),
734                 flags,
735                 addr.cast(),
736                 addrlen
737             ))
738             .map(|n| n as usize)
739         })
740     }
741 }
742 
743 #[cfg(not(target_os = "redox"))]
recv_vectored( fd: Socket, bufs: &mut [crate::MaybeUninitSlice<'_>], flags: c_int, ) -> io::Result<(usize, RecvFlags)>744 pub(crate) fn recv_vectored(
745     fd: Socket,
746     bufs: &mut [crate::MaybeUninitSlice<'_>],
747     flags: c_int,
748 ) -> io::Result<(usize, RecvFlags)> {
749     recvmsg(fd, ptr::null_mut(), bufs, flags).map(|(n, _, recv_flags)| (n, recv_flags))
750 }
751 
752 #[cfg(not(target_os = "redox"))]
recv_from_vectored( fd: Socket, bufs: &mut [crate::MaybeUninitSlice<'_>], flags: c_int, ) -> io::Result<(usize, RecvFlags, SockAddr)>753 pub(crate) fn recv_from_vectored(
754     fd: Socket,
755     bufs: &mut [crate::MaybeUninitSlice<'_>],
756     flags: c_int,
757 ) -> io::Result<(usize, RecvFlags, SockAddr)> {
758     // Safety: `recvmsg` initialises the address storage and we set the length
759     // manually.
760     unsafe {
761         SockAddr::init(|storage, len| {
762             recvmsg(fd, storage, bufs, flags).map(|(n, addrlen, recv_flags)| {
763                 // Set the correct address length.
764                 *len = addrlen;
765                 (n, recv_flags)
766             })
767         })
768     }
769     .map(|((n, recv_flags), addr)| (n, recv_flags, addr))
770 }
771 
772 /// Returns the (bytes received, sending address len, `RecvFlags`).
773 #[cfg(not(target_os = "redox"))]
recvmsg( fd: Socket, msg_name: *mut sockaddr_storage, bufs: &mut [crate::MaybeUninitSlice<'_>], flags: c_int, ) -> io::Result<(usize, libc::socklen_t, RecvFlags)>774 fn recvmsg(
775     fd: Socket,
776     msg_name: *mut sockaddr_storage,
777     bufs: &mut [crate::MaybeUninitSlice<'_>],
778     flags: c_int,
779 ) -> io::Result<(usize, libc::socklen_t, RecvFlags)> {
780     let msg_namelen = if msg_name.is_null() {
781         0
782     } else {
783         size_of::<sockaddr_storage>() as libc::socklen_t
784     };
785     // libc::msghdr contains unexported padding fields on Fuchsia.
786     let mut msg: libc::msghdr = unsafe { mem::zeroed() };
787     msg.msg_name = msg_name.cast();
788     msg.msg_namelen = msg_namelen;
789     msg.msg_iov = bufs.as_mut_ptr().cast();
790     msg.msg_iovlen = min(bufs.len(), IovLen::MAX as usize) as IovLen;
791     syscall!(recvmsg(fd, &mut msg, flags))
792         .map(|n| (n as usize, msg.msg_namelen, RecvFlags(msg.msg_flags)))
793 }
794 
send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result<usize>795 pub(crate) fn send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result<usize> {
796     syscall!(send(
797         fd,
798         buf.as_ptr().cast(),
799         min(buf.len(), MAX_BUF_LEN),
800         flags,
801     ))
802     .map(|n| n as usize)
803 }
804 
805 #[cfg(not(target_os = "redox"))]
send_vectored(fd: Socket, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result<usize>806 pub(crate) fn send_vectored(fd: Socket, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result<usize> {
807     sendmsg(fd, ptr::null(), 0, bufs, flags)
808 }
809 
send_to(fd: Socket, buf: &[u8], addr: &SockAddr, flags: c_int) -> io::Result<usize>810 pub(crate) fn send_to(fd: Socket, buf: &[u8], addr: &SockAddr, flags: c_int) -> io::Result<usize> {
811     syscall!(sendto(
812         fd,
813         buf.as_ptr().cast(),
814         min(buf.len(), MAX_BUF_LEN),
815         flags,
816         addr.as_ptr(),
817         addr.len(),
818     ))
819     .map(|n| n as usize)
820 }
821 
822 #[cfg(not(target_os = "redox"))]
send_to_vectored( fd: Socket, bufs: &[IoSlice<'_>], addr: &SockAddr, flags: c_int, ) -> io::Result<usize>823 pub(crate) fn send_to_vectored(
824     fd: Socket,
825     bufs: &[IoSlice<'_>],
826     addr: &SockAddr,
827     flags: c_int,
828 ) -> io::Result<usize> {
829     sendmsg(fd, addr.as_storage_ptr(), addr.len(), bufs, flags)
830 }
831 
832 /// Returns the (bytes received, sending address len, `RecvFlags`).
833 #[cfg(not(target_os = "redox"))]
sendmsg( fd: Socket, msg_name: *const sockaddr_storage, msg_namelen: socklen_t, bufs: &[IoSlice<'_>], flags: c_int, ) -> io::Result<usize>834 fn sendmsg(
835     fd: Socket,
836     msg_name: *const sockaddr_storage,
837     msg_namelen: socklen_t,
838     bufs: &[IoSlice<'_>],
839     flags: c_int,
840 ) -> io::Result<usize> {
841     // libc::msghdr contains unexported padding fields on Fuchsia.
842     let mut msg: libc::msghdr = unsafe { mem::zeroed() };
843     // Safety: we're creating a `*mut` pointer from a reference, which is UB
844     // once actually used. However the OS should not write to it in the
845     // `sendmsg` system call.
846     msg.msg_name = (msg_name as *mut sockaddr_storage).cast();
847     msg.msg_namelen = msg_namelen;
848     // Safety: Same as above about `*const` -> `*mut`.
849     msg.msg_iov = bufs.as_ptr() as *mut _;
850     msg.msg_iovlen = min(bufs.len(), IovLen::MAX as usize) as IovLen;
851     syscall!(sendmsg(fd, &msg, flags)).map(|n| n as usize)
852 }
853 
854 /// Wrapper around `getsockopt` to deal with platform specific timeouts.
timeout_opt(fd: Socket, opt: c_int, val: c_int) -> io::Result<Option<Duration>>855 pub(crate) fn timeout_opt(fd: Socket, opt: c_int, val: c_int) -> io::Result<Option<Duration>> {
856     unsafe { getsockopt(fd, opt, val).map(from_timeval) }
857 }
858 
from_timeval(duration: libc::timeval) -> Option<Duration>859 fn from_timeval(duration: libc::timeval) -> Option<Duration> {
860     if duration.tv_sec == 0 && duration.tv_usec == 0 {
861         None
862     } else {
863         let sec = duration.tv_sec as u64;
864         let nsec = (duration.tv_usec as u32) * 1000;
865         Some(Duration::new(sec, nsec))
866     }
867 }
868 
869 /// Wrapper around `setsockopt` to deal with platform specific timeouts.
set_timeout_opt( fd: Socket, opt: c_int, val: c_int, duration: Option<Duration>, ) -> io::Result<()>870 pub(crate) fn set_timeout_opt(
871     fd: Socket,
872     opt: c_int,
873     val: c_int,
874     duration: Option<Duration>,
875 ) -> io::Result<()> {
876     let duration = into_timeval(duration);
877     unsafe { setsockopt(fd, opt, val, duration) }
878 }
879 
into_timeval(duration: Option<Duration>) -> libc::timeval880 fn into_timeval(duration: Option<Duration>) -> libc::timeval {
881     match duration {
882         // https://github.com/rust-lang/libc/issues/1848
883         #[cfg_attr(target_env = "musl", allow(deprecated))]
884         Some(duration) => libc::timeval {
885             tv_sec: min(duration.as_secs(), libc::time_t::max_value() as u64) as libc::time_t,
886             tv_usec: duration.subsec_micros() as libc::suseconds_t,
887         },
888         None => libc::timeval {
889             tv_sec: 0,
890             tv_usec: 0,
891         },
892     }
893 }
894 
895 #[cfg(feature = "all")]
896 #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
keepalive_time(fd: Socket) -> io::Result<Duration>897 pub(crate) fn keepalive_time(fd: Socket) -> io::Result<Duration> {
898     unsafe {
899         getsockopt::<c_int>(fd, IPPROTO_TCP, KEEPALIVE_TIME)
900             .map(|secs| Duration::from_secs(secs as u64))
901     }
902 }
903 
904 #[allow(unused_variables)]
set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()>905 pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
906     #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
907     if let Some(time) = keepalive.time {
908         let secs = into_secs(time);
909         unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
910     }
911 
912     #[cfg(any(
913         target_os = "android",
914         target_os = "dragonfly",
915         target_os = "freebsd",
916         target_os = "fuchsia",
917         target_os = "illumos",
918         target_os = "linux",
919         target_os = "netbsd",
920         target_vendor = "apple",
921     ))]
922     {
923         if let Some(interval) = keepalive.interval {
924             let secs = into_secs(interval);
925             unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, secs)? }
926         }
927 
928         if let Some(retries) = keepalive.retries {
929             unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, retries as c_int)? }
930         }
931     }
932 
933     Ok(())
934 }
935 
936 #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
into_secs(duration: Duration) -> c_int937 fn into_secs(duration: Duration) -> c_int {
938     min(duration.as_secs(), c_int::max_value() as u64) as c_int
939 }
940 
941 /// Add `flag` to the current set flags of `F_GETFD`.
fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()>942 fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
943     let previous = syscall!(fcntl(fd, get_cmd))?;
944     let new = previous | flag;
945     if new != previous {
946         syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
947     } else {
948         // Flag was already set.
949         Ok(())
950     }
951 }
952 
953 /// Remove `flag` to the current set flags of `F_GETFD`.
fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()>954 fn fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
955     let previous = syscall!(fcntl(fd, get_cmd))?;
956     let new = previous & !flag;
957     if new != previous {
958         syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
959     } else {
960         // Flag was already set.
961         Ok(())
962     }
963 }
964 
965 /// Caller must ensure `T` is the correct type for `opt` and `val`.
getsockopt<T>(fd: Socket, opt: c_int, val: c_int) -> io::Result<T>966 pub(crate) unsafe fn getsockopt<T>(fd: Socket, opt: c_int, val: c_int) -> io::Result<T> {
967     let mut payload: MaybeUninit<T> = MaybeUninit::uninit();
968     let mut len = size_of::<T>() as libc::socklen_t;
969     syscall!(getsockopt(
970         fd,
971         opt,
972         val,
973         payload.as_mut_ptr().cast(),
974         &mut len,
975     ))
976     .map(|_| {
977         debug_assert_eq!(len as usize, size_of::<T>());
978         // Safety: `getsockopt` initialised `payload` for us.
979         payload.assume_init()
980     })
981 }
982 
983 /// Caller must ensure `T` is the correct type for `opt` and `val`.
setsockopt<T>( fd: Socket, opt: c_int, val: c_int, payload: T, ) -> io::Result<()>984 pub(crate) unsafe fn setsockopt<T>(
985     fd: Socket,
986     opt: c_int,
987     val: c_int,
988     payload: T,
989 ) -> io::Result<()> {
990     let payload = &payload as *const T as *const c_void;
991     syscall!(setsockopt(
992         fd,
993         opt,
994         val,
995         payload,
996         mem::size_of::<T>() as libc::socklen_t,
997     ))
998     .map(|_| ())
999 }
1000 
to_in_addr(addr: &Ipv4Addr) -> in_addr1001 pub(crate) fn to_in_addr(addr: &Ipv4Addr) -> in_addr {
1002     // `s_addr` is stored as BE on all machines, and the array is in BE order.
1003     // So the native endian conversion method is used so that it's never
1004     // swapped.
1005     in_addr {
1006         s_addr: u32::from_ne_bytes(addr.octets()),
1007     }
1008 }
1009 
from_in_addr(in_addr: in_addr) -> Ipv4Addr1010 pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr {
1011     Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
1012 }
1013 
to_in6_addr(addr: &Ipv6Addr) -> in6_addr1014 pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr {
1015     in6_addr {
1016         s6_addr: addr.octets(),
1017     }
1018 }
1019 
from_in6_addr(addr: in6_addr) -> Ipv6Addr1020 pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
1021     Ipv6Addr::from(addr.s6_addr)
1022 }
1023 
1024 #[cfg(not(any(
1025     target_os = "haiku",
1026     target_os = "illumos",
1027     target_os = "netbsd",
1028     target_os = "openbsd",
1029     target_os = "redox",
1030     target_os = "solaris",
1031 )))]
to_mreqn( multiaddr: &Ipv4Addr, interface: &crate::socket::InterfaceIndexOrAddress, ) -> libc::ip_mreqn1032 pub(crate) fn to_mreqn(
1033     multiaddr: &Ipv4Addr,
1034     interface: &crate::socket::InterfaceIndexOrAddress,
1035 ) -> libc::ip_mreqn {
1036     match interface {
1037         crate::socket::InterfaceIndexOrAddress::Index(interface) => libc::ip_mreqn {
1038             imr_multiaddr: to_in_addr(multiaddr),
1039             imr_address: to_in_addr(&Ipv4Addr::UNSPECIFIED),
1040             imr_ifindex: *interface as _,
1041         },
1042         crate::socket::InterfaceIndexOrAddress::Address(interface) => libc::ip_mreqn {
1043             imr_multiaddr: to_in_addr(multiaddr),
1044             imr_address: to_in_addr(interface),
1045             imr_ifindex: 0,
1046         },
1047     }
1048 }
1049 
1050 /// Unix only API.
1051 impl crate::Socket {
1052     /// Accept a new incoming connection from this listener.
1053     ///
1054     /// This function directly corresponds to the `accept4(2)` function.
1055     ///
1056     /// This function will block the calling thread until a new connection is
1057     /// established. When established, the corresponding `Socket` and the remote
1058     /// peer's address will be returned.
1059     #[cfg(all(
1060         feature = "all",
1061         any(
1062             target_os = "android",
1063             target_os = "dragonfly",
1064             target_os = "freebsd",
1065             target_os = "fuchsia",
1066             target_os = "illumos",
1067             target_os = "linux",
1068             target_os = "netbsd",
1069             target_os = "openbsd"
1070         )
1071     ))]
1072     #[cfg_attr(
1073         docsrs,
1074         doc(cfg(all(
1075             feature = "all",
1076             any(
1077                 target_os = "android",
1078                 target_os = "dragonfly",
1079                 target_os = "freebsd",
1080                 target_os = "fuchsia",
1081                 target_os = "illumos",
1082                 target_os = "linux",
1083                 target_os = "netbsd",
1084                 target_os = "openbsd"
1085             )
1086         )))
1087     )]
accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)>1088     pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
1089         self._accept4(flags)
1090     }
1091 
1092     #[cfg(any(
1093         target_os = "android",
1094         target_os = "dragonfly",
1095         target_os = "freebsd",
1096         target_os = "fuchsia",
1097         target_os = "illumos",
1098         target_os = "linux",
1099         target_os = "netbsd",
1100         target_os = "openbsd"
1101     ))]
_accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)>1102     pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
1103         // Safety: `accept4` initialises the `SockAddr` for us.
1104         unsafe {
1105             SockAddr::init(|storage, len| {
1106                 syscall!(accept4(self.as_raw(), storage.cast(), len, flags))
1107                     .map(crate::Socket::from_raw)
1108             })
1109         }
1110     }
1111 
1112     /// Sets `CLOEXEC` on the socket.
1113     ///
1114     /// # Notes
1115     ///
1116     /// On supported platforms you can use [`Type::cloexec`].
1117     #[cfg(feature = "all")]
1118     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
set_cloexec(&self, close_on_exec: bool) -> io::Result<()>1119     pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
1120         self._set_cloexec(close_on_exec)
1121     }
1122 
_set_cloexec(&self, close_on_exec: bool) -> io::Result<()>1123     pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
1124         if close_on_exec {
1125             fcntl_add(
1126                 self.as_raw(),
1127                 libc::F_GETFD,
1128                 libc::F_SETFD,
1129                 libc::FD_CLOEXEC,
1130             )
1131         } else {
1132             fcntl_remove(
1133                 self.as_raw(),
1134                 libc::F_GETFD,
1135                 libc::F_SETFD,
1136                 libc::FD_CLOEXEC,
1137             )
1138         }
1139     }
1140 
1141     /// Sets `SO_NOSIGPIPE` on the socket.
1142     #[cfg(all(feature = "all", any(doc, target_vendor = "apple")))]
1143     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()>1144     pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
1145         self._set_nosigpipe(nosigpipe)
1146     }
1147 
1148     #[cfg(target_vendor = "apple")]
_set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()>1149     pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
1150         unsafe {
1151             setsockopt(
1152                 self.as_raw(),
1153                 libc::SOL_SOCKET,
1154                 libc::SO_NOSIGPIPE,
1155                 nosigpipe as c_int,
1156             )
1157         }
1158     }
1159 
1160     /// Gets the value of the `TCP_MAXSEG` option on this socket.
1161     ///
1162     /// For more information about this option, see [`set_mss`].
1163     ///
1164     /// [`set_mss`]: crate::Socket::set_mss
1165     #[cfg(all(feature = "all", not(target_os = "redox")))]
1166     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
mss(&self) -> io::Result<u32>1167     pub fn mss(&self) -> io::Result<u32> {
1168         unsafe {
1169             getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_MAXSEG)
1170                 .map(|mss| mss as u32)
1171         }
1172     }
1173 
1174     /// Sets the value of the `TCP_MAXSEG` option on this socket.
1175     ///
1176     /// The `TCP_MAXSEG` option denotes the TCP Maximum Segment Size and is only
1177     /// available on TCP sockets.
1178     #[cfg(all(feature = "all", not(target_os = "redox")))]
1179     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
set_mss(&self, mss: u32) -> io::Result<()>1180     pub fn set_mss(&self, mss: u32) -> io::Result<()> {
1181         unsafe {
1182             setsockopt(
1183                 self.as_raw(),
1184                 libc::IPPROTO_TCP,
1185                 libc::TCP_MAXSEG,
1186                 mss as c_int,
1187             )
1188         }
1189     }
1190 
1191     /// Returns `true` if `listen(2)` was called on this socket by checking the
1192     /// `SO_ACCEPTCONN` option on this socket.
1193     #[cfg(all(
1194         feature = "all",
1195         any(
1196             target_os = "android",
1197             target_os = "freebsd",
1198             target_os = "fuchsia",
1199             target_os = "linux",
1200         )
1201     ))]
1202     #[cfg_attr(
1203         docsrs,
1204         doc(cfg(all(
1205             feature = "all",
1206             any(
1207                 target_os = "android",
1208                 target_os = "freebsd",
1209                 target_os = "fuchsia",
1210                 target_os = "linux",
1211             )
1212         )))
1213     )]
is_listener(&self) -> io::Result<bool>1214     pub fn is_listener(&self) -> io::Result<bool> {
1215         unsafe {
1216             getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_ACCEPTCONN)
1217                 .map(|v| v != 0)
1218         }
1219     }
1220 
1221     /// Returns the [`Domain`] of this socket by checking the `SO_DOMAIN` option
1222     /// on this socket.
1223     #[cfg(all(
1224         feature = "all",
1225         any(
1226             target_os = "android",
1227             // TODO: add FreeBSD.
1228             // target_os = "freebsd",
1229             target_os = "fuchsia",
1230             target_os = "linux",
1231         )
1232     ))]
1233     #[cfg_attr(docsrs, doc(cfg(all(
1234         feature = "all",
1235         any(
1236             target_os = "android",
1237             // TODO: add FreeBSD.
1238             // target_os = "freebsd",
1239             target_os = "fuchsia",
1240             target_os = "linux",
1241         )
1242     ))))]
domain(&self) -> io::Result<Domain>1243     pub fn domain(&self) -> io::Result<Domain> {
1244         unsafe { getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_DOMAIN).map(Domain) }
1245     }
1246 
1247     /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL`
1248     /// option on this socket.
1249     #[cfg(all(
1250         feature = "all",
1251         any(
1252             target_os = "android",
1253             target_os = "freebsd",
1254             target_os = "fuchsia",
1255             target_os = "linux",
1256         )
1257     ))]
1258     #[cfg_attr(
1259         docsrs,
1260         doc(cfg(all(
1261             feature = "all",
1262             any(
1263                 target_os = "android",
1264                 target_os = "freebsd",
1265                 target_os = "fuchsia",
1266                 target_os = "linux",
1267             )
1268         )))
1269     )]
protocol(&self) -> io::Result<Option<Protocol>>1270     pub fn protocol(&self) -> io::Result<Option<Protocol>> {
1271         unsafe {
1272             getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_PROTOCOL).map(|v| match v
1273             {
1274                 0 => None,
1275                 p => Some(Protocol(p)),
1276             })
1277         }
1278     }
1279 
1280     /// Gets the value for the `SO_MARK` option on this socket.
1281     ///
1282     /// This value gets the socket mark field for each packet sent through
1283     /// this socket.
1284     ///
1285     /// On Linux this function requires the `CAP_NET_ADMIN` capability.
1286     #[cfg(all(
1287         feature = "all",
1288         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1289     ))]
1290     #[cfg_attr(
1291         docsrs,
1292         doc(cfg(all(
1293             feature = "all",
1294             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1295         )))
1296     )]
mark(&self) -> io::Result<u32>1297     pub fn mark(&self) -> io::Result<u32> {
1298         unsafe {
1299             getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_MARK)
1300                 .map(|mark| mark as u32)
1301         }
1302     }
1303 
1304     /// Sets the value for the `SO_MARK` option on this socket.
1305     ///
1306     /// This value sets the socket mark field for each packet sent through
1307     /// this socket. Changing the mark can be used for mark-based routing
1308     /// without netfilter or for packet filtering.
1309     ///
1310     /// On Linux this function requires the `CAP_NET_ADMIN` capability.
1311     #[cfg(all(
1312         feature = "all",
1313         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1314     ))]
1315     #[cfg_attr(
1316         docsrs,
1317         doc(cfg(all(
1318             feature = "all",
1319             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1320         )))
1321     )]
set_mark(&self, mark: u32) -> io::Result<()>1322     pub fn set_mark(&self, mark: u32) -> io::Result<()> {
1323         unsafe {
1324             setsockopt::<c_int>(
1325                 self.as_raw(),
1326                 libc::SOL_SOCKET,
1327                 libc::SO_MARK,
1328                 mark as c_int,
1329             )
1330         }
1331     }
1332 
1333     /// Get the value of the `TCP_CORK` option on this socket.
1334     ///
1335     /// For more information about this option, see [`set_cork`].
1336     ///
1337     /// [`set_cork`]: Socket::set_cork
1338     #[cfg(all(
1339         feature = "all",
1340         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1341     ))]
1342     #[cfg_attr(
1343         docsrs,
1344         doc(cfg(all(
1345             feature = "all",
1346             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1347         )))
1348     )]
cork(&self) -> io::Result<bool>1349     pub fn cork(&self) -> io::Result<bool> {
1350         unsafe {
1351             getsockopt::<Bool>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_CORK)
1352                 .map(|cork| cork != 0)
1353         }
1354     }
1355 
1356     /// Set the value of the `TCP_CORK` option on this socket.
1357     ///
1358     /// If set, don't send out partial frames. All queued partial frames are
1359     /// sent when the option is cleared again. There is a 200 millisecond ceiling on
1360     /// the time for which output is corked by `TCP_CORK`. If this ceiling is reached,
1361     /// then queued data is automatically transmitted.
1362     #[cfg(all(
1363         feature = "all",
1364         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1365     ))]
1366     #[cfg_attr(
1367         docsrs,
1368         doc(cfg(all(
1369             feature = "all",
1370             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1371         )))
1372     )]
set_cork(&self, cork: bool) -> io::Result<()>1373     pub fn set_cork(&self, cork: bool) -> io::Result<()> {
1374         unsafe {
1375             setsockopt(
1376                 self.as_raw(),
1377                 libc::IPPROTO_TCP,
1378                 libc::TCP_CORK,
1379                 cork as c_int,
1380             )
1381         }
1382     }
1383 
1384     /// Get the value of the `TCP_QUICKACK` option on this socket.
1385     ///
1386     /// For more information about this option, see [`set_quickack`].
1387     ///
1388     /// [`set_quickack`]: Socket::set_quickack
1389     #[cfg(all(
1390         feature = "all",
1391         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1392     ))]
1393     #[cfg_attr(
1394         docsrs,
1395         doc(cfg(all(
1396             feature = "all",
1397             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1398         )))
1399     )]
quickack(&self) -> io::Result<bool>1400     pub fn quickack(&self) -> io::Result<bool> {
1401         unsafe {
1402             getsockopt::<Bool>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_QUICKACK)
1403                 .map(|quickack| quickack != 0)
1404         }
1405     }
1406 
1407     /// Set the value of the `TCP_QUICKACK` option on this socket.
1408     ///
1409     /// If set, acks are sent immediately, rather than delayed if needed in accordance to normal
1410     /// TCP operation. This flag is not permanent, it only enables a switch to or from quickack mode.
1411     /// Subsequent operation of the TCP protocol will once again enter/leave quickack mode depending on
1412     /// internal protocol processing and factors such as delayed ack timeouts occurring and data transfer.
1413     #[cfg(all(
1414         feature = "all",
1415         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1416     ))]
1417     #[cfg_attr(
1418         docsrs,
1419         doc(cfg(all(
1420             feature = "all",
1421             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1422         )))
1423     )]
set_quickack(&self, quickack: bool) -> io::Result<()>1424     pub fn set_quickack(&self, quickack: bool) -> io::Result<()> {
1425         unsafe {
1426             setsockopt(
1427                 self.as_raw(),
1428                 libc::IPPROTO_TCP,
1429                 libc::TCP_QUICKACK,
1430                 quickack as c_int,
1431             )
1432         }
1433     }
1434 
1435     /// Get the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket.
1436     ///
1437     /// For more information about this option, see [`set_thin_linear_timeouts`].
1438     ///
1439     /// [`set_thin_linear_timeouts`]: Socket::set_thin_linear_timeouts
1440     #[cfg(all(
1441         feature = "all",
1442         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1443     ))]
1444     #[cfg_attr(
1445         docsrs,
1446         doc(cfg(all(
1447             feature = "all",
1448             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1449         )))
1450     )]
thin_linear_timeouts(&self) -> io::Result<bool>1451     pub fn thin_linear_timeouts(&self) -> io::Result<bool> {
1452         unsafe {
1453             getsockopt::<Bool>(
1454                 self.as_raw(),
1455                 libc::IPPROTO_TCP,
1456                 libc::TCP_THIN_LINEAR_TIMEOUTS,
1457             )
1458             .map(|timeouts| timeouts != 0)
1459         }
1460     }
1461 
1462     /// Set the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket.
1463     ///
1464     /// If set, the kernel will dynamically detect a thin-stream connection if there are less than four packets in flight.
1465     /// With less than four packets in flight the normal TCP fast retransmission will not be effective.
1466     /// The kernel will modify the retransmission to avoid the very high latencies that thin stream suffer because of exponential backoff.
1467     #[cfg(all(
1468         feature = "all",
1469         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1470     ))]
1471     #[cfg_attr(
1472         docsrs,
1473         doc(cfg(all(
1474             feature = "all",
1475             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1476         )))
1477     )]
set_thin_linear_timeouts(&self, timeouts: bool) -> io::Result<()>1478     pub fn set_thin_linear_timeouts(&self, timeouts: bool) -> io::Result<()> {
1479         unsafe {
1480             setsockopt(
1481                 self.as_raw(),
1482                 libc::IPPROTO_TCP,
1483                 libc::TCP_THIN_LINEAR_TIMEOUTS,
1484                 timeouts as c_int,
1485             )
1486         }
1487     }
1488 
1489     /// Gets the value for the `SO_BINDTODEVICE` option on this socket.
1490     ///
1491     /// This value gets the socket binded device's interface name.
1492     #[cfg(all(
1493         feature = "all",
1494         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1495     ))]
1496     #[cfg_attr(
1497         docsrs,
1498         doc(cfg(all(
1499             feature = "all",
1500             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1501         )))
1502     )]
device(&self) -> io::Result<Option<Vec<u8>>>1503     pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
1504         // TODO: replace with `MaybeUninit::uninit_array` once stable.
1505         let mut buf: [MaybeUninit<u8>; libc::IFNAMSIZ] =
1506             unsafe { MaybeUninit::uninit().assume_init() };
1507         let mut len = buf.len() as libc::socklen_t;
1508         syscall!(getsockopt(
1509             self.as_raw(),
1510             libc::SOL_SOCKET,
1511             libc::SO_BINDTODEVICE,
1512             buf.as_mut_ptr().cast(),
1513             &mut len,
1514         ))?;
1515         if len == 0 {
1516             Ok(None)
1517         } else {
1518             let buf = &buf[..len as usize - 1];
1519             // TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
1520             Ok(Some(unsafe { &*(buf as *const [_] as *const [u8]) }.into()))
1521         }
1522     }
1523 
1524     /// Sets the value for the `SO_BINDTODEVICE` option on this socket.
1525     ///
1526     /// If a socket is bound to an interface, only packets received from that
1527     /// particular interface are processed by the socket. Note that this only
1528     /// works for some socket types, particularly `AF_INET` sockets.
1529     ///
1530     /// If `interface` is `None` or an empty string it removes the binding.
1531     #[cfg(all(
1532         feature = "all",
1533         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1534     ))]
1535     #[cfg_attr(
1536         docsrs,
1537         doc(cfg(all(
1538             feature = "all",
1539             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1540         )))
1541     )]
bind_device(&self, interface: Option<&[u8]>) -> io::Result<()>1542     pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
1543         let (value, len) = if let Some(interface) = interface {
1544             (interface.as_ptr(), interface.len())
1545         } else {
1546             (ptr::null(), 0)
1547         };
1548         syscall!(setsockopt(
1549             self.as_raw(),
1550             libc::SOL_SOCKET,
1551             libc::SO_BINDTODEVICE,
1552             value.cast(),
1553             len as libc::socklen_t,
1554         ))
1555         .map(|_| ())
1556     }
1557 
1558     /// Sets the value for the `SO_SETFIB` option on this socket.
1559     ///
1560     /// Bind socket to the specified forwarding table (VRF) on a FreeBSD.
1561     #[cfg(all(feature = "all", any(target_os = "freebsd")))]
1562     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", any(target_os = "freebsd")))))]
set_fib(&self, fib: u32) -> io::Result<()>1563     pub fn set_fib(&self, fib: u32) -> io::Result<()> {
1564         syscall!(setsockopt(
1565             self.as_raw(),
1566             libc::SOL_SOCKET,
1567             libc::SO_SETFIB,
1568             (&fib as *const u32).cast(),
1569             mem::size_of::<u32>() as libc::socklen_t,
1570         ))
1571         .map(|_| ())
1572     }
1573 
1574     /// Sets the value for `IP_BOUND_IF` option on this socket.
1575     ///
1576     /// If a socket is bound to an interface, only packets received from that
1577     /// particular interface are processed by the socket.
1578     ///
1579     /// If `interface` is `None`, the binding is removed. If the `interface`
1580     /// index is not valid, an error is returned.
1581     ///
1582     /// One can use `libc::if_nametoindex` to convert an interface alias to an
1583     /// index.
1584     #[cfg(all(feature = "all", target_vendor = "apple"))]
1585     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()>1586     pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
1587         let index = interface.map(NonZeroU32::get).unwrap_or(0);
1588         unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) }
1589     }
1590 
1591     /// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index
1592     /// for the interface to which the socket is bound.
1593     ///
1594     /// Returns `None` if the socket is not bound to any interface, otherwise
1595     /// returns an interface index.
1596     #[cfg(all(feature = "all", target_vendor = "apple"))]
1597     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
device_index(&self) -> io::Result<Option<NonZeroU32>>1598     pub fn device_index(&self) -> io::Result<Option<NonZeroU32>> {
1599         let index =
1600             unsafe { getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF)? };
1601         Ok(NonZeroU32::new(index))
1602     }
1603 
1604     /// Get the value of the `SO_INCOMING_CPU` option on this socket.
1605     ///
1606     /// For more information about this option, see [`set_cpu_affinity`].
1607     ///
1608     /// [`set_cpu_affinity`]: crate::Socket::set_cpu_affinity
1609     #[cfg(all(feature = "all", target_os = "linux"))]
1610     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
cpu_affinity(&self) -> io::Result<usize>1611     pub fn cpu_affinity(&self) -> io::Result<usize> {
1612         unsafe {
1613             getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_INCOMING_CPU)
1614                 .map(|cpu| cpu as usize)
1615         }
1616     }
1617 
1618     /// Set value for the `SO_INCOMING_CPU` option on this socket.
1619     ///
1620     /// Sets the CPU affinity of the socket.
1621     #[cfg(all(feature = "all", target_os = "linux"))]
1622     #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
set_cpu_affinity(&self, cpu: usize) -> io::Result<()>1623     pub fn set_cpu_affinity(&self, cpu: usize) -> io::Result<()> {
1624         unsafe {
1625             setsockopt(
1626                 self.as_raw(),
1627                 libc::SOL_SOCKET,
1628                 libc::SO_INCOMING_CPU,
1629                 cpu as c_int,
1630             )
1631         }
1632     }
1633 
1634     /// Get the value of the `SO_REUSEPORT` option on this socket.
1635     ///
1636     /// For more information about this option, see [`set_reuse_port`].
1637     ///
1638     /// [`set_reuse_port`]: crate::Socket::set_reuse_port
1639     #[cfg(all(
1640         feature = "all",
1641         not(any(target_os = "solaris", target_os = "illumos"))
1642     ))]
1643     #[cfg_attr(
1644         docsrs,
1645         doc(cfg(all(
1646             feature = "all",
1647             unix,
1648             not(any(target_os = "solaris", target_os = "illumos"))
1649         )))
1650     )]
reuse_port(&self) -> io::Result<bool>1651     pub fn reuse_port(&self) -> io::Result<bool> {
1652         unsafe {
1653             getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT)
1654                 .map(|reuse| reuse != 0)
1655         }
1656     }
1657 
1658     /// Set value for the `SO_REUSEPORT` option on this socket.
1659     ///
1660     /// This indicates that further calls to `bind` may allow reuse of local
1661     /// addresses. For IPv4 sockets this means that a socket may bind even when
1662     /// there's a socket already listening on this port.
1663     #[cfg(all(
1664         feature = "all",
1665         not(any(target_os = "solaris", target_os = "illumos"))
1666     ))]
1667     #[cfg_attr(
1668         docsrs,
1669         doc(cfg(all(
1670             feature = "all",
1671             unix,
1672             not(any(target_os = "solaris", target_os = "illumos"))
1673         )))
1674     )]
set_reuse_port(&self, reuse: bool) -> io::Result<()>1675     pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> {
1676         unsafe {
1677             setsockopt(
1678                 self.as_raw(),
1679                 libc::SOL_SOCKET,
1680                 libc::SO_REUSEPORT,
1681                 reuse as c_int,
1682             )
1683         }
1684     }
1685 
1686     /// Get the value of the `IP_FREEBIND` option on this socket.
1687     ///
1688     /// For more information about this option, see [`set_freebind`].
1689     ///
1690     /// [`set_freebind`]: crate::Socket::set_freebind
1691     #[cfg(all(
1692         feature = "all",
1693         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1694     ))]
1695     #[cfg_attr(
1696         docsrs,
1697         doc(cfg(all(
1698             feature = "all",
1699             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1700         )))
1701     )]
freebind(&self) -> io::Result<bool>1702     pub fn freebind(&self) -> io::Result<bool> {
1703         unsafe {
1704             getsockopt::<c_int>(self.as_raw(), libc::SOL_IP, libc::IP_FREEBIND)
1705                 .map(|freebind| freebind != 0)
1706         }
1707     }
1708 
1709     /// Set value for the `IP_FREEBIND` option on this socket.
1710     ///
1711     /// If enabled, this boolean option allows binding to an IP address that is
1712     /// nonlocal or does not (yet) exist.  This permits listening on a socket,
1713     /// without requiring the underlying network interface or the specified
1714     /// dynamic IP address to be up at the time that the application is trying
1715     /// to bind to it.
1716     #[cfg(all(
1717         feature = "all",
1718         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1719     ))]
1720     #[cfg_attr(
1721         docsrs,
1722         doc(cfg(all(
1723             feature = "all",
1724             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1725         )))
1726     )]
set_freebind(&self, freebind: bool) -> io::Result<()>1727     pub fn set_freebind(&self, freebind: bool) -> io::Result<()> {
1728         unsafe {
1729             setsockopt(
1730                 self.as_raw(),
1731                 libc::SOL_IP,
1732                 libc::IP_FREEBIND,
1733                 freebind as c_int,
1734             )
1735         }
1736     }
1737 
1738     /// Get the value of the `IPV6_FREEBIND` option on this socket.
1739     ///
1740     /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
1741     /// Android/Linux. For more information about this option, see
1742     /// [`set_freebind`].
1743     ///
1744     /// [`set_freebind`]: crate::Socket::set_freebind
1745     #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1746     #[cfg_attr(
1747         docsrs,
1748         doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
1749     )]
freebind_ipv6(&self) -> io::Result<bool>1750     pub fn freebind_ipv6(&self) -> io::Result<bool> {
1751         unsafe {
1752             getsockopt::<c_int>(self.as_raw(), libc::SOL_IPV6, libc::IPV6_FREEBIND)
1753                 .map(|freebind| freebind != 0)
1754         }
1755     }
1756 
1757     /// Set value for the `IPV6_FREEBIND` option on this socket.
1758     ///
1759     /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
1760     /// Android/Linux. For more information about this option, see
1761     /// [`set_freebind`].
1762     ///
1763     /// [`set_freebind`]: crate::Socket::set_freebind
1764     ///
1765     /// # Examples
1766     ///
1767     /// On Linux:
1768     ///
1769     /// ```
1770     /// use socket2::{Domain, Socket, Type};
1771     /// use std::io::{self, Error, ErrorKind};
1772     ///
1773     /// fn enable_freebind(socket: &Socket) -> io::Result<()> {
1774     ///     match socket.domain()? {
1775     ///         Domain::IPV4 => socket.set_freebind(true)?,
1776     ///         Domain::IPV6 => socket.set_freebind_ipv6(true)?,
1777     ///         _ => return Err(Error::new(ErrorKind::Other, "unsupported domain")),
1778     ///     };
1779     ///     Ok(())
1780     /// }
1781     ///
1782     /// # fn main() -> io::Result<()> {
1783     /// #     let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
1784     /// #     enable_freebind(&socket)
1785     /// # }
1786     /// ```
1787     #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1788     #[cfg_attr(
1789         docsrs,
1790         doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
1791     )]
set_freebind_ipv6(&self, freebind: bool) -> io::Result<()>1792     pub fn set_freebind_ipv6(&self, freebind: bool) -> io::Result<()> {
1793         unsafe {
1794             setsockopt(
1795                 self.as_raw(),
1796                 libc::SOL_IPV6,
1797                 libc::IPV6_FREEBIND,
1798                 freebind as c_int,
1799             )
1800         }
1801     }
1802 
1803     /// Copies data between a `file` and this socket using the `sendfile(2)`
1804     /// system call. Because this copying is done within the kernel,
1805     /// `sendfile()` is more efficient than the combination of `read(2)` and
1806     /// `write(2)`, which would require transferring data to and from user
1807     /// space.
1808     ///
1809     /// Different OSs support different kinds of `file`s, see the OS
1810     /// documentation for what kind of files are supported. Generally *regular*
1811     /// files are supported by all OSs.
1812     ///
1813     /// The `offset` is the absolute offset into the `file` to use as starting
1814     /// point.
1815     ///
1816     /// Depending on the OS this function *may* change the offset of `file`. For
1817     /// the best results reset the offset of the file before using it again.
1818     ///
1819     /// The `length` determines how many bytes to send, where a length of `None`
1820     /// means it will try to send all bytes.
1821     #[cfg(all(
1822         feature = "all",
1823         any(
1824             target_os = "android",
1825             target_os = "freebsd",
1826             target_os = "linux",
1827             target_vendor = "apple",
1828         )
1829     ))]
1830     #[cfg_attr(
1831         docsrs,
1832         doc(cfg(all(
1833             feature = "all",
1834             any(
1835                 target_os = "android",
1836                 target_os = "freebsd",
1837                 target_os = "linux",
1838                 target_vendor = "apple",
1839             )
1840         )))
1841     )]
sendfile<F>( &self, file: &F, offset: usize, length: Option<NonZeroUsize>, ) -> io::Result<usize> where F: AsRawFd,1842     pub fn sendfile<F>(
1843         &self,
1844         file: &F,
1845         offset: usize,
1846         length: Option<NonZeroUsize>,
1847     ) -> io::Result<usize>
1848     where
1849         F: AsRawFd,
1850     {
1851         self._sendfile(file.as_raw_fd(), offset as _, length)
1852     }
1853 
1854     #[cfg(all(feature = "all", target_vendor = "apple"))]
_sendfile( &self, file: RawFd, offset: libc::off_t, length: Option<NonZeroUsize>, ) -> io::Result<usize>1855     fn _sendfile(
1856         &self,
1857         file: RawFd,
1858         offset: libc::off_t,
1859         length: Option<NonZeroUsize>,
1860     ) -> io::Result<usize> {
1861         // On macOS `length` is value-result parameter. It determines the number
1862         // of bytes to write and returns the number of bytes written.
1863         let mut length = match length {
1864             Some(n) => n.get() as libc::off_t,
1865             // A value of `0` means send all bytes.
1866             None => 0,
1867         };
1868         syscall!(sendfile(
1869             file,
1870             self.as_raw(),
1871             offset,
1872             &mut length,
1873             ptr::null_mut(),
1874             0,
1875         ))
1876         .map(|_| length as usize)
1877     }
1878 
1879     #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
_sendfile( &self, file: RawFd, offset: libc::off_t, length: Option<NonZeroUsize>, ) -> io::Result<usize>1880     fn _sendfile(
1881         &self,
1882         file: RawFd,
1883         offset: libc::off_t,
1884         length: Option<NonZeroUsize>,
1885     ) -> io::Result<usize> {
1886         let count = match length {
1887             Some(n) => n.get() as libc::size_t,
1888             // The maximum the Linux kernel will write in a single call.
1889             None => 0x7ffff000, // 2,147,479,552 bytes.
1890         };
1891         let mut offset = offset;
1892         syscall!(sendfile(self.as_raw(), file, &mut offset, count)).map(|n| n as usize)
1893     }
1894 
1895     #[cfg(all(feature = "all", target_os = "freebsd"))]
_sendfile( &self, file: RawFd, offset: libc::off_t, length: Option<NonZeroUsize>, ) -> io::Result<usize>1896     fn _sendfile(
1897         &self,
1898         file: RawFd,
1899         offset: libc::off_t,
1900         length: Option<NonZeroUsize>,
1901     ) -> io::Result<usize> {
1902         let nbytes = match length {
1903             Some(n) => n.get() as libc::size_t,
1904             // A value of `0` means send all bytes.
1905             None => 0,
1906         };
1907         let mut sbytes: libc::off_t = 0;
1908         syscall!(sendfile(
1909             file,
1910             self.as_raw(),
1911             offset,
1912             nbytes,
1913             ptr::null_mut(),
1914             &mut sbytes,
1915             0,
1916         ))
1917         .map(|_| sbytes as usize)
1918     }
1919 
1920     /// Set the value of the `TCP_USER_TIMEOUT` option on this socket.
1921     ///
1922     /// If set, this specifies the maximum amount of time that transmitted data may remain
1923     /// unacknowledged or buffered data may remain untransmitted before TCP will forcibly close the
1924     /// corresponding connection.
1925     ///
1926     /// Setting `timeout` to `None` or a zero duration causes the system default timeouts to
1927     /// be used. If `timeout` in milliseconds is larger than `c_uint::MAX`, the timeout is clamped
1928     /// to `c_uint::MAX`. For example, when `c_uint` is a 32-bit value, this limits the timeout to
1929     /// approximately 49.71 days.
1930     #[cfg(all(
1931         feature = "all",
1932         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1933     ))]
1934     #[cfg_attr(
1935         docsrs,
1936         doc(cfg(all(
1937             feature = "all",
1938             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1939         )))
1940     )]
set_tcp_user_timeout(&self, timeout: Option<Duration>) -> io::Result<()>1941     pub fn set_tcp_user_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
1942         let timeout = timeout
1943             .map(|to| min(to.as_millis(), libc::c_uint::MAX as u128) as libc::c_uint)
1944             .unwrap_or(0);
1945         unsafe {
1946             setsockopt(
1947                 self.as_raw(),
1948                 libc::IPPROTO_TCP,
1949                 libc::TCP_USER_TIMEOUT,
1950                 timeout,
1951             )
1952         }
1953     }
1954 
1955     /// Get the value of the `TCP_USER_TIMEOUT` option on this socket.
1956     ///
1957     /// For more information about this option, see [`set_tcp_user_timeout`].
1958     ///
1959     /// [`set_tcp_user_timeout`]: Socket::set_tcp_user_timeout
1960     #[cfg(all(
1961         feature = "all",
1962         any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1963     ))]
1964     #[cfg_attr(
1965         docsrs,
1966         doc(cfg(all(
1967             feature = "all",
1968             any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1969         )))
1970     )]
tcp_user_timeout(&self) -> io::Result<Option<Duration>>1971     pub fn tcp_user_timeout(&self) -> io::Result<Option<Duration>> {
1972         unsafe {
1973             getsockopt::<libc::c_uint>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT)
1974                 .map(|millis| {
1975                     if millis == 0 {
1976                         None
1977                     } else {
1978                         Some(Duration::from_millis(millis as u64))
1979                     }
1980                 })
1981         }
1982     }
1983 
1984     /// Attach Berkeley Packet Filter(BPF) on this socket.
1985     ///
1986     /// BPF allows a user-space program to attach a filter onto any socket
1987     /// and allow or disallow certain types of data to come through the socket.
1988     ///
1989     /// For more information about this option, see [filter](https://www.kernel.org/doc/html/v5.12/networking/filter.html)
1990     #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
attach_filter(&self, filters: &[libc::sock_filter]) -> io::Result<()>1991     pub fn attach_filter(&self, filters: &[libc::sock_filter]) -> io::Result<()> {
1992         let prog = libc::sock_fprog {
1993             len: filters.len() as u16,
1994             filter: filters.as_ptr() as *mut _,
1995         };
1996 
1997         unsafe {
1998             setsockopt(
1999                 self.as_raw(),
2000                 libc::SOL_SOCKET,
2001                 libc::SO_ATTACH_FILTER,
2002                 prog,
2003             )
2004         }
2005     }
2006 
2007     /// Detach Berkeley Packet Filter(BPF) from this socket.
2008     ///
2009     /// For more information about this option, see [`attach_filter`]
2010     #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
detach_filter(&self) -> io::Result<()>2011     pub fn detach_filter(&self) -> io::Result<()> {
2012         unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) }
2013     }
2014 }
2015 
2016 #[cfg_attr(docsrs, doc(cfg(unix)))]
2017 impl AsRawFd for crate::Socket {
as_raw_fd(&self) -> c_int2018     fn as_raw_fd(&self) -> c_int {
2019         self.as_raw()
2020     }
2021 }
2022 
2023 #[cfg_attr(docsrs, doc(cfg(unix)))]
2024 impl IntoRawFd for crate::Socket {
into_raw_fd(self) -> c_int2025     fn into_raw_fd(self) -> c_int {
2026         self.into_raw()
2027     }
2028 }
2029 
2030 #[cfg_attr(docsrs, doc(cfg(unix)))]
2031 impl FromRawFd for crate::Socket {
from_raw_fd(fd: c_int) -> crate::Socket2032     unsafe fn from_raw_fd(fd: c_int) -> crate::Socket {
2033         crate::Socket::from_raw(fd)
2034     }
2035 }
2036 
2037 #[cfg(feature = "all")]
2038 from!(UnixStream, crate::Socket);
2039 #[cfg(feature = "all")]
2040 from!(UnixListener, crate::Socket);
2041 #[cfg(feature = "all")]
2042 from!(UnixDatagram, crate::Socket);
2043 #[cfg(feature = "all")]
2044 from!(crate::Socket, UnixStream);
2045 #[cfg(feature = "all")]
2046 from!(crate::Socket, UnixListener);
2047 #[cfg(feature = "all")]
2048 from!(crate::Socket, UnixDatagram);
2049 
2050 #[test]
in_addr_convertion()2051 fn in_addr_convertion() {
2052     let ip = Ipv4Addr::new(127, 0, 0, 1);
2053     let raw = to_in_addr(&ip);
2054     // NOTE: `in_addr` is packed on NetBSD and it's unsafe to borrow.
2055     let a = raw.s_addr;
2056     assert_eq!(a, u32::from_ne_bytes([127, 0, 0, 1]));
2057     assert_eq!(from_in_addr(raw), ip);
2058 
2059     let ip = Ipv4Addr::new(127, 34, 4, 12);
2060     let raw = to_in_addr(&ip);
2061     let a = raw.s_addr;
2062     assert_eq!(a, u32::from_ne_bytes([127, 34, 4, 12]));
2063     assert_eq!(from_in_addr(raw), ip);
2064 }
2065 
2066 #[test]
in6_addr_convertion()2067 fn in6_addr_convertion() {
2068     let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7);
2069     let raw = to_in6_addr(&ip);
2070     let want = [32, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7];
2071     assert_eq!(raw.s6_addr, want);
2072     assert_eq!(from_in6_addr(raw), ip);
2073 }
2074