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