1 //! libc syscalls supporting `rustix::net`.
2
3 use super::super::c;
4 use super::super::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
5 #[cfg(unix)]
6 use super::addr::SocketAddrUnix;
7 use super::ext::{in6_addr_new, in_addr_new};
8 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
9 use super::read_sockaddr::initialize_family_to_unspec;
10 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
11 use super::read_sockaddr::{maybe_read_sockaddr_os, read_sockaddr_os};
12 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
13 use super::send_recv::{RecvFlags, SendFlags};
14 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
15 use super::types::{AcceptFlags, AddressFamily, Protocol, Shutdown, SocketFlags, SocketType};
16 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
17 use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
18 use crate::fd::{BorrowedFd, OwnedFd};
19 use crate::io;
20 use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
21 use crate::utils::as_ptr;
22 use core::convert::TryInto;
23 use core::mem::{size_of, MaybeUninit};
24 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
25 use core::ptr::null_mut;
26
27 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result<usize>28 pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result<usize> {
29 let nrecv = unsafe {
30 ret_send_recv(c::recv(
31 borrowed_fd(fd),
32 buf.as_mut_ptr().cast(),
33 send_recv_len(buf.len()),
34 flags.bits(),
35 ))?
36 };
37 Ok(nrecv as usize)
38 }
39
40 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize>41 pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize> {
42 let nwritten = unsafe {
43 ret_send_recv(c::send(
44 borrowed_fd(fd),
45 buf.as_ptr().cast(),
46 send_recv_len(buf.len()),
47 flags.bits(),
48 ))?
49 };
50 Ok(nwritten as usize)
51 }
52
53 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
recvfrom( fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags, ) -> io::Result<(usize, Option<SocketAddrAny>)>54 pub(crate) fn recvfrom(
55 fd: BorrowedFd<'_>,
56 buf: &mut [u8],
57 flags: RecvFlags,
58 ) -> io::Result<(usize, Option<SocketAddrAny>)> {
59 unsafe {
60 let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
61 let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
62
63 // `recvfrom` does not write to the storage if the socket is
64 // connection-oriented sockets, so we initialize the family field to
65 // `AF_UNSPEC` so that we can detect this case.
66 initialize_family_to_unspec(storage.as_mut_ptr());
67
68 let nread = ret_send_recv(c::recvfrom(
69 borrowed_fd(fd),
70 buf.as_mut_ptr().cast(),
71 send_recv_len(buf.len()),
72 flags.bits(),
73 storage.as_mut_ptr().cast(),
74 &mut len,
75 ))?;
76 Ok((
77 nread as usize,
78 maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
79 ))
80 }
81 }
82
83 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
sendto_v4( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrV4, ) -> io::Result<usize>84 pub(crate) fn sendto_v4(
85 fd: BorrowedFd<'_>,
86 buf: &[u8],
87 flags: SendFlags,
88 addr: &SocketAddrV4,
89 ) -> io::Result<usize> {
90 let nwritten = unsafe {
91 ret_send_recv(c::sendto(
92 borrowed_fd(fd),
93 buf.as_ptr().cast(),
94 send_recv_len(buf.len()),
95 flags.bits(),
96 as_ptr(&encode_sockaddr_v4(addr)).cast::<c::sockaddr>(),
97 size_of::<SocketAddrV4>() as _,
98 ))?
99 };
100 Ok(nwritten as usize)
101 }
102
103 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
sendto_v6( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrV6, ) -> io::Result<usize>104 pub(crate) fn sendto_v6(
105 fd: BorrowedFd<'_>,
106 buf: &[u8],
107 flags: SendFlags,
108 addr: &SocketAddrV6,
109 ) -> io::Result<usize> {
110 let nwritten = unsafe {
111 ret_send_recv(c::sendto(
112 borrowed_fd(fd),
113 buf.as_ptr().cast(),
114 send_recv_len(buf.len()),
115 flags.bits(),
116 as_ptr(&encode_sockaddr_v6(addr)).cast::<c::sockaddr>(),
117 size_of::<SocketAddrV6>() as _,
118 ))?
119 };
120 Ok(nwritten as usize)
121 }
122
123 #[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
sendto_unix( fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags, addr: &SocketAddrUnix, ) -> io::Result<usize>124 pub(crate) fn sendto_unix(
125 fd: BorrowedFd<'_>,
126 buf: &[u8],
127 flags: SendFlags,
128 addr: &SocketAddrUnix,
129 ) -> io::Result<usize> {
130 let nwritten = unsafe {
131 ret_send_recv(c::sendto(
132 borrowed_fd(fd),
133 buf.as_ptr().cast(),
134 send_recv_len(buf.len()),
135 flags.bits(),
136 as_ptr(&addr.unix).cast(),
137 addr.addr_len(),
138 ))?
139 };
140 Ok(nwritten as usize)
141 }
142
143 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
socket( domain: AddressFamily, type_: SocketType, protocol: Protocol, ) -> io::Result<OwnedFd>144 pub(crate) fn socket(
145 domain: AddressFamily,
146 type_: SocketType,
147 protocol: Protocol,
148 ) -> io::Result<OwnedFd> {
149 unsafe {
150 ret_owned_fd(c::socket(
151 domain.0 as c::c_int,
152 type_.0 as c::c_int,
153 protocol.0,
154 ))
155 }
156 }
157
158 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
socket_with( domain: AddressFamily, type_: SocketType, flags: SocketFlags, protocol: Protocol, ) -> io::Result<OwnedFd>159 pub(crate) fn socket_with(
160 domain: AddressFamily,
161 type_: SocketType,
162 flags: SocketFlags,
163 protocol: Protocol,
164 ) -> io::Result<OwnedFd> {
165 unsafe {
166 ret_owned_fd(c::socket(
167 domain.0 as c::c_int,
168 type_.0 as c::c_int | flags.bits(),
169 protocol.0,
170 ))
171 }
172 }
173
174 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()>175 pub(crate) fn bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
176 unsafe {
177 ret(c::bind(
178 borrowed_fd(sockfd),
179 as_ptr(&encode_sockaddr_v4(addr)).cast(),
180 size_of::<c::sockaddr_in>() as c::socklen_t,
181 ))
182 }
183 }
184
185 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()>186 pub(crate) fn bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
187 unsafe {
188 ret(c::bind(
189 borrowed_fd(sockfd),
190 as_ptr(&encode_sockaddr_v6(addr)).cast(),
191 size_of::<c::sockaddr_in6>() as c::socklen_t,
192 ))
193 }
194 }
195
196 #[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()>197 pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
198 unsafe {
199 ret(c::bind(
200 borrowed_fd(sockfd),
201 as_ptr(&addr.unix).cast(),
202 addr.addr_len(),
203 ))
204 }
205 }
206
207 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()>208 pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
209 unsafe {
210 ret(c::connect(
211 borrowed_fd(sockfd),
212 as_ptr(&encode_sockaddr_v4(addr)).cast(),
213 size_of::<c::sockaddr_in>() as c::socklen_t,
214 ))
215 }
216 }
217
218 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()>219 pub(crate) fn connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
220 unsafe {
221 ret(c::connect(
222 borrowed_fd(sockfd),
223 as_ptr(&encode_sockaddr_v6(addr)).cast(),
224 size_of::<c::sockaddr_in6>() as c::socklen_t,
225 ))
226 }
227 }
228
229 #[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()>230 pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
231 unsafe {
232 ret(c::connect(
233 borrowed_fd(sockfd),
234 as_ptr(&addr.unix).cast(),
235 addr.addr_len(),
236 ))
237 }
238 }
239
240 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()>241 pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
242 unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) }
243 }
244
245 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd>246 pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
247 unsafe {
248 let owned_fd = ret_owned_fd(c::accept(borrowed_fd(sockfd), null_mut(), null_mut()))?;
249 Ok(owned_fd)
250 }
251 }
252
253 #[cfg(not(any(
254 windows,
255 target_os = "haiku",
256 target_os = "ios",
257 target_os = "macos",
258 target_os = "redox",
259 target_os = "wasi",
260 )))]
accept_with(sockfd: BorrowedFd<'_>, flags: AcceptFlags) -> io::Result<OwnedFd>261 pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, flags: AcceptFlags) -> io::Result<OwnedFd> {
262 unsafe {
263 let owned_fd = ret_owned_fd(c::accept4(
264 borrowed_fd(sockfd),
265 null_mut(),
266 null_mut(),
267 flags.bits(),
268 ))?;
269 Ok(owned_fd)
270 }
271 }
272
273 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)>274 pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
275 unsafe {
276 let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
277 let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
278 let owned_fd = ret_owned_fd(c::accept(
279 borrowed_fd(sockfd),
280 storage.as_mut_ptr().cast(),
281 &mut len,
282 ))?;
283 Ok((
284 owned_fd,
285 maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
286 ))
287 }
288 }
289
290 #[cfg(not(any(
291 windows,
292 target_os = "haiku",
293 target_os = "ios",
294 target_os = "macos",
295 target_os = "redox",
296 target_os = "wasi",
297 )))]
acceptfrom_with( sockfd: BorrowedFd<'_>, flags: AcceptFlags, ) -> io::Result<(OwnedFd, Option<SocketAddrAny>)>298 pub(crate) fn acceptfrom_with(
299 sockfd: BorrowedFd<'_>,
300 flags: AcceptFlags,
301 ) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
302 unsafe {
303 let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
304 let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
305 let owned_fd = ret_owned_fd(c::accept4(
306 borrowed_fd(sockfd),
307 storage.as_mut_ptr().cast(),
308 &mut len,
309 flags.bits(),
310 ))?;
311 Ok((
312 owned_fd,
313 maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
314 ))
315 }
316 }
317
318 /// Darwin lacks `accept4`, but does have `accept`. We define
319 /// `AcceptFlags` to have no flags, so we can discard it here.
320 #[cfg(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos"))]
accept_with(sockfd: BorrowedFd<'_>, _flags: AcceptFlags) -> io::Result<OwnedFd>321 pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: AcceptFlags) -> io::Result<OwnedFd> {
322 accept(sockfd)
323 }
324
325 /// Darwin lacks `accept4`, but does have `accept`. We define
326 /// `AcceptFlags` to have no flags, so we can discard it here.
327 #[cfg(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos"))]
acceptfrom_with( sockfd: BorrowedFd<'_>, _flags: AcceptFlags, ) -> io::Result<(OwnedFd, Option<SocketAddrAny>)>328 pub(crate) fn acceptfrom_with(
329 sockfd: BorrowedFd<'_>,
330 _flags: AcceptFlags,
331 ) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
332 acceptfrom(sockfd)
333 }
334
335 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()>336 pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> {
337 unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) }
338 }
339
340 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny>341 pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> {
342 unsafe {
343 let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
344 let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
345 ret(c::getsockname(
346 borrowed_fd(sockfd),
347 storage.as_mut_ptr().cast(),
348 &mut len,
349 ))?;
350 Ok(read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()))
351 }
352 }
353
354 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>>355 pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> {
356 unsafe {
357 let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
358 let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
359 ret(c::getpeername(
360 borrowed_fd(sockfd),
361 storage.as_mut_ptr().cast(),
362 &mut len,
363 ))?;
364 Ok(maybe_read_sockaddr_os(
365 storage.as_ptr(),
366 len.try_into().unwrap(),
367 ))
368 }
369 }
370
371 #[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
socketpair( domain: AddressFamily, type_: SocketType, flags: SocketFlags, protocol: Protocol, ) -> io::Result<(OwnedFd, OwnedFd)>372 pub(crate) fn socketpair(
373 domain: AddressFamily,
374 type_: SocketType,
375 flags: SocketFlags,
376 protocol: Protocol,
377 ) -> io::Result<(OwnedFd, OwnedFd)> {
378 unsafe {
379 let mut fds = MaybeUninit::<[OwnedFd; 2]>::uninit();
380 ret(c::socketpair(
381 c::c_int::from(domain.0),
382 type_.0 as c::c_int | flags.bits(),
383 protocol.0,
384 fds.as_mut_ptr().cast::<c::c_int>(),
385 ))?;
386
387 let [fd0, fd1] = fds.assume_init();
388 Ok((fd0, fd1))
389 }
390 }
391
392 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
393 pub(crate) mod sockopt {
394 use super::{c, in6_addr_new, in_addr_new, BorrowedFd};
395 use crate::io;
396 use crate::net::sockopt::Timeout;
397 use crate::net::{Ipv4Addr, Ipv6Addr, SocketType};
398 use crate::utils::as_mut_ptr;
399 use core::convert::TryInto;
400 use core::time::Duration;
401 #[cfg(windows)]
402 use windows_sys::Win32::Foundation::BOOL;
403
404 // TODO: With Rust 1.53 we can use `Duration::ZERO` instead.
405 const DURATION_ZERO: Duration = Duration::from_secs(0);
406
407 #[inline]
getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T>408 fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T> {
409 use super::*;
410
411 let mut optlen = core::mem::size_of::<T>().try_into().unwrap();
412 debug_assert!(
413 optlen as usize >= core::mem::size_of::<c::c_int>(),
414 "Socket APIs don't ever use `bool` directly"
415 );
416
417 unsafe {
418 let mut value = core::mem::zeroed::<T>();
419 ret(c::getsockopt(
420 borrowed_fd(fd),
421 level,
422 optname,
423 as_mut_ptr(&mut value).cast(),
424 &mut optlen,
425 ))?;
426 // On Windows at least, `getsockopt` has been observed writing 1
427 // byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though
428 // Windows' documentation says that should write a 4-byte `BOOL`.
429 // So, we initialize the memory to zeros above, and just assert
430 // that `getsockopt` doesn't write too many bytes here.
431 assert!(
432 optlen as usize <= size_of::<T>(),
433 "unexpected getsockopt size"
434 );
435 Ok(value)
436 }
437 }
438
439 #[inline]
setsockopt<T: Copy>( fd: BorrowedFd<'_>, level: i32, optname: i32, value: T, ) -> io::Result<()>440 fn setsockopt<T: Copy>(
441 fd: BorrowedFd<'_>,
442 level: i32,
443 optname: i32,
444 value: T,
445 ) -> io::Result<()> {
446 use super::*;
447
448 let optlen = core::mem::size_of::<T>().try_into().unwrap();
449 debug_assert!(
450 optlen as usize >= core::mem::size_of::<c::c_int>(),
451 "Socket APIs don't ever use `bool` directly"
452 );
453
454 unsafe {
455 ret(c::setsockopt(
456 borrowed_fd(fd),
457 level,
458 optname,
459 as_ptr(&value).cast(),
460 optlen,
461 ))
462 }
463 }
464
465 #[inline]
get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType>466 pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> {
467 getsockopt(fd, c::SOL_SOCKET as _, c::SO_TYPE)
468 }
469
470 #[inline]
set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()>471 pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> {
472 setsockopt(
473 fd,
474 c::SOL_SOCKET as _,
475 c::SO_REUSEADDR,
476 from_bool(reuseaddr),
477 )
478 }
479
480 #[inline]
set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()>481 pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> {
482 setsockopt(
483 fd,
484 c::SOL_SOCKET as _,
485 c::SO_BROADCAST,
486 from_bool(broadcast),
487 )
488 }
489
490 #[inline]
get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool>491 pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> {
492 getsockopt(fd, c::SOL_SOCKET as _, c::SO_BROADCAST).map(to_bool)
493 }
494
495 #[inline]
set_socket_linger( fd: BorrowedFd<'_>, linger: Option<Duration>, ) -> io::Result<()>496 pub(crate) fn set_socket_linger(
497 fd: BorrowedFd<'_>,
498 linger: Option<Duration>,
499 ) -> io::Result<()> {
500 // Convert `linger` to seconds, rounding up.
501 let l_linger = if let Some(linger) = linger {
502 let mut l_linger = linger.as_secs();
503 if linger.subsec_nanos() != 0 {
504 l_linger = l_linger.checked_add(1).ok_or(io::Errno::INVAL)?;
505 }
506 l_linger.try_into().map_err(|_e| io::Errno::INVAL)?
507 } else {
508 0
509 };
510 let linger = c::linger {
511 l_onoff: linger.is_some() as _,
512 l_linger,
513 };
514 setsockopt(fd, c::SOL_SOCKET as _, c::SO_LINGER, linger)
515 }
516
517 #[inline]
get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>>518 pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> {
519 let linger: c::linger = getsockopt(fd, c::SOL_SOCKET as _, c::SO_LINGER)?;
520 // TODO: With Rust 1.50, this could use `.then`.
521 Ok(if linger.l_onoff != 0 {
522 Some(Duration::from_secs(linger.l_linger as u64))
523 } else {
524 None
525 })
526 }
527
528 #[cfg(any(target_os = "android", target_os = "linux"))]
529 #[inline]
set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()>530 pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> {
531 setsockopt(fd, c::SOL_SOCKET as _, c::SO_PASSCRED, from_bool(passcred))
532 }
533
534 #[cfg(any(target_os = "android", target_os = "linux"))]
535 #[inline]
get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool>536 pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> {
537 getsockopt(fd, c::SOL_SOCKET as _, c::SO_PASSCRED).map(to_bool)
538 }
539
540 #[inline]
set_socket_timeout( fd: BorrowedFd<'_>, id: Timeout, timeout: Option<Duration>, ) -> io::Result<()>541 pub(crate) fn set_socket_timeout(
542 fd: BorrowedFd<'_>,
543 id: Timeout,
544 timeout: Option<Duration>,
545 ) -> io::Result<()> {
546 let optname = match id {
547 Timeout::Recv => c::SO_RCVTIMEO,
548 Timeout::Send => c::SO_SNDTIMEO,
549 };
550
551 #[cfg(not(windows))]
552 let timeout = match timeout {
553 Some(timeout) => {
554 if timeout == DURATION_ZERO {
555 return Err(io::Errno::INVAL);
556 }
557
558 // Rust's musl libc bindings deprecated `time_t` while they
559 // transition to 64-bit `time_t`. What we want here is just
560 // "whatever type `timeval`'s `tv_sec` is", so we're ok using
561 // the deprecated type.
562 #[allow(deprecated)]
563 let tv_sec = timeout.as_secs().try_into().unwrap_or(c::time_t::MAX);
564
565 // `subsec_micros` rounds down, so we use `subsec_nanos` and
566 // manually round up.
567 let mut timeout = c::timeval {
568 tv_sec,
569 tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
570 };
571 if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
572 timeout.tv_usec = 1;
573 }
574 timeout
575 }
576 None => c::timeval {
577 tv_sec: 0,
578 tv_usec: 0,
579 },
580 };
581
582 #[cfg(windows)]
583 let timeout: u32 = match timeout {
584 Some(timeout) => {
585 if timeout == DURATION_ZERO {
586 return Err(io::Errno::INVAL);
587 }
588
589 // `as_millis` rounds down, so we use `as_nanos` and
590 // manually round up.
591 let mut timeout: u32 = ((timeout.as_nanos() + 999_999) / 1_000_000)
592 .try_into()
593 .map_err(|_convert_err| io::Errno::INVAL)?;
594 if timeout == 0 {
595 timeout = 1;
596 }
597 timeout
598 }
599 None => 0,
600 };
601
602 setsockopt(fd, c::SOL_SOCKET, optname, timeout)
603 }
604
605 #[inline]
get_socket_timeout( fd: BorrowedFd<'_>, id: Timeout, ) -> io::Result<Option<Duration>>606 pub(crate) fn get_socket_timeout(
607 fd: BorrowedFd<'_>,
608 id: Timeout,
609 ) -> io::Result<Option<Duration>> {
610 let optname = match id {
611 Timeout::Recv => c::SO_RCVTIMEO,
612 Timeout::Send => c::SO_SNDTIMEO,
613 };
614
615 #[cfg(not(windows))]
616 {
617 let timeout: c::timeval = getsockopt(fd, c::SOL_SOCKET, optname)?;
618 if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
619 Ok(None)
620 } else {
621 Ok(Some(
622 Duration::from_secs(timeout.tv_sec as u64)
623 + Duration::from_micros(timeout.tv_usec as u64),
624 ))
625 }
626 }
627
628 #[cfg(windows)]
629 {
630 let timeout: u32 = getsockopt(fd, c::SOL_SOCKET, optname)?;
631 if timeout == 0 {
632 Ok(None)
633 } else {
634 Ok(Some(Duration::from_millis(timeout as u64)))
635 }
636 }
637 }
638
639 #[inline]
set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()>640 pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
641 setsockopt(fd, c::IPPROTO_IP as _, c::IP_TTL, ttl)
642 }
643
644 #[inline]
get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>645 pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
646 getsockopt(fd, c::IPPROTO_IP as _, c::IP_TTL)
647 }
648
649 #[inline]
set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()>650 pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
651 setsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_V6ONLY, from_bool(only_v6))
652 }
653
654 #[inline]
get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool>655 pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
656 getsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_V6ONLY).map(to_bool)
657 }
658
659 #[inline]
set_ip_multicast_loop( fd: BorrowedFd<'_>, multicast_loop: bool, ) -> io::Result<()>660 pub(crate) fn set_ip_multicast_loop(
661 fd: BorrowedFd<'_>,
662 multicast_loop: bool,
663 ) -> io::Result<()> {
664 setsockopt(
665 fd,
666 c::IPPROTO_IP as _,
667 c::IP_MULTICAST_LOOP,
668 from_bool(multicast_loop),
669 )
670 }
671
672 #[inline]
get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>673 pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
674 getsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_LOOP).map(to_bool)
675 }
676
677 #[inline]
set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()>678 pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
679 setsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_TTL, multicast_ttl)
680 }
681
682 #[inline]
get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>683 pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
684 getsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_TTL)
685 }
686
687 #[inline]
set_ipv6_multicast_loop( fd: BorrowedFd<'_>, multicast_loop: bool, ) -> io::Result<()>688 pub(crate) fn set_ipv6_multicast_loop(
689 fd: BorrowedFd<'_>,
690 multicast_loop: bool,
691 ) -> io::Result<()> {
692 setsockopt(
693 fd,
694 c::IPPROTO_IPV6 as _,
695 c::IPV6_MULTICAST_LOOP,
696 from_bool(multicast_loop),
697 )
698 }
699
700 #[inline]
get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>701 pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
702 getsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_MULTICAST_LOOP).map(to_bool)
703 }
704
705 #[inline]
set_ipv6_multicast_hops( fd: BorrowedFd<'_>, multicast_hops: u32, ) -> io::Result<()>706 pub(crate) fn set_ipv6_multicast_hops(
707 fd: BorrowedFd<'_>,
708 multicast_hops: u32,
709 ) -> io::Result<()> {
710 setsockopt(
711 fd,
712 c::IPPROTO_IP as _,
713 c::IPV6_MULTICAST_LOOP,
714 multicast_hops,
715 )
716 }
717
718 #[inline]
get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32>719 pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
720 getsockopt(fd, c::IPPROTO_IP as _, c::IPV6_MULTICAST_LOOP)
721 }
722
723 #[inline]
set_ip_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>724 pub(crate) fn set_ip_add_membership(
725 fd: BorrowedFd<'_>,
726 multiaddr: &Ipv4Addr,
727 interface: &Ipv4Addr,
728 ) -> io::Result<()> {
729 let mreq = to_imr(multiaddr, interface);
730 setsockopt(fd, c::IPPROTO_IP as _, c::IP_ADD_MEMBERSHIP, mreq)
731 }
732
733 #[inline]
set_ipv6_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>734 pub(crate) fn set_ipv6_add_membership(
735 fd: BorrowedFd<'_>,
736 multiaddr: &Ipv6Addr,
737 interface: u32,
738 ) -> io::Result<()> {
739 #[cfg(not(any(
740 target_os = "dragonfly",
741 target_os = "freebsd",
742 target_os = "haiku",
743 target_os = "illumos",
744 target_os = "ios",
745 target_os = "l4re",
746 target_os = "macos",
747 target_os = "netbsd",
748 target_os = "openbsd",
749 target_os = "solaris",
750 )))]
751 use c::IPV6_ADD_MEMBERSHIP;
752 #[cfg(any(
753 target_os = "dragonfly",
754 target_os = "freebsd",
755 target_os = "haiku",
756 target_os = "illumos",
757 target_os = "ios",
758 target_os = "l4re",
759 target_os = "macos",
760 target_os = "netbsd",
761 target_os = "openbsd",
762 target_os = "solaris",
763 ))]
764 use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
765
766 let mreq = to_ipv6mr(multiaddr, interface);
767 setsockopt(fd, c::IPPROTO_IPV6 as _, IPV6_ADD_MEMBERSHIP, mreq)
768 }
769
770 #[inline]
set_ip_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>771 pub(crate) fn set_ip_drop_membership(
772 fd: BorrowedFd<'_>,
773 multiaddr: &Ipv4Addr,
774 interface: &Ipv4Addr,
775 ) -> io::Result<()> {
776 let mreq = to_imr(multiaddr, interface);
777 setsockopt(fd, c::IPPROTO_IP as _, c::IP_DROP_MEMBERSHIP, mreq)
778 }
779
780 #[inline]
set_ipv6_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>781 pub(crate) fn set_ipv6_drop_membership(
782 fd: BorrowedFd<'_>,
783 multiaddr: &Ipv6Addr,
784 interface: u32,
785 ) -> io::Result<()> {
786 #[cfg(not(any(
787 target_os = "dragonfly",
788 target_os = "freebsd",
789 target_os = "haiku",
790 target_os = "illumos",
791 target_os = "ios",
792 target_os = "l4re",
793 target_os = "macos",
794 target_os = "netbsd",
795 target_os = "openbsd",
796 target_os = "solaris",
797 )))]
798 use c::IPV6_DROP_MEMBERSHIP;
799 #[cfg(any(
800 target_os = "dragonfly",
801 target_os = "freebsd",
802 target_os = "haiku",
803 target_os = "illumos",
804 target_os = "ios",
805 target_os = "l4re",
806 target_os = "macos",
807 target_os = "netbsd",
808 target_os = "openbsd",
809 target_os = "solaris",
810 ))]
811 use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
812
813 let mreq = to_ipv6mr(multiaddr, interface);
814 setsockopt(fd, c::IPPROTO_IPV6 as _, IPV6_DROP_MEMBERSHIP, mreq)
815 }
816
817 #[inline]
set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()>818 pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
819 setsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY, from_bool(nodelay))
820 }
821
822 #[inline]
get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool>823 pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
824 getsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY).map(to_bool)
825 }
826
827 #[inline]
to_imr(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq828 fn to_imr(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
829 c::ip_mreq {
830 imr_multiaddr: to_imr_addr(multiaddr),
831 imr_interface: to_imr_addr(interface),
832 }
833 }
834
835 #[inline]
to_imr_addr(addr: &Ipv4Addr) -> c::in_addr836 fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
837 in_addr_new(u32::from_ne_bytes(addr.octets()))
838 }
839
840 #[inline]
to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq841 fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
842 c::ipv6_mreq {
843 ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
844 ipv6mr_interface: to_ipv6mr_interface(interface),
845 }
846 }
847
848 #[inline]
to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr849 fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
850 in6_addr_new(multiaddr.octets())
851 }
852
853 #[cfg(target_os = "android")]
854 #[inline]
to_ipv6mr_interface(interface: u32) -> c::c_int855 fn to_ipv6mr_interface(interface: u32) -> c::c_int {
856 interface as c::c_int
857 }
858
859 #[cfg(not(target_os = "android"))]
860 #[inline]
to_ipv6mr_interface(interface: u32) -> c::c_uint861 fn to_ipv6mr_interface(interface: u32) -> c::c_uint {
862 interface as c::c_uint
863 }
864
865 // `getsockopt` and `setsockopt` represent boolean values as integers.
866 #[cfg(not(windows))]
867 type RawSocketBool = c::c_int;
868 #[cfg(windows)]
869 type RawSocketBool = BOOL;
870
871 // Wrap `RawSocketBool` in a newtype to discourage misuse.
872 #[repr(transparent)]
873 #[derive(Copy, Clone)]
874 struct SocketBool(RawSocketBool);
875
876 // Convert from a `bool` to a `SocketBool`.
877 #[inline]
from_bool(value: bool) -> SocketBool878 fn from_bool(value: bool) -> SocketBool {
879 SocketBool(value as _)
880 }
881
882 // Convert from a `SocketBool` to a `bool`.
883 #[inline]
to_bool(value: SocketBool) -> bool884 fn to_bool(value: SocketBool) -> bool {
885 value.0 != 0
886 }
887 }
888