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