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