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 | |