1// Copyright 2015 The Rust Project Developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::cmp::min;
10use std::ffi::OsStr;
11#[cfg(not(target_os = "redox"))]
12use std::io::IoSlice;
13use std::marker::PhantomData;
14use std::mem::{self, size_of, MaybeUninit};
15use std::net::Shutdown;
16use std::net::{Ipv4Addr, Ipv6Addr};
17#[cfg(all(
18 feature = "all",
19 any(
20 target_os = "ios",
21 target_os = "visionos",
22 target_os = "macos",
23 target_os = "tvos",
24 target_os = "watchos",
25 )
26))]
27use 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))]
42use std::num::NonZeroUsize;
43use 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))]
58use std::os::unix::io::RawFd;
59use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd};
60#[cfg(feature = "all")]
61use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
62use std::path::Path;
63use std::ptr;
64use std::time::{Duration, Instant};
65use 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)))]
74use libc::ssize_t;
75use libc::{in6_addr, in_addr};
76
77use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
78#[cfg(not(target_os = "redox"))]
79use crate::{MsgHdr, MsgHdrMut, RecvFlags};
80
81pub(crate) use libc::c_int;
82
83// Used in `Domain`.
84pub(crate) use libc::{AF_INET, AF_INET6, AF_UNIX};
85// Used in `Type`.
86#[cfg(all(feature = "all", target_os = "linux"))]
87pub(crate) use libc::SOCK_DCCP;
88#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
89pub(crate) use libc::SOCK_RAW;
90#[cfg(all(feature = "all", not(target_os = "espidf")))]
91pub(crate) use libc::SOCK_SEQPACKET;
92pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
93// Used in `Protocol`.
94#[cfg(all(feature = "all", target_os = "linux"))]
95pub(crate) use libc::IPPROTO_DCCP;
96#[cfg(target_os = "linux")]
97pub(crate) use libc::IPPROTO_MPTCP;
98#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
99pub(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))]
109pub(crate) use libc::IPPROTO_UDPLITE;
110pub(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")))]
113pub(crate) use libc::IPPROTO_DIVERT;
114pub(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")))]
119pub(crate) use libc::MSG_TRUNC;
120#[cfg(not(target_os = "redox"))]
121pub(crate) use libc::SO_OOBINLINE;
122// Used in `Socket`.
123#[cfg(not(target_os = "nto"))]
124pub(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)))]
138pub(crate) use libc::IPV6_RECVTCLASS;
139#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
140pub(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)))]
156pub(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)))]
164pub(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)))]
172pub(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))]
180pub(crate) use libc::SO_LINGER_SEC as SO_LINGER;
181#[cfg(target_os = "linux")]
182pub(crate) use libc::SO_PASSCRED;
183pub(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)))]
202pub(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)))]
220pub(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))]
235pub(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))]
255pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL};
256
257// See this type in the Windows file.
258pub(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))]
268use 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)))]
280use libc::TCP_KEEPIDLE as KEEPALIVE_TIME;
281
282/// Helper macro to execute a system call that returns an `io::Result`.
283macro_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)))]
303const 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))]
320const 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")))]
324const 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))]
336type 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))]
366type IovLen = c_int;
367
368/// Unix only API.
369impl 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
393impl_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.
411impl 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 )]
442 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 )]
482 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 ))]
499 pub(crate) const fn _cloexec(self) -> Type {
500 Type(self.0 | libc::SOCK_CLOEXEC)
501 }
502}
503
504impl_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
540impl_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"))]
568impl 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"))]
579 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.
589 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 )]
605 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 )]
620 pub const fn is_dontroute(self) -> bool {
621 self.0 & libc::MSG_DONTROUTE != 0
622 }
623}
624
625#[cfg(not(target_os = "redox"))]
626impl std::fmt::Debug for RecvFlags {
627 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
628 let mut s: DebugStruct<'_, '_> = f.debug_struct(name:"RecvFlags");
629 #[cfg(not(target_os = "espidf"))]
630 s.field(name:"is_end_of_record", &self.is_end_of_record());
631 s.field(name:"is_out_of_band", &self.is_out_of_band());
632 #[cfg(not(target_os = "espidf"))]
633 s.field(name:"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)]
643pub struct MaybeUninitSlice<'a> {
644 vec: libc::iovec,
645 _lifetime: PhantomData<&'a mut [MaybeUninit<u8>]>,
646}
647
648unsafe impl<'a> Send for MaybeUninitSlice<'a> {}
649
650unsafe impl<'a> Sync for MaybeUninitSlice<'a> {}
651
652impl<'a> 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
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
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.
673pub(crate) fn offset_of_path(storage: &libc::sockaddr_un) -> usize {
674 let base: usize = storage as *const _ as usize;
675 let path: usize = ptr::addr_of!(storage.sun_path) as usize;
676 path - base
677}
678
679#[allow(unsafe_op_in_unsafe_fn)]
680pub(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"))]
726pub(crate) use libc::msghdr;
727
728#[cfg(not(target_os = "redox"))]
729pub(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`.
736pub(crate) fn set_msghdr_iov(msg: &mut msghdr, ptr: *mut libc::iovec, len: usize) {
737 msg.msg_iov = ptr;
738 msg.msg_iovlen = min(v1:len, v2:IovLen::MAX as usize) as IovLen;
739}
740
741#[cfg(not(target_os = "redox"))]
742pub(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"))]
748pub(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"))]
753pub(crate) fn msghdr_flags(msg: &msghdr) -> RecvFlags {
754 RecvFlags(msg.msg_flags)
755}
756
757#[cfg(not(target_os = "redox"))]
758pub(crate) fn msghdr_control_len(msg: &msghdr) -> usize {
759 msg.msg_controllen as _
760}
761
762/// Unix only API.
763impl 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 )]
776 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 )]
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.
808 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`.
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.
836 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.
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`.
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`.
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.
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
901pub(crate) type Socket = c_int;
902
903pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
904 crate::socket::Inner::from_raw_fd(socket)
905}
906
907pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket {
908 socket.as_raw_fd()
909}
910
911pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket {
912 socket.into_raw_fd()
913}
914
915pub(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))))]
921pub(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
926pub(crate) fn bind(fd: Socket, addr: &SockAddr) -> io::Result<()> {
927 syscall!(bind(fd, addr.as_ptr(), addr.len() as _)).map(|_| ())
928}
929
930pub(crate) fn connect(fd: Socket, addr: &SockAddr) -> io::Result<()> {
931 syscall!(connect(fd, addr.as_ptr(), addr.len())).map(|_| ())
932}
933
934pub(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
976pub(crate) fn listen(fd: Socket, backlog: c_int) -> io::Result<()> {
977 syscall!(listen(fd, backlog)).map(|_| ())
978}
979
980pub(crate) fn accept(fd: Socket) -> io::Result<(Socket, SockAddr)> {
981 // Safety: `accept` initialises the `SockAddr` for us.
982 unsafe { SockAddr::try_init(|storage: *mut sockaddr_storage, len: *mut u32| syscall!(accept(fd, storage.cast(), len))) }
983}
984
985pub(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: SockAddr)| addr)
989}
990
991pub(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: SockAddr)| addr)
995}
996
997pub(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")))]
1002pub(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"))]
1008pub(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"))]
1015pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> {
1016 if nonblocking {
1017 fcntl_add(fd, get_cmd:libc::F_GETFL, set_cmd:libc::F_SETFL, flag:libc::O_NONBLOCK)
1018 } else {
1019 fcntl_remove(fd, get_cmd:libc::F_GETFL, set_cmd:libc::F_SETFL, flag:libc::O_NONBLOCK)
1020 }
1021}
1022
1023#[cfg(target_os = "vita")]
1024pub(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
1035pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> {
1036 let how: i32 = 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
1044pub(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: isize| n as usize)
1052}
1053
1054pub(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: *mut sockaddr_storage, addrlen: *mut u32| {
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: isize| n as usize)
1071 })
1072 }
1073}
1074
1075pub(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: SockAddr) = recv_from(fd, &mut [MaybeUninit::uninit(); 8], MSG_PEEK)?;
1081 Ok(sender)
1082}
1083
1084#[cfg(not(target_os = "redox"))]
1085pub(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<'_, '_, '_> = MsgHdrMut::new().with_buffers(bufs);
1091 let n: usize = recvmsg(fd, &mut msg, flags)?;
1092 Ok((n, msg.flags()))
1093}
1094
1095#[cfg(not(target_os = "redox"))]
1096pub(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<'_, '_, '_> = MsgHdrMut::new().with_buffers(bufs);
1102 // SAFETY: `recvmsg` initialises the address storage and we set the length
1103 // manually.
1104 let (n: usize, addr: SockAddr) = unsafe {
1105 SockAddr::try_init(|storage: *mut sockaddr_storage, len: *mut u32| {
1106 msg.inner.msg_name = storage.cast();
1107 msg.inner.msg_namelen = *len;
1108 let n: usize = 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"))]
1118pub(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: isize| n as usize)
1124}
1125
1126pub(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: isize| n as usize)
1134}
1135
1136#[cfg(not(target_os = "redox"))]
1137pub(crate) fn send_vectored(fd: Socket, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result<usize> {
1138 let msg: MsgHdr<'_, '_, '_> = MsgHdr::new().with_buffers(bufs);
1139 sendmsg(fd, &msg, flags)
1140}
1141
1142pub(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: isize| n as usize)
1152}
1153
1154#[cfg(not(target_os = "redox"))]
1155pub(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<'_, '_, '_> = MsgHdr::new().with_addr(addr).with_buffers(bufs);
1162 sendmsg(fd, &msg, flags)
1163}
1164
1165#[cfg(not(target_os = "redox"))]
1166pub(crate) fn sendmsg(fd: Socket, msg: &MsgHdr<'_, '_, '_>, flags: c_int) -> io::Result<usize> {
1167 syscall!(sendmsg(fd, &msg.inner, flags)).map(|n: isize| n as usize)
1168}
1169
1170/// Wrapper around `getsockopt` to deal with platform specific timeouts.
1171pub(crate) fn timeout_opt(fd: Socket, opt: c_int, val: c_int) -> io::Result<Option<Duration>> {
1172 unsafe { getsockopt(fd, opt, val).map(op:from_timeval) }
1173}
1174
1175const 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: u64 = duration.tv_sec as u64;
1180 let nsec: u32 = (duration.tv_usec as u32) * 1000;
1181 Some(Duration::new(secs:sec, nanos:nsec))
1182 }
1183}
1184
1185/// Wrapper around `setsockopt` to deal with platform specific timeouts.
1186pub(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: timeval = into_timeval(duration);
1193 unsafe { setsockopt(fd, opt, val, payload:duration) }
1194}
1195
1196fn 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: Duration) => libc::timeval {
1201 tv_sec: min(v1:duration.as_secs(), v2: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)]
1222pub(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)]
1230pub(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)))]
1284fn into_secs(duration: Duration) -> c_int {
1285 min(v1:duration.as_secs(), v2:c_int::MAX as u64) as c_int
1286}
1287
1288/// Get the flags using `cmd`.
1289#[cfg(not(target_os = "vita"))]
1290fn 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"))]
1296fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
1297 let previous: i32 = fcntl_get(fd, get_cmd)?;
1298 let new: i32 = 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"))]
1309fn fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
1310 let previous: i32 = fcntl_get(fd, get_cmd)?;
1311 let new: i32 = 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`.
1321pub(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: u32 = 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`.
1339pub(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: *const c_void = 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
1356pub(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
1365pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr {
1366 Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
1367}
1368
1369pub(crate) const fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr {
1370 in6_addr {
1371 s6_addr: addr.octets(),
1372 }
1373}
1374
1375pub(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)))]
1391pub(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: &u32) => 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: &Ipv4Addr) => 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))]
1413pub(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")))]
1434pub(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.
1451impl 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 )]
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 ))]
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))))]
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"))]
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 )]
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 ))]
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")))))]
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")))))]
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 )]
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 ))))]
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 )]
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 )]
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 )]
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 )]
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 )]
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 )]
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 )]
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 )]
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 )]
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 )]
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 )]
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"))))]
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"]
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 )]
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 )]
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 )]
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"]
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 )]
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"))))]
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"))))]
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 )]
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 )]
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"))]
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"))]
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 )]
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 )]
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 )]
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 )]
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 )]
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 ))]
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")))]
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"))]
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"))]
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 )]
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 )]
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")))]
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")))]
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"))))]
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 )]
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 )]
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 )]
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 )]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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"))))]
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)]
3091pub 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"))))]
3098impl<const N: usize> std::ops::Deref for CcidEndpoints<N> {
3099 type Target = [u8];
3100
3101 fn deref(&self) -> &[u8] {
3102 &self.endpoints[0..self.length as usize]
3103 }
3104}
3105
3106#[cfg_attr(docsrs, doc(cfg(unix)))]
3107impl AsFd for crate::Socket {
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)))]
3115impl AsRawFd for crate::Socket {
3116 fn as_raw_fd(&self) -> c_int {
3117 self.as_raw()
3118 }
3119}
3120
3121#[cfg_attr(docsrs, doc(cfg(unix)))]
3122impl From<crate::Socket> for OwnedFd {
3123 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)))]
3130impl IntoRawFd for crate::Socket {
3131 fn into_raw_fd(self) -> c_int {
3132 self.into_raw()
3133 }
3134}
3135
3136#[cfg_attr(docsrs, doc(cfg(unix)))]
3137impl From<OwnedFd> for crate::Socket {
3138 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)))]
3145impl FromRawFd for crate::Socket {
3146 unsafe fn from_raw_fd(fd: c_int) -> crate::Socket {
3147 crate::Socket::from_raw(fd)
3148 }
3149}
3150
3151#[cfg(feature = "all")]
3152from!(UnixStream, crate::Socket);
3153#[cfg(feature = "all")]
3154from!(UnixListener, crate::Socket);
3155#[cfg(feature = "all")]
3156from!(UnixDatagram, crate::Socket);
3157#[cfg(feature = "all")]
3158from!(crate::Socket, UnixStream);
3159#[cfg(feature = "all")]
3160from!(crate::Socket, UnixListener);
3161#[cfg(feature = "all")]
3162from!(crate::Socket, UnixDatagram);
3163
3164#[test]
3165fn 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]
3181fn 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