| 1 | use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; |
| 2 | |
| 3 | #[cfg (not(any(target_os = "espidf" , target_os = "nuttx" )))] |
| 4 | use crate::ffi::CStr; |
| 5 | use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; |
| 6 | use crate::net::{Shutdown, SocketAddr}; |
| 7 | use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; |
| 8 | use crate::sys::fd::FileDesc; |
| 9 | use crate::sys::net::{getsockopt, setsockopt}; |
| 10 | use crate::sys::pal::IsMinusOne; |
| 11 | use crate::sys_common::{AsInner, FromInner, IntoInner}; |
| 12 | use crate::time::{Duration, Instant}; |
| 13 | use crate::{cmp, mem}; |
| 14 | |
| 15 | cfg_if::cfg_if! { |
| 16 | if #[cfg(target_vendor = "apple" )] { |
| 17 | use libc::SO_LINGER_SEC as SO_LINGER; |
| 18 | } else { |
| 19 | use libc::SO_LINGER; |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | pub(super) use libc as netc; |
| 24 | |
| 25 | use super::{socket_addr_from_c, socket_addr_to_c}; |
| 26 | pub use crate::sys::{cvt, cvt_r}; |
| 27 | |
| 28 | #[expect (non_camel_case_types)] |
| 29 | pub type wrlen_t = size_t; |
| 30 | |
| 31 | pub struct Socket(FileDesc); |
| 32 | |
| 33 | pub fn init() {} |
| 34 | |
| 35 | pub fn cvt_gai(err: c_int) -> io::Result<()> { |
| 36 | if err == 0 { |
| 37 | return Ok(()); |
| 38 | } |
| 39 | |
| 40 | // We may need to trigger a glibc workaround. See on_resolver_failure() for details. |
| 41 | on_resolver_failure(); |
| 42 | |
| 43 | #[cfg (not(any(target_os = "espidf" , target_os = "nuttx" )))] |
| 44 | if err == libc::EAI_SYSTEM { |
| 45 | return Err(io::Error::last_os_error()); |
| 46 | } |
| 47 | |
| 48 | #[cfg (not(any(target_os = "espidf" , target_os = "nuttx" )))] |
| 49 | let detail = unsafe { |
| 50 | // We can't always expect a UTF-8 environment. When we don't get that luxury, |
| 51 | // it's better to give a low-quality error message than none at all. |
| 52 | CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() |
| 53 | }; |
| 54 | |
| 55 | #[cfg (any(target_os = "espidf" , target_os = "nuttx" ))] |
| 56 | let detail = "" ; |
| 57 | |
| 58 | Err(io::Error::new( |
| 59 | io::ErrorKind::Uncategorized, |
| 60 | &format!("failed to lookup address information: {detail}" )[..], |
| 61 | )) |
| 62 | } |
| 63 | |
| 64 | impl Socket { |
| 65 | pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { |
| 66 | let fam = match *addr { |
| 67 | SocketAddr::V4(..) => libc::AF_INET, |
| 68 | SocketAddr::V6(..) => libc::AF_INET6, |
| 69 | }; |
| 70 | Socket::new_raw(fam, ty) |
| 71 | } |
| 72 | |
| 73 | pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { |
| 74 | unsafe { |
| 75 | cfg_if::cfg_if! { |
| 76 | if #[cfg(any( |
| 77 | target_os = "android" , |
| 78 | target_os = "dragonfly" , |
| 79 | target_os = "freebsd" , |
| 80 | target_os = "illumos" , |
| 81 | target_os = "hurd" , |
| 82 | target_os = "linux" , |
| 83 | target_os = "netbsd" , |
| 84 | target_os = "openbsd" , |
| 85 | target_os = "cygwin" , |
| 86 | target_os = "nto" , |
| 87 | target_os = "solaris" , |
| 88 | ))] { |
| 89 | // On platforms that support it we pass the SOCK_CLOEXEC |
| 90 | // flag to atomically create the socket and set it as |
| 91 | // CLOEXEC. On Linux this was added in 2.6.27. |
| 92 | let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; |
| 93 | let socket = Socket(FileDesc::from_raw_fd(fd)); |
| 94 | |
| 95 | // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` |
| 96 | // flag to disable `SIGPIPE` emission on socket. |
| 97 | #[cfg (any(target_os = "freebsd" , target_os = "netbsd" , target_os = "dragonfly" ))] |
| 98 | setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; |
| 99 | |
| 100 | Ok(socket) |
| 101 | } else { |
| 102 | let fd = cvt(libc::socket(fam, ty, 0))?; |
| 103 | let fd = FileDesc::from_raw_fd(fd); |
| 104 | fd.set_cloexec()?; |
| 105 | let socket = Socket(fd); |
| 106 | |
| 107 | // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` |
| 108 | // flag to disable `SIGPIPE` emission on socket. |
| 109 | #[cfg (target_vendor = "apple" )] |
| 110 | setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; |
| 111 | |
| 112 | Ok(socket) |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | #[cfg (not(target_os = "vxworks" ))] |
| 119 | pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { |
| 120 | unsafe { |
| 121 | let mut fds = [0, 0]; |
| 122 | |
| 123 | cfg_if::cfg_if! { |
| 124 | if #[cfg(any( |
| 125 | target_os = "android" , |
| 126 | target_os = "dragonfly" , |
| 127 | target_os = "freebsd" , |
| 128 | target_os = "illumos" , |
| 129 | target_os = "linux" , |
| 130 | target_os = "hurd" , |
| 131 | target_os = "netbsd" , |
| 132 | target_os = "openbsd" , |
| 133 | target_os = "cygwin" , |
| 134 | target_os = "nto" , |
| 135 | ))] { |
| 136 | // Like above, set cloexec atomically |
| 137 | cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; |
| 138 | Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) |
| 139 | } else { |
| 140 | cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; |
| 141 | let a = FileDesc::from_raw_fd(fds[0]); |
| 142 | let b = FileDesc::from_raw_fd(fds[1]); |
| 143 | a.set_cloexec()?; |
| 144 | b.set_cloexec()?; |
| 145 | Ok((Socket(a), Socket(b))) |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | #[cfg (target_os = "vxworks" )] |
| 152 | pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { |
| 153 | unimplemented!() |
| 154 | } |
| 155 | |
| 156 | pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { |
| 157 | let (addr, len) = socket_addr_to_c(addr); |
| 158 | loop { |
| 159 | let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; |
| 160 | if result.is_minus_one() { |
| 161 | let err = crate::sys::os::errno(); |
| 162 | match err { |
| 163 | libc::EINTR => continue, |
| 164 | libc::EISCONN => return Ok(()), |
| 165 | _ => return Err(io::Error::from_raw_os_error(err)), |
| 166 | } |
| 167 | } |
| 168 | return Ok(()); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { |
| 173 | self.set_nonblocking(true)?; |
| 174 | let r = unsafe { |
| 175 | let (addr, len) = socket_addr_to_c(addr); |
| 176 | cvt(libc::connect(self.as_raw_fd(), addr.as_ptr(), len)) |
| 177 | }; |
| 178 | self.set_nonblocking(false)?; |
| 179 | |
| 180 | match r { |
| 181 | Ok(_) => return Ok(()), |
| 182 | // there's no ErrorKind for EINPROGRESS :( |
| 183 | Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} |
| 184 | Err(e) => return Err(e), |
| 185 | } |
| 186 | |
| 187 | let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; |
| 188 | |
| 189 | if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { |
| 190 | return Err(io::Error::ZERO_TIMEOUT); |
| 191 | } |
| 192 | |
| 193 | let start = Instant::now(); |
| 194 | |
| 195 | loop { |
| 196 | let elapsed = start.elapsed(); |
| 197 | if elapsed >= timeout { |
| 198 | return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out" )); |
| 199 | } |
| 200 | |
| 201 | let timeout = timeout - elapsed; |
| 202 | let mut timeout = timeout |
| 203 | .as_secs() |
| 204 | .saturating_mul(1_000) |
| 205 | .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); |
| 206 | if timeout == 0 { |
| 207 | timeout = 1; |
| 208 | } |
| 209 | |
| 210 | let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; |
| 211 | |
| 212 | match unsafe { libc::poll(&mut pollfd, 1, timeout) } { |
| 213 | -1 => { |
| 214 | let err = io::Error::last_os_error(); |
| 215 | if !err.is_interrupted() { |
| 216 | return Err(err); |
| 217 | } |
| 218 | } |
| 219 | 0 => {} |
| 220 | _ => { |
| 221 | if cfg!(target_os = "vxworks" ) { |
| 222 | // VxWorks poll does not return POLLHUP or POLLERR in revents. Check if the |
| 223 | // connection actually succeeded and return ok only when the socket is |
| 224 | // ready and no errors were found. |
| 225 | if let Some(e) = self.take_error()? { |
| 226 | return Err(e); |
| 227 | } |
| 228 | } else { |
| 229 | // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look |
| 230 | // for POLLHUP or POLLERR rather than read readiness |
| 231 | if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { |
| 232 | let e = self.take_error()?.unwrap_or_else(|| { |
| 233 | io::const_error!( |
| 234 | io::ErrorKind::Uncategorized, |
| 235 | "no error set after POLLHUP" , |
| 236 | ) |
| 237 | }); |
| 238 | return Err(e); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | return Ok(()); |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> { |
| 249 | // Unfortunately the only known way right now to accept a socket and |
| 250 | // atomically set the CLOEXEC flag is to use the `accept4` syscall on |
| 251 | // platforms that support it. On Linux, this was added in 2.6.28, |
| 252 | // glibc 2.10 and musl 0.9.5. |
| 253 | cfg_if::cfg_if! { |
| 254 | if #[cfg(any( |
| 255 | target_os = "android" , |
| 256 | target_os = "dragonfly" , |
| 257 | target_os = "freebsd" , |
| 258 | target_os = "illumos" , |
| 259 | target_os = "linux" , |
| 260 | target_os = "hurd" , |
| 261 | target_os = "netbsd" , |
| 262 | target_os = "openbsd" , |
| 263 | target_os = "cygwin" , |
| 264 | ))] { |
| 265 | unsafe { |
| 266 | let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; |
| 267 | Ok(Socket(FileDesc::from_raw_fd(fd))) |
| 268 | } |
| 269 | } else { |
| 270 | unsafe { |
| 271 | let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; |
| 272 | let fd = FileDesc::from_raw_fd(fd); |
| 273 | fd.set_cloexec()?; |
| 274 | Ok(Socket(fd)) |
| 275 | } |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | pub fn duplicate(&self) -> io::Result<Socket> { |
| 281 | self.0.duplicate().map(Socket) |
| 282 | } |
| 283 | |
| 284 | fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { |
| 285 | let ret = cvt(unsafe { |
| 286 | libc::recv( |
| 287 | self.as_raw_fd(), |
| 288 | buf.as_mut().as_mut_ptr() as *mut c_void, |
| 289 | buf.capacity(), |
| 290 | flags, |
| 291 | ) |
| 292 | })?; |
| 293 | unsafe { |
| 294 | buf.advance_unchecked(ret as usize); |
| 295 | } |
| 296 | Ok(()) |
| 297 | } |
| 298 | |
| 299 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { |
| 300 | let mut buf = BorrowedBuf::from(buf); |
| 301 | self.recv_with_flags(buf.unfilled(), 0)?; |
| 302 | Ok(buf.len()) |
| 303 | } |
| 304 | |
| 305 | pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { |
| 306 | let mut buf = BorrowedBuf::from(buf); |
| 307 | self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; |
| 308 | Ok(buf.len()) |
| 309 | } |
| 310 | |
| 311 | pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { |
| 312 | self.recv_with_flags(buf, 0) |
| 313 | } |
| 314 | |
| 315 | pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
| 316 | self.0.read_vectored(bufs) |
| 317 | } |
| 318 | |
| 319 | #[inline ] |
| 320 | pub fn is_read_vectored(&self) -> bool { |
| 321 | self.0.is_read_vectored() |
| 322 | } |
| 323 | |
| 324 | fn recv_from_with_flags( |
| 325 | &self, |
| 326 | buf: &mut [u8], |
| 327 | flags: c_int, |
| 328 | ) -> io::Result<(usize, SocketAddr)> { |
| 329 | // The `recvfrom` function will fill in the storage with the address, |
| 330 | // so we don't need to zero it here. |
| 331 | // reference: https://linux.die.net/man/2/recvfrom |
| 332 | let mut storage: mem::MaybeUninit<libc::sockaddr_storage> = mem::MaybeUninit::uninit(); |
| 333 | let mut addrlen = size_of_val(&storage) as libc::socklen_t; |
| 334 | |
| 335 | let n = cvt(unsafe { |
| 336 | libc::recvfrom( |
| 337 | self.as_raw_fd(), |
| 338 | buf.as_mut_ptr() as *mut c_void, |
| 339 | buf.len(), |
| 340 | flags, |
| 341 | (&raw mut storage) as *mut _, |
| 342 | &mut addrlen, |
| 343 | ) |
| 344 | })?; |
| 345 | Ok((n as usize, unsafe { socket_addr_from_c(storage.as_ptr(), addrlen as usize)? })) |
| 346 | } |
| 347 | |
| 348 | pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { |
| 349 | self.recv_from_with_flags(buf, 0) |
| 350 | } |
| 351 | |
| 352 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
| 353 | pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { |
| 354 | let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; |
| 355 | Ok(n as usize) |
| 356 | } |
| 357 | |
| 358 | pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { |
| 359 | self.recv_from_with_flags(buf, MSG_PEEK) |
| 360 | } |
| 361 | |
| 362 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { |
| 363 | self.0.write(buf) |
| 364 | } |
| 365 | |
| 366 | pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
| 367 | self.0.write_vectored(bufs) |
| 368 | } |
| 369 | |
| 370 | #[inline ] |
| 371 | pub fn is_write_vectored(&self) -> bool { |
| 372 | self.0.is_write_vectored() |
| 373 | } |
| 374 | |
| 375 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
| 376 | pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { |
| 377 | let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; |
| 378 | Ok(n as usize) |
| 379 | } |
| 380 | |
| 381 | pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> { |
| 382 | let timeout = match dur { |
| 383 | Some(dur) => { |
| 384 | if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { |
| 385 | return Err(io::Error::ZERO_TIMEOUT); |
| 386 | } |
| 387 | |
| 388 | let secs = if dur.as_secs() > libc::time_t::MAX as u64 { |
| 389 | libc::time_t::MAX |
| 390 | } else { |
| 391 | dur.as_secs() as libc::time_t |
| 392 | }; |
| 393 | let mut timeout = libc::timeval { |
| 394 | tv_sec: secs, |
| 395 | tv_usec: dur.subsec_micros() as libc::suseconds_t, |
| 396 | }; |
| 397 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { |
| 398 | timeout.tv_usec = 1; |
| 399 | } |
| 400 | timeout |
| 401 | } |
| 402 | None => libc::timeval { tv_sec: 0, tv_usec: 0 }, |
| 403 | }; |
| 404 | setsockopt(self, libc::SOL_SOCKET, kind, timeout) |
| 405 | } |
| 406 | |
| 407 | pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> { |
| 408 | let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; |
| 409 | if raw.tv_sec == 0 && raw.tv_usec == 0 { |
| 410 | Ok(None) |
| 411 | } else { |
| 412 | let sec = raw.tv_sec as u64; |
| 413 | let nsec = (raw.tv_usec as u32) * 1000; |
| 414 | Ok(Some(Duration::new(sec, nsec))) |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { |
| 419 | let how = match how { |
| 420 | Shutdown::Write => libc::SHUT_WR, |
| 421 | Shutdown::Read => libc::SHUT_RD, |
| 422 | Shutdown::Both => libc::SHUT_RDWR, |
| 423 | }; |
| 424 | cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; |
| 425 | Ok(()) |
| 426 | } |
| 427 | |
| 428 | #[cfg (not(target_os = "cygwin" ))] |
| 429 | pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> { |
| 430 | let linger = libc::linger { |
| 431 | l_onoff: linger.is_some() as libc::c_int, |
| 432 | l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, |
| 433 | }; |
| 434 | |
| 435 | setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) |
| 436 | } |
| 437 | |
| 438 | #[cfg (target_os = "cygwin" )] |
| 439 | pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> { |
| 440 | let linger = libc::linger { |
| 441 | l_onoff: linger.is_some() as libc::c_ushort, |
| 442 | l_linger: linger.unwrap_or_default().as_secs() as libc::c_ushort, |
| 443 | }; |
| 444 | |
| 445 | setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) |
| 446 | } |
| 447 | |
| 448 | pub fn linger(&self) -> io::Result<Option<Duration>> { |
| 449 | let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; |
| 450 | |
| 451 | Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) |
| 452 | } |
| 453 | |
| 454 | pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { |
| 455 | setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) |
| 456 | } |
| 457 | |
| 458 | pub fn nodelay(&self) -> io::Result<bool> { |
| 459 | let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; |
| 460 | Ok(raw != 0) |
| 461 | } |
| 462 | |
| 463 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
| 464 | pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { |
| 465 | setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) |
| 466 | } |
| 467 | |
| 468 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
| 469 | pub fn quickack(&self) -> io::Result<bool> { |
| 470 | let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; |
| 471 | Ok(raw != 0) |
| 472 | } |
| 473 | |
| 474 | // bionic libc makes no use of this flag |
| 475 | #[cfg (target_os = "linux" )] |
| 476 | pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { |
| 477 | setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) |
| 478 | } |
| 479 | |
| 480 | #[cfg (target_os = "linux" )] |
| 481 | pub fn deferaccept(&self) -> io::Result<u32> { |
| 482 | let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; |
| 483 | Ok(raw as u32) |
| 484 | } |
| 485 | |
| 486 | #[cfg (any(target_os = "freebsd" , target_os = "netbsd" ))] |
| 487 | pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { |
| 488 | if !name.to_bytes().is_empty() { |
| 489 | const AF_NAME_MAX: usize = 16; |
| 490 | let mut buf = [0; AF_NAME_MAX]; |
| 491 | for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { |
| 492 | *dst = *src as libc::c_char; |
| 493 | } |
| 494 | let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; |
| 495 | arg.af_name = buf; |
| 496 | setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) |
| 497 | } else { |
| 498 | setsockopt( |
| 499 | self, |
| 500 | libc::SOL_SOCKET, |
| 501 | libc::SO_ACCEPTFILTER, |
| 502 | core::ptr::null_mut() as *mut c_void, |
| 503 | ) |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | #[cfg (any(target_os = "freebsd" , target_os = "netbsd" ))] |
| 508 | pub fn acceptfilter(&self) -> io::Result<&CStr> { |
| 509 | let arg: libc::accept_filter_arg = |
| 510 | getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; |
| 511 | let s: &[u8] = |
| 512 | unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; |
| 513 | let name = CStr::from_bytes_with_nul(s).unwrap(); |
| 514 | Ok(name) |
| 515 | } |
| 516 | |
| 517 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
| 518 | pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { |
| 519 | setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) |
| 520 | } |
| 521 | |
| 522 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
| 523 | pub fn passcred(&self) -> io::Result<bool> { |
| 524 | let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; |
| 525 | Ok(passcred != 0) |
| 526 | } |
| 527 | |
| 528 | #[cfg (target_os = "netbsd" )] |
| 529 | pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { |
| 530 | setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) |
| 531 | } |
| 532 | |
| 533 | #[cfg (target_os = "netbsd" )] |
| 534 | pub fn local_creds(&self) -> io::Result<bool> { |
| 535 | let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; |
| 536 | Ok(local_creds != 0) |
| 537 | } |
| 538 | |
| 539 | #[cfg (target_os = "freebsd" )] |
| 540 | pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { |
| 541 | setsockopt( |
| 542 | self, |
| 543 | libc::AF_LOCAL, |
| 544 | libc::LOCAL_CREDS_PERSISTENT, |
| 545 | local_creds_persistent as libc::c_int, |
| 546 | ) |
| 547 | } |
| 548 | |
| 549 | #[cfg (target_os = "freebsd" )] |
| 550 | pub fn local_creds_persistent(&self) -> io::Result<bool> { |
| 551 | let local_creds_persistent: libc::c_int = |
| 552 | getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; |
| 553 | Ok(local_creds_persistent != 0) |
| 554 | } |
| 555 | |
| 556 | #[cfg (not(any(target_os = "solaris" , target_os = "illumos" , target_os = "vita" )))] |
| 557 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { |
| 558 | let mut nonblocking = nonblocking as libc::c_int; |
| 559 | cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) |
| 560 | } |
| 561 | |
| 562 | #[cfg (target_os = "vita" )] |
| 563 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { |
| 564 | let option = nonblocking as libc::c_int; |
| 565 | setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) |
| 566 | } |
| 567 | |
| 568 | #[cfg (any(target_os = "solaris" , target_os = "illumos" ))] |
| 569 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { |
| 570 | // FIONBIO is inadequate for sockets on illumos/Solaris, so use the |
| 571 | // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. |
| 572 | self.0.set_nonblocking(nonblocking) |
| 573 | } |
| 574 | |
| 575 | #[cfg (any(target_os = "linux" , target_os = "freebsd" , target_os = "openbsd" ))] |
| 576 | pub fn set_mark(&self, mark: u32) -> io::Result<()> { |
| 577 | #[cfg (target_os = "linux" )] |
| 578 | let option = libc::SO_MARK; |
| 579 | #[cfg (target_os = "freebsd" )] |
| 580 | let option = libc::SO_USER_COOKIE; |
| 581 | #[cfg (target_os = "openbsd" )] |
| 582 | let option = libc::SO_RTABLE; |
| 583 | setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) |
| 584 | } |
| 585 | |
| 586 | pub fn take_error(&self) -> io::Result<Option<io::Error>> { |
| 587 | let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; |
| 588 | if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } |
| 589 | } |
| 590 | |
| 591 | // This is used by sys_common code to abstract over Windows and Unix. |
| 592 | pub fn as_raw(&self) -> RawFd { |
| 593 | self.as_raw_fd() |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | impl AsInner<FileDesc> for Socket { |
| 598 | #[inline ] |
| 599 | fn as_inner(&self) -> &FileDesc { |
| 600 | &self.0 |
| 601 | } |
| 602 | } |
| 603 | |
| 604 | impl IntoInner<FileDesc> for Socket { |
| 605 | fn into_inner(self) -> FileDesc { |
| 606 | self.0 |
| 607 | } |
| 608 | } |
| 609 | |
| 610 | impl FromInner<FileDesc> for Socket { |
| 611 | fn from_inner(file_desc: FileDesc) -> Self { |
| 612 | Self(file_desc) |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | impl AsFd for Socket { |
| 617 | fn as_fd(&self) -> BorrowedFd<'_> { |
| 618 | self.0.as_fd() |
| 619 | } |
| 620 | } |
| 621 | |
| 622 | impl AsRawFd for Socket { |
| 623 | #[inline ] |
| 624 | fn as_raw_fd(&self) -> RawFd { |
| 625 | self.0.as_raw_fd() |
| 626 | } |
| 627 | } |
| 628 | |
| 629 | impl IntoRawFd for Socket { |
| 630 | fn into_raw_fd(self) -> RawFd { |
| 631 | self.0.into_raw_fd() |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | impl FromRawFd for Socket { |
| 636 | unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { |
| 637 | Self(FromRawFd::from_raw_fd(raw_fd)) |
| 638 | } |
| 639 | } |
| 640 | |
| 641 | // In versions of glibc prior to 2.26, there's a bug where the DNS resolver |
| 642 | // will cache the contents of /etc/resolv.conf, so changes to that file on disk |
| 643 | // can be ignored by a long-running program. That can break DNS lookups on e.g. |
| 644 | // laptops where the network comes and goes. See |
| 645 | // https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some |
| 646 | // distros including Debian have patched glibc to fix this for a long time. |
| 647 | // |
| 648 | // A workaround for this bug is to call the res_init libc function, to clear |
| 649 | // the cached configs. Unfortunately, while we believe glibc's implementation |
| 650 | // of res_init is thread-safe, we know that other implementations are not |
| 651 | // (https://github.com/rust-lang/rust/issues/43592). Code here in std could |
| 652 | // try to synchronize its res_init calls with a Mutex, but that wouldn't |
| 653 | // protect programs that call into libc in other ways. So instead of calling |
| 654 | // res_init unconditionally, we call it only when we detect we're linking |
| 655 | // against glibc version < 2.26. (That is, when we both know its needed and |
| 656 | // believe it's thread-safe). |
| 657 | #[cfg (all(target_os = "linux" , target_env = "gnu" ))] |
| 658 | fn on_resolver_failure() { |
| 659 | use crate::sys; |
| 660 | |
| 661 | // If the version fails to parse, we treat it the same as "not glibc". |
| 662 | if let Some(version: (usize, usize)) = sys::os::glibc_version() { |
| 663 | if version < (2, 26) { |
| 664 | unsafe { libc::res_init() }; |
| 665 | } |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | #[cfg (not(all(target_os = "linux" , target_env = "gnu" )))] |
| 670 | fn on_resolver_failure() {} |
| 671 | |