1//! libc syscalls supporting `rustix::net`. 2 3use super::super::c; 4use super::super::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len}; 5#[cfg(unix)] 6use super::addr::SocketAddrUnix; 7use super::ext::{in6_addr_new, in_addr_new}; 8#[cfg(not(any(target_os = "redox", target_os = "wasi")))] 9use super::read_sockaddr::initialize_family_to_unspec; 10#[cfg(not(any(target_os = "redox", target_os = "wasi")))] 11use super::read_sockaddr::{maybe_read_sockaddr_os, read_sockaddr_os}; 12#[cfg(not(any(target_os = "redox", target_os = "wasi")))] 13use super::send_recv::{RecvFlags, SendFlags}; 14#[cfg(not(any(target_os = "redox", target_os = "wasi")))] 15use super::types::{AcceptFlags, AddressFamily, Protocol, Shutdown, SocketFlags, SocketType}; 16#[cfg(not(any(target_os = "redox", target_os = "wasi")))] 17use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; 18use crate::fd::{BorrowedFd, OwnedFd}; 19use crate::io; 20use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6}; 21use crate::utils::as_ptr; 22use core::convert::TryInto; 23use core::mem::{size_of, MaybeUninit}; 24#[cfg(not(any(target_os = "redox", target_os = "wasi")))] 25use core::ptr::null_mut; 26 27#[cfg(not(any(target_os = "redox", target_os = "wasi")))] 28pub(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")))] 41pub(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")))] 54pub(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")))] 84pub(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")))] 104pub(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")))] 124pub(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")))] 144pub(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")))] 159pub(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")))] 175pub(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")))] 186pub(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")))] 197pub(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")))] 208pub(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")))] 219pub(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")))] 230pub(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")))] 241pub(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")))] 246pub(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)))] 261pub(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")))] 274pub(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)))] 298pub(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"))] 321pub(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"))] 328pub(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")))] 336pub(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")))] 341pub(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")))] 355pub(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")))] 372pub(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")))] 393pub(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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 828 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] 836 fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr { 837 in_addr_new(u32::from_ne_bytes(addr.octets())) 838 } 839 840 #[inline] 841 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] 849 fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr { 850 in6_addr_new(multiaddr.octets()) 851 } 852 853 #[cfg(target_os = "android")] 854 #[inline] 855 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] 861 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] 878 fn from_bool(value: bool) -> SocketBool { 879 SocketBool(value as _) 880 } 881 882 // Convert from a `SocketBool` to a `bool`. 883 #[inline] 884 fn to_bool(value: SocketBool) -> bool { 885 value.0 != 0 886 } 887} 888