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