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