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