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