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")]
869 #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
keepalive_time(fd: Socket) -> io::Result<Duration>870 pub(crate) fn keepalive_time(fd: Socket) -> io::Result<Duration> {
871 unsafe {
872 getsockopt::<c_int>(fd, IPPROTO_TCP, KEEPALIVE_TIME)
873 .map(|secs| Duration::from_secs(secs as u64))
874 }
875 }
876
877 #[allow(unused_variables)]
set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()>878 pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
879 #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
880 if let Some(time) = keepalive.time {
881 let secs = into_secs(time);
882 unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
883 }
884
885 #[cfg(any(
886 target_os = "android",
887 target_os = "dragonfly",
888 target_os = "freebsd",
889 target_os = "fuchsia",
890 target_os = "illumos",
891 target_os = "linux",
892 target_os = "netbsd",
893 target_vendor = "apple",
894 ))]
895 {
896 if let Some(interval) = keepalive.interval {
897 let secs = into_secs(interval);
898 unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, secs)? }
899 }
900
901 if let Some(retries) = keepalive.retries {
902 unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, retries as c_int)? }
903 }
904 }
905
906 Ok(())
907 }
908
909 #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
into_secs(duration: Duration) -> c_int910 fn into_secs(duration: Duration) -> c_int {
911 min(duration.as_secs(), c_int::max_value() as u64) as c_int
912 }
913
914 /// 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<()>915 fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
916 let previous = syscall!(fcntl(fd, get_cmd))?;
917 let new = previous | flag;
918 if new != previous {
919 syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
920 } else {
921 // Flag was already set.
922 Ok(())
923 }
924 }
925
926 /// 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<()>927 fn fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
928 let previous = syscall!(fcntl(fd, get_cmd))?;
929 let new = previous & !flag;
930 if new != previous {
931 syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
932 } else {
933 // Flag was already set.
934 Ok(())
935 }
936 }
937
938 /// 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>939 pub(crate) unsafe fn getsockopt<T>(fd: Socket, opt: c_int, val: c_int) -> io::Result<T> {
940 let mut payload: MaybeUninit<T> = MaybeUninit::uninit();
941 let mut len = size_of::<T>() as libc::socklen_t;
942 syscall!(getsockopt(
943 fd,
944 opt,
945 val,
946 payload.as_mut_ptr().cast(),
947 &mut len,
948 ))
949 .map(|_| {
950 debug_assert_eq!(len as usize, size_of::<T>());
951 // Safety: `getsockopt` initialised `payload` for us.
952 payload.assume_init()
953 })
954 }
955
956 /// 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<()>957 pub(crate) unsafe fn setsockopt<T>(
958 fd: Socket,
959 opt: c_int,
960 val: c_int,
961 payload: T,
962 ) -> io::Result<()> {
963 let payload = &payload as *const T as *const c_void;
964 syscall!(setsockopt(
965 fd,
966 opt,
967 val,
968 payload,
969 mem::size_of::<T>() as libc::socklen_t,
970 ))
971 .map(|_| ())
972 }
973
to_in_addr(addr: &Ipv4Addr) -> in_addr974 pub(crate) fn to_in_addr(addr: &Ipv4Addr) -> in_addr {
975 // `s_addr` is stored as BE on all machines, and the array is in BE order.
976 // So the native endian conversion method is used so that it's never
977 // swapped.
978 in_addr {
979 s_addr: u32::from_ne_bytes(addr.octets()),
980 }
981 }
982
from_in_addr(in_addr: in_addr) -> Ipv4Addr983 pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr {
984 Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
985 }
986
to_in6_addr(addr: &Ipv6Addr) -> in6_addr987 pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr {
988 in6_addr {
989 s6_addr: addr.octets(),
990 }
991 }
992
from_in6_addr(addr: in6_addr) -> Ipv6Addr993 pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
994 Ipv6Addr::from(addr.s6_addr)
995 }
996
997 /// Unix only API.
998 impl crate::Socket {
999 /// Accept a new incoming connection from this listener.
1000 ///
1001 /// This function directly corresponds to the `accept4(2)` function.
1002 ///
1003 /// This function will block the calling thread until a new connection is
1004 /// established. When established, the corresponding `Socket` and the remote
1005 /// peer's address will be returned.
1006 #[cfg(all(
1007 feature = "all",
1008 any(
1009 target_os = "android",
1010 target_os = "dragonfly",
1011 target_os = "freebsd",
1012 target_os = "fuchsia",
1013 target_os = "illumos",
1014 target_os = "linux",
1015 target_os = "netbsd",
1016 target_os = "openbsd"
1017 )
1018 ))]
1019 #[cfg_attr(
1020 docsrs,
1021 doc(cfg(all(
1022 feature = "all",
1023 any(
1024 target_os = "android",
1025 target_os = "dragonfly",
1026 target_os = "freebsd",
1027 target_os = "fuchsia",
1028 target_os = "illumos",
1029 target_os = "linux",
1030 target_os = "netbsd",
1031 target_os = "openbsd"
1032 )
1033 )))
1034 )]
accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)>1035 pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
1036 self._accept4(flags)
1037 }
1038
1039 #[cfg(any(
1040 target_os = "android",
1041 target_os = "dragonfly",
1042 target_os = "freebsd",
1043 target_os = "fuchsia",
1044 target_os = "illumos",
1045 target_os = "linux",
1046 target_os = "netbsd",
1047 target_os = "openbsd"
1048 ))]
_accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)>1049 pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
1050 // Safety: `accept4` initialises the `SockAddr` for us.
1051 unsafe {
1052 SockAddr::init(|storage, len| {
1053 syscall!(accept4(self.as_raw(), storage.cast(), len, flags))
1054 .map(crate::Socket::from_raw)
1055 })
1056 }
1057 }
1058
1059 /// Sets `CLOEXEC` on the socket.
1060 ///
1061 /// # Notes
1062 ///
1063 /// On supported platforms you can use [`Type::cloexec`].
1064 #[cfg(feature = "all")]
1065 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
set_cloexec(&self, close_on_exec: bool) -> io::Result<()>1066 pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
1067 self._set_cloexec(close_on_exec)
1068 }
1069
_set_cloexec(&self, close_on_exec: bool) -> io::Result<()>1070 pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
1071 if close_on_exec {
1072 fcntl_add(
1073 self.as_raw(),
1074 libc::F_GETFD,
1075 libc::F_SETFD,
1076 libc::FD_CLOEXEC,
1077 )
1078 } else {
1079 fcntl_remove(
1080 self.as_raw(),
1081 libc::F_GETFD,
1082 libc::F_SETFD,
1083 libc::FD_CLOEXEC,
1084 )
1085 }
1086 }
1087
1088 /// Sets `SO_NOSIGPIPE` on the socket.
1089 #[cfg(all(feature = "all", any(doc, target_vendor = "apple")))]
1090 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()>1091 pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
1092 self._set_nosigpipe(nosigpipe)
1093 }
1094
1095 #[cfg(target_vendor = "apple")]
_set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()>1096 pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
1097 unsafe {
1098 setsockopt(
1099 self.as_raw(),
1100 libc::SOL_SOCKET,
1101 libc::SO_NOSIGPIPE,
1102 nosigpipe as c_int,
1103 )
1104 }
1105 }
1106
1107 /// Gets the value of the `TCP_MAXSEG` option on this socket.
1108 ///
1109 /// For more information about this option, see [`set_mss`].
1110 ///
1111 /// [`set_mss`]: crate::Socket::set_mss
1112 #[cfg(all(feature = "all", not(target_os = "redox")))]
1113 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
mss(&self) -> io::Result<u32>1114 pub fn mss(&self) -> io::Result<u32> {
1115 unsafe {
1116 getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_MAXSEG)
1117 .map(|mss| mss as u32)
1118 }
1119 }
1120
1121 /// Sets the value of the `TCP_MAXSEG` option on this socket.
1122 ///
1123 /// The `TCP_MAXSEG` option denotes the TCP Maximum Segment Size and is only
1124 /// available on TCP sockets.
1125 #[cfg(all(feature = "all", not(target_os = "redox")))]
1126 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
set_mss(&self, mss: u32) -> io::Result<()>1127 pub fn set_mss(&self, mss: u32) -> io::Result<()> {
1128 unsafe {
1129 setsockopt(
1130 self.as_raw(),
1131 libc::IPPROTO_TCP,
1132 libc::TCP_MAXSEG,
1133 mss as c_int,
1134 )
1135 }
1136 }
1137
1138 /// Returns `true` if `listen(2)` was called on this socket by checking the
1139 /// `SO_ACCEPTCONN` option on this socket.
1140 #[cfg(all(
1141 feature = "all",
1142 any(
1143 target_os = "android",
1144 target_os = "freebsd",
1145 target_os = "fuchsia",
1146 target_os = "linux",
1147 )
1148 ))]
1149 #[cfg_attr(
1150 docsrs,
1151 doc(cfg(all(
1152 feature = "all",
1153 any(
1154 target_os = "android",
1155 target_os = "freebsd",
1156 target_os = "fuchsia",
1157 target_os = "linux",
1158 )
1159 )))
1160 )]
is_listener(&self) -> io::Result<bool>1161 pub fn is_listener(&self) -> io::Result<bool> {
1162 unsafe {
1163 getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_ACCEPTCONN)
1164 .map(|v| v != 0)
1165 }
1166 }
1167
1168 /// Returns the [`Domain`] of this socket by checking the `SO_DOMAIN` option
1169 /// on this socket.
1170 #[cfg(all(
1171 feature = "all",
1172 any(
1173 target_os = "android",
1174 // TODO: add FreeBSD.
1175 // target_os = "freebsd",
1176 target_os = "fuchsia",
1177 target_os = "linux",
1178 )
1179 ))]
1180 #[cfg_attr(docsrs, doc(cfg(all(
1181 feature = "all",
1182 any(
1183 target_os = "android",
1184 // TODO: add FreeBSD.
1185 // target_os = "freebsd",
1186 target_os = "fuchsia",
1187 target_os = "linux",
1188 )
1189 ))))]
domain(&self) -> io::Result<Domain>1190 pub fn domain(&self) -> io::Result<Domain> {
1191 unsafe { getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_DOMAIN).map(Domain) }
1192 }
1193
1194 /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL`
1195 /// option on this socket.
1196 #[cfg(all(
1197 feature = "all",
1198 any(
1199 target_os = "android",
1200 target_os = "freebsd",
1201 target_os = "fuchsia",
1202 target_os = "linux",
1203 )
1204 ))]
1205 #[cfg_attr(
1206 docsrs,
1207 doc(cfg(all(
1208 feature = "all",
1209 any(
1210 target_os = "android",
1211 target_os = "freebsd",
1212 target_os = "fuchsia",
1213 target_os = "linux",
1214 )
1215 )))
1216 )]
protocol(&self) -> io::Result<Option<Protocol>>1217 pub fn protocol(&self) -> io::Result<Option<Protocol>> {
1218 unsafe {
1219 getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_PROTOCOL).map(|v| match v
1220 {
1221 0 => None,
1222 p => Some(Protocol(p)),
1223 })
1224 }
1225 }
1226
1227 /// Gets the value for the `SO_MARK` option on this socket.
1228 ///
1229 /// This value gets the socket mark field for each packet sent through
1230 /// this socket.
1231 ///
1232 /// On Linux this function requires the `CAP_NET_ADMIN` capability.
1233 #[cfg(all(
1234 feature = "all",
1235 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1236 ))]
1237 #[cfg_attr(
1238 docsrs,
1239 doc(cfg(all(
1240 feature = "all",
1241 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1242 )))
1243 )]
mark(&self) -> io::Result<u32>1244 pub fn mark(&self) -> io::Result<u32> {
1245 unsafe {
1246 getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_MARK)
1247 .map(|mark| mark as u32)
1248 }
1249 }
1250
1251 /// Sets the value for the `SO_MARK` option on this socket.
1252 ///
1253 /// This value sets the socket mark field for each packet sent through
1254 /// this socket. Changing the mark can be used for mark-based routing
1255 /// without netfilter or for packet filtering.
1256 ///
1257 /// On Linux this function requires the `CAP_NET_ADMIN` capability.
1258 #[cfg(all(
1259 feature = "all",
1260 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1261 ))]
1262 #[cfg_attr(
1263 docsrs,
1264 doc(cfg(all(
1265 feature = "all",
1266 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1267 )))
1268 )]
set_mark(&self, mark: u32) -> io::Result<()>1269 pub fn set_mark(&self, mark: u32) -> io::Result<()> {
1270 unsafe {
1271 setsockopt::<c_int>(
1272 self.as_raw(),
1273 libc::SOL_SOCKET,
1274 libc::SO_MARK,
1275 mark as c_int,
1276 )
1277 }
1278 }
1279
1280 /// Gets the value for the `SO_BINDTODEVICE` option on this socket.
1281 ///
1282 /// This value gets the socket binded device's interface name.
1283 #[cfg(all(
1284 feature = "all",
1285 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1286 ))]
1287 #[cfg_attr(
1288 docsrs,
1289 doc(cfg(all(
1290 feature = "all",
1291 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1292 )))
1293 )]
device(&self) -> io::Result<Option<Vec<u8>>>1294 pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
1295 // TODO: replace with `MaybeUninit::uninit_array` once stable.
1296 let mut buf: [MaybeUninit<u8>; libc::IFNAMSIZ] =
1297 unsafe { MaybeUninit::uninit().assume_init() };
1298 let mut len = buf.len() as libc::socklen_t;
1299 unsafe {
1300 syscall!(getsockopt(
1301 self.as_raw(),
1302 libc::SOL_SOCKET,
1303 libc::SO_BINDTODEVICE,
1304 buf.as_mut_ptr().cast(),
1305 &mut len,
1306 ))?;
1307 }
1308 if len == 0 {
1309 Ok(None)
1310 } else {
1311 let buf = &buf[..len as usize - 1];
1312 // TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
1313 Ok(Some(unsafe { &*(buf as *const [_] as *const [u8]) }.into()))
1314 }
1315 }
1316
1317 /// Sets the value for the `SO_BINDTODEVICE` option on this socket.
1318 ///
1319 /// If a socket is bound to an interface, only packets received from that
1320 /// particular interface are processed by the socket. Note that this only
1321 /// works for some socket types, particularly `AF_INET` sockets.
1322 ///
1323 /// If `interface` is `None` or an empty string it removes the binding.
1324 #[cfg(all(
1325 feature = "all",
1326 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1327 ))]
1328 #[cfg_attr(
1329 docsrs,
1330 doc(cfg(all(
1331 feature = "all",
1332 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1333 )))
1334 )]
bind_device(&self, interface: Option<&[u8]>) -> io::Result<()>1335 pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
1336 let (value, len) = if let Some(interface) = interface {
1337 (interface.as_ptr(), interface.len())
1338 } else {
1339 (ptr::null(), 0)
1340 };
1341 syscall!(setsockopt(
1342 self.as_raw(),
1343 libc::SOL_SOCKET,
1344 libc::SO_BINDTODEVICE,
1345 value.cast(),
1346 len as libc::socklen_t,
1347 ))
1348 .map(|_| ())
1349 }
1350
1351 /// Sets the value for `IP_BOUND_IF` option on this socket.
1352 ///
1353 /// If a socket is bound to an interface, only packets received from that
1354 /// particular interface are processed by the socket.
1355 ///
1356 /// If `interface` is `None`, the binding is removed. If the `interface`
1357 /// index is not valid, an error is returned.
1358 ///
1359 /// One can use `libc::if_nametoindex` to convert an interface alias to an
1360 /// index.
1361 #[cfg(all(feature = "all", target_vendor = "apple"))]
1362 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()>1363 pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
1364 let index = interface.map(NonZeroU32::get).unwrap_or(0);
1365 unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) }
1366 }
1367
1368 /// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index
1369 /// for the interface to which the socket is bound.
1370 ///
1371 /// Returns `None` if the socket is not bound to any interface, otherwise
1372 /// returns an interface index.
1373 #[cfg(all(feature = "all", target_vendor = "apple"))]
1374 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
device_index(&self) -> io::Result<Option<NonZeroU32>>1375 pub fn device_index(&self) -> io::Result<Option<NonZeroU32>> {
1376 let index =
1377 unsafe { getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF)? };
1378 Ok(NonZeroU32::new(index))
1379 }
1380
1381 /// Get the value of the `SO_INCOMING_CPU` option on this socket.
1382 ///
1383 /// For more information about this option, see [`set_cpu_affinity`].
1384 ///
1385 /// [`set_cpu_affinity`]: crate::Socket::set_cpu_affinity
1386 #[cfg(all(feature = "all", target_os = "linux"))]
1387 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
cpu_affinity(&self) -> io::Result<usize>1388 pub fn cpu_affinity(&self) -> io::Result<usize> {
1389 unsafe {
1390 getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_INCOMING_CPU)
1391 .map(|cpu| cpu as usize)
1392 }
1393 }
1394
1395 /// Set value for the `SO_INCOMING_CPU` option on this socket.
1396 ///
1397 /// Sets the CPU affinity of the socket.
1398 #[cfg(all(feature = "all", target_os = "linux"))]
1399 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
set_cpu_affinity(&self, cpu: usize) -> io::Result<()>1400 pub fn set_cpu_affinity(&self, cpu: usize) -> io::Result<()> {
1401 unsafe {
1402 setsockopt(
1403 self.as_raw(),
1404 libc::SOL_SOCKET,
1405 libc::SO_INCOMING_CPU,
1406 cpu as c_int,
1407 )
1408 }
1409 }
1410
1411 /// Get the value of the `SO_REUSEPORT` option on this socket.
1412 ///
1413 /// For more information about this option, see [`set_reuse_port`].
1414 ///
1415 /// [`set_reuse_port`]: crate::Socket::set_reuse_port
1416 #[cfg(all(
1417 feature = "all",
1418 not(any(target_os = "solaris", target_os = "illumos"))
1419 ))]
1420 #[cfg_attr(
1421 docsrs,
1422 doc(cfg(all(
1423 feature = "all",
1424 unix,
1425 not(any(target_os = "solaris", target_os = "illumos"))
1426 )))
1427 )]
reuse_port(&self) -> io::Result<bool>1428 pub fn reuse_port(&self) -> io::Result<bool> {
1429 unsafe {
1430 getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT)
1431 .map(|reuse| reuse != 0)
1432 }
1433 }
1434
1435 /// Set value for the `SO_REUSEPORT` option on this socket.
1436 ///
1437 /// This indicates that further calls to `bind` may allow reuse of local
1438 /// addresses. For IPv4 sockets this means that a socket may bind even when
1439 /// there's a socket already listening on this port.
1440 #[cfg(all(
1441 feature = "all",
1442 not(any(target_os = "solaris", target_os = "illumos"))
1443 ))]
1444 #[cfg_attr(
1445 docsrs,
1446 doc(cfg(all(
1447 feature = "all",
1448 unix,
1449 not(any(target_os = "solaris", target_os = "illumos"))
1450 )))
1451 )]
set_reuse_port(&self, reuse: bool) -> io::Result<()>1452 pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> {
1453 unsafe {
1454 setsockopt(
1455 self.as_raw(),
1456 libc::SOL_SOCKET,
1457 libc::SO_REUSEPORT,
1458 reuse as c_int,
1459 )
1460 }
1461 }
1462
1463 /// Get the value of the `IP_FREEBIND` option on this socket.
1464 ///
1465 /// For more information about this option, see [`set_freebind`].
1466 ///
1467 /// [`set_freebind`]: crate::Socket::set_freebind
1468 #[cfg(all(
1469 feature = "all",
1470 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1471 ))]
1472 #[cfg_attr(
1473 docsrs,
1474 doc(cfg(all(
1475 feature = "all",
1476 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1477 )))
1478 )]
freebind(&self) -> io::Result<bool>1479 pub fn freebind(&self) -> io::Result<bool> {
1480 unsafe {
1481 getsockopt::<c_int>(self.as_raw(), libc::SOL_IP, libc::IP_FREEBIND)
1482 .map(|freebind| freebind != 0)
1483 }
1484 }
1485
1486 /// Set value for the `IP_FREEBIND` option on this socket.
1487 ///
1488 /// If enabled, this boolean option allows binding to an IP address that is
1489 /// nonlocal or does not (yet) exist. This permits listening on a socket,
1490 /// without requiring the underlying network interface or the specified
1491 /// dynamic IP address to be up at the time that the application is trying
1492 /// to bind to it.
1493 #[cfg(all(
1494 feature = "all",
1495 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1496 ))]
1497 #[cfg_attr(
1498 docsrs,
1499 doc(cfg(all(
1500 feature = "all",
1501 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1502 )))
1503 )]
set_freebind(&self, freebind: bool) -> io::Result<()>1504 pub fn set_freebind(&self, freebind: bool) -> io::Result<()> {
1505 unsafe {
1506 setsockopt(
1507 self.as_raw(),
1508 libc::SOL_IP,
1509 libc::IP_FREEBIND,
1510 freebind as c_int,
1511 )
1512 }
1513 }
1514
1515 /// Get the value of the `IPV6_FREEBIND` option on this socket.
1516 ///
1517 /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
1518 /// Android/Linux. For more information about this option, see
1519 /// [`set_freebind`].
1520 ///
1521 /// [`set_freebind`]: crate::Socket::set_freebind
1522 #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1523 #[cfg_attr(
1524 docsrs,
1525 doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
1526 )]
freebind_ipv6(&self) -> io::Result<bool>1527 pub fn freebind_ipv6(&self) -> io::Result<bool> {
1528 unsafe {
1529 getsockopt::<c_int>(self.as_raw(), libc::SOL_IPV6, libc::IPV6_FREEBIND)
1530 .map(|freebind| freebind != 0)
1531 }
1532 }
1533
1534 /// Set value for the `IPV6_FREEBIND` option on this socket.
1535 ///
1536 /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
1537 /// Android/Linux. For more information about this option, see
1538 /// [`set_freebind`].
1539 ///
1540 /// [`set_freebind`]: crate::Socket::set_freebind
1541 ///
1542 /// # Examples
1543 ///
1544 /// On Linux:
1545 ///
1546 /// ```
1547 /// use socket2::{Domain, Socket, Type};
1548 /// use std::io::{self, Error, ErrorKind};
1549 ///
1550 /// fn enable_freebind(socket: &Socket) -> io::Result<()> {
1551 /// match socket.domain()? {
1552 /// Domain::IPV4 => socket.set_freebind(true)?,
1553 /// Domain::IPV6 => socket.set_freebind_ipv6(true)?,
1554 /// _ => return Err(Error::new(ErrorKind::Other, "unsupported domain")),
1555 /// };
1556 /// Ok(())
1557 /// }
1558 ///
1559 /// # fn main() -> io::Result<()> {
1560 /// # let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
1561 /// # enable_freebind(&socket)
1562 /// # }
1563 /// ```
1564 #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1565 #[cfg_attr(
1566 docsrs,
1567 doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
1568 )]
set_freebind_ipv6(&self, freebind: bool) -> io::Result<()>1569 pub fn set_freebind_ipv6(&self, freebind: bool) -> io::Result<()> {
1570 unsafe {
1571 setsockopt(
1572 self.as_raw(),
1573 libc::SOL_IPV6,
1574 libc::IPV6_FREEBIND,
1575 freebind as c_int,
1576 )
1577 }
1578 }
1579
1580 /// Copies data between a `file` and this socket using the `sendfile(2)`
1581 /// system call. Because this copying is done within the kernel,
1582 /// `sendfile()` is more efficient than the combination of `read(2)` and
1583 /// `write(2)`, which would require transferring data to and from user
1584 /// space.
1585 ///
1586 /// Different OSs support different kinds of `file`s, see the OS
1587 /// documentation for what kind of files are supported. Generally *regular*
1588 /// files are supported by all OSs.
1589 ///
1590 /// The `offset` is the absolute offset into the `file` to use as starting
1591 /// point.
1592 ///
1593 /// Depending on the OS this function *may* change the offset of `file`. For
1594 /// the best results reset the offset of the file before using it again.
1595 ///
1596 /// The `length` determines how many bytes to send, where a length of `None`
1597 /// means it will try to send all bytes.
1598 #[cfg(all(
1599 feature = "all",
1600 any(
1601 target_os = "android",
1602 target_os = "freebsd",
1603 target_os = "linux",
1604 target_vendor = "apple",
1605 )
1606 ))]
1607 #[cfg_attr(
1608 docsrs,
1609 doc(cfg(all(
1610 feature = "all",
1611 any(
1612 target_os = "android",
1613 target_os = "freebsd",
1614 target_os = "linux",
1615 target_vendor = "apple",
1616 )
1617 )))
1618 )]
sendfile<F>( &self, file: &F, offset: usize, length: Option<NonZeroUsize>, ) -> io::Result<usize> where F: AsRawFd,1619 pub fn sendfile<F>(
1620 &self,
1621 file: &F,
1622 offset: usize,
1623 length: Option<NonZeroUsize>,
1624 ) -> io::Result<usize>
1625 where
1626 F: AsRawFd,
1627 {
1628 self._sendfile(file.as_raw_fd(), offset as _, length)
1629 }
1630
1631 #[cfg(all(feature = "all", target_vendor = "apple"))]
_sendfile( &self, file: RawFd, offset: libc::off_t, length: Option<NonZeroUsize>, ) -> io::Result<usize>1632 fn _sendfile(
1633 &self,
1634 file: RawFd,
1635 offset: libc::off_t,
1636 length: Option<NonZeroUsize>,
1637 ) -> io::Result<usize> {
1638 // On macOS `length` is value-result parameter. It determines the number
1639 // of bytes to write and returns the number of bytes written.
1640 let mut length = match length {
1641 Some(n) => n.get() as libc::off_t,
1642 // A value of `0` means send all bytes.
1643 None => 0,
1644 };
1645 syscall!(sendfile(
1646 file,
1647 self.as_raw(),
1648 offset,
1649 &mut length,
1650 ptr::null_mut(),
1651 0,
1652 ))
1653 .map(|_| length as usize)
1654 }
1655
1656 #[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>1657 fn _sendfile(
1658 &self,
1659 file: RawFd,
1660 offset: libc::off_t,
1661 length: Option<NonZeroUsize>,
1662 ) -> io::Result<usize> {
1663 let count = match length {
1664 Some(n) => n.get() as libc::size_t,
1665 // The maximum the Linux kernel will write in a single call.
1666 None => 0x7ffff000, // 2,147,479,552 bytes.
1667 };
1668 let mut offset = offset;
1669 syscall!(sendfile(self.as_raw(), file, &mut offset, count)).map(|n| n as usize)
1670 }
1671
1672 #[cfg(all(feature = "all", target_os = "freebsd"))]
_sendfile( &self, file: RawFd, offset: libc::off_t, length: Option<NonZeroUsize>, ) -> io::Result<usize>1673 fn _sendfile(
1674 &self,
1675 file: RawFd,
1676 offset: libc::off_t,
1677 length: Option<NonZeroUsize>,
1678 ) -> io::Result<usize> {
1679 let nbytes = match length {
1680 Some(n) => n.get() as libc::size_t,
1681 // A value of `0` means send all bytes.
1682 None => 0,
1683 };
1684 let mut sbytes: libc::off_t = 0;
1685 syscall!(sendfile(
1686 file,
1687 self.as_raw(),
1688 offset,
1689 nbytes,
1690 ptr::null_mut(),
1691 &mut sbytes,
1692 0,
1693 ))
1694 .map(|_| sbytes as usize)
1695 }
1696
1697 /// Set the value of the `TCP_USER_TIMEOUT` option on this socket.
1698 ///
1699 /// If set, this specifies the maximum amount of time that transmitted data may remain
1700 /// unacknowledged or buffered data may remain untransmitted before TCP will forcibly close the
1701 /// corresponding connection.
1702 ///
1703 /// Setting `timeout` to `None` or a zero duration causes the system default timeouts to
1704 /// be used. If `timeout` in milliseconds is larger than `c_uint::MAX`, the timeout is clamped
1705 /// to `c_uint::MAX`. For example, when `c_uint` is a 32-bit value, this limits the timeout to
1706 /// approximately 49.71 days.
1707 #[cfg(all(
1708 feature = "all",
1709 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1710 ))]
1711 #[cfg_attr(
1712 docsrs,
1713 doc(cfg(all(
1714 feature = "all",
1715 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1716 )))
1717 )]
set_tcp_user_timeout(&self, timeout: Option<Duration>) -> io::Result<()>1718 pub fn set_tcp_user_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
1719 let timeout = timeout
1720 .map(|to| min(to.as_millis(), libc::c_uint::MAX as u128) as libc::c_uint)
1721 .unwrap_or(0);
1722 unsafe {
1723 setsockopt(
1724 self.as_raw(),
1725 libc::IPPROTO_TCP,
1726 libc::TCP_USER_TIMEOUT,
1727 timeout,
1728 )
1729 }
1730 }
1731
1732 /// Get the value of the `TCP_USER_TIMEOUT` option on this socket.
1733 ///
1734 /// For more information about this option, see [`set_tcp_user_timeout`].
1735 ///
1736 /// [`set_tcp_user_timeout`]: Socket::set_tcp_user_timeout
1737 #[cfg(all(
1738 feature = "all",
1739 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1740 ))]
1741 #[cfg_attr(
1742 docsrs,
1743 doc(cfg(all(
1744 feature = "all",
1745 any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1746 )))
1747 )]
tcp_user_timeout(&self) -> io::Result<Option<Duration>>1748 pub fn tcp_user_timeout(&self) -> io::Result<Option<Duration>> {
1749 unsafe {
1750 getsockopt::<libc::c_uint>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT)
1751 .map(|millis| {
1752 if millis == 0 {
1753 None
1754 } else {
1755 Some(Duration::from_millis(millis as u64))
1756 }
1757 })
1758 }
1759 }
1760 }
1761
1762 #[cfg_attr(docsrs, doc(cfg(unix)))]
1763 impl AsRawFd for crate::Socket {
as_raw_fd(&self) -> c_int1764 fn as_raw_fd(&self) -> c_int {
1765 self.as_raw()
1766 }
1767 }
1768
1769 #[cfg_attr(docsrs, doc(cfg(unix)))]
1770 impl IntoRawFd for crate::Socket {
into_raw_fd(self) -> c_int1771 fn into_raw_fd(self) -> c_int {
1772 self.into_raw()
1773 }
1774 }
1775
1776 #[cfg_attr(docsrs, doc(cfg(unix)))]
1777 impl FromRawFd for crate::Socket {
from_raw_fd(fd: c_int) -> crate::Socket1778 unsafe fn from_raw_fd(fd: c_int) -> crate::Socket {
1779 crate::Socket::from_raw(fd)
1780 }
1781 }
1782
1783 #[cfg(feature = "all")]
1784 from!(UnixStream, crate::Socket);
1785 #[cfg(feature = "all")]
1786 from!(UnixListener, crate::Socket);
1787 #[cfg(feature = "all")]
1788 from!(UnixDatagram, crate::Socket);
1789 #[cfg(feature = "all")]
1790 from!(crate::Socket, UnixStream);
1791 #[cfg(feature = "all")]
1792 from!(crate::Socket, UnixListener);
1793 #[cfg(feature = "all")]
1794 from!(crate::Socket, UnixDatagram);
1795
1796 #[test]
in_addr_convertion()1797 fn in_addr_convertion() {
1798 let ip = Ipv4Addr::new(127, 0, 0, 1);
1799 let raw = to_in_addr(&ip);
1800 // NOTE: `in_addr` is packed on NetBSD and it's unsafe to borrow.
1801 let a = raw.s_addr;
1802 assert_eq!(a, u32::from_ne_bytes([127, 0, 0, 1]));
1803 assert_eq!(from_in_addr(raw), ip);
1804
1805 let ip = Ipv4Addr::new(127, 34, 4, 12);
1806 let raw = to_in_addr(&ip);
1807 let a = raw.s_addr;
1808 assert_eq!(a, u32::from_ne_bytes([127, 34, 4, 12]));
1809 assert_eq!(from_in_addr(raw), ip);
1810 }
1811
1812 #[test]
in6_addr_convertion()1813 fn in6_addr_convertion() {
1814 let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7);
1815 let raw = to_in6_addr(&ip);
1816 let want = [32, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7];
1817 assert_eq!(raw.s6_addr, want);
1818 assert_eq!(from_in6_addr(raw), ip);
1819 }
1820