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