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::Error::ZERO_TIMEOUT); |
179 | } |
180 | |
181 | let start = Instant::now(); |
182 | |
183 | loop { |
184 | let elapsed = start.elapsed(); |
185 | if elapsed >= timeout { |
186 | return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out" )); |
187 | } |
188 | |
189 | let timeout = timeout - elapsed; |
190 | let mut timeout = timeout |
191 | .as_secs() |
192 | .saturating_mul(1_000) |
193 | .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); |
194 | if timeout == 0 { |
195 | timeout = 1; |
196 | } |
197 | |
198 | let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; |
199 | |
200 | match unsafe { libc::poll(&mut pollfd, 1, timeout) } { |
201 | -1 => { |
202 | let err = io::Error::last_os_error(); |
203 | if !err.is_interrupted() { |
204 | return Err(err); |
205 | } |
206 | } |
207 | 0 => {} |
208 | _ => { |
209 | // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look |
210 | // for POLLHUP rather than read readiness |
211 | if pollfd.revents & libc::POLLHUP != 0 { |
212 | let e = self.take_error()?.unwrap_or_else(|| { |
213 | io::const_io_error!( |
214 | io::ErrorKind::Uncategorized, |
215 | "no error set after POLLHUP" , |
216 | ) |
217 | }); |
218 | return Err(e); |
219 | } |
220 | |
221 | return Ok(()); |
222 | } |
223 | } |
224 | } |
225 | } |
226 | |
227 | pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> { |
228 | // Unfortunately the only known way right now to accept a socket and |
229 | // atomically set the CLOEXEC flag is to use the `accept4` syscall on |
230 | // platforms that support it. On Linux, this was added in 2.6.28, |
231 | // glibc 2.10 and musl 0.9.5. |
232 | cfg_if::cfg_if! { |
233 | if #[cfg(any( |
234 | target_os = "android" , |
235 | target_os = "dragonfly" , |
236 | target_os = "freebsd" , |
237 | target_os = "illumos" , |
238 | target_os = "linux" , |
239 | target_os = "hurd" , |
240 | target_os = "netbsd" , |
241 | target_os = "openbsd" , |
242 | ))] { |
243 | unsafe { |
244 | let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; |
245 | Ok(Socket(FileDesc::from_raw_fd(fd))) |
246 | } |
247 | } else { |
248 | unsafe { |
249 | let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; |
250 | let fd = FileDesc::from_raw_fd(fd); |
251 | fd.set_cloexec()?; |
252 | Ok(Socket(fd)) |
253 | } |
254 | } |
255 | } |
256 | } |
257 | |
258 | pub fn duplicate(&self) -> io::Result<Socket> { |
259 | self.0.duplicate().map(Socket) |
260 | } |
261 | |
262 | fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { |
263 | let ret = cvt(unsafe { |
264 | libc::recv( |
265 | self.as_raw_fd(), |
266 | buf.as_mut().as_mut_ptr() as *mut c_void, |
267 | buf.capacity(), |
268 | flags, |
269 | ) |
270 | })?; |
271 | unsafe { |
272 | buf.advance_unchecked(ret as usize); |
273 | } |
274 | Ok(()) |
275 | } |
276 | |
277 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { |
278 | let mut buf = BorrowedBuf::from(buf); |
279 | self.recv_with_flags(buf.unfilled(), 0)?; |
280 | Ok(buf.len()) |
281 | } |
282 | |
283 | pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { |
284 | let mut buf = BorrowedBuf::from(buf); |
285 | self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; |
286 | Ok(buf.len()) |
287 | } |
288 | |
289 | pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { |
290 | self.recv_with_flags(buf, 0) |
291 | } |
292 | |
293 | pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
294 | self.0.read_vectored(bufs) |
295 | } |
296 | |
297 | #[inline ] |
298 | pub fn is_read_vectored(&self) -> bool { |
299 | self.0.is_read_vectored() |
300 | } |
301 | |
302 | fn recv_from_with_flags( |
303 | &self, |
304 | buf: &mut [u8], |
305 | flags: c_int, |
306 | ) -> io::Result<(usize, SocketAddr)> { |
307 | let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; |
308 | let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; |
309 | |
310 | let n = cvt(unsafe { |
311 | libc::recvfrom( |
312 | self.as_raw_fd(), |
313 | buf.as_mut_ptr() as *mut c_void, |
314 | buf.len(), |
315 | flags, |
316 | core::ptr::addr_of_mut!(storage) as *mut _, |
317 | &mut addrlen, |
318 | ) |
319 | })?; |
320 | Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) |
321 | } |
322 | |
323 | pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { |
324 | self.recv_from_with_flags(buf, 0) |
325 | } |
326 | |
327 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
328 | pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { |
329 | let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; |
330 | Ok(n as usize) |
331 | } |
332 | |
333 | pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { |
334 | self.recv_from_with_flags(buf, MSG_PEEK) |
335 | } |
336 | |
337 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { |
338 | self.0.write(buf) |
339 | } |
340 | |
341 | pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
342 | self.0.write_vectored(bufs) |
343 | } |
344 | |
345 | #[inline ] |
346 | pub fn is_write_vectored(&self) -> bool { |
347 | self.0.is_write_vectored() |
348 | } |
349 | |
350 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
351 | pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { |
352 | let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; |
353 | Ok(n as usize) |
354 | } |
355 | |
356 | pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> { |
357 | let timeout = match dur { |
358 | Some(dur) => { |
359 | if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { |
360 | return Err(io::Error::ZERO_TIMEOUT); |
361 | } |
362 | |
363 | let secs = if dur.as_secs() > libc::time_t::MAX as u64 { |
364 | libc::time_t::MAX |
365 | } else { |
366 | dur.as_secs() as libc::time_t |
367 | }; |
368 | let mut timeout = libc::timeval { |
369 | tv_sec: secs, |
370 | tv_usec: dur.subsec_micros() as libc::suseconds_t, |
371 | }; |
372 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { |
373 | timeout.tv_usec = 1; |
374 | } |
375 | timeout |
376 | } |
377 | None => libc::timeval { tv_sec: 0, tv_usec: 0 }, |
378 | }; |
379 | setsockopt(self, libc::SOL_SOCKET, kind, timeout) |
380 | } |
381 | |
382 | pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> { |
383 | let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; |
384 | if raw.tv_sec == 0 && raw.tv_usec == 0 { |
385 | Ok(None) |
386 | } else { |
387 | let sec = raw.tv_sec as u64; |
388 | let nsec = (raw.tv_usec as u32) * 1000; |
389 | Ok(Some(Duration::new(sec, nsec))) |
390 | } |
391 | } |
392 | |
393 | pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { |
394 | let how = match how { |
395 | Shutdown::Write => libc::SHUT_WR, |
396 | Shutdown::Read => libc::SHUT_RD, |
397 | Shutdown::Both => libc::SHUT_RDWR, |
398 | }; |
399 | cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; |
400 | Ok(()) |
401 | } |
402 | |
403 | pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> { |
404 | let linger = libc::linger { |
405 | l_onoff: linger.is_some() as libc::c_int, |
406 | l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, |
407 | }; |
408 | |
409 | setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) |
410 | } |
411 | |
412 | pub fn linger(&self) -> io::Result<Option<Duration>> { |
413 | let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; |
414 | |
415 | Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) |
416 | } |
417 | |
418 | pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { |
419 | setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) |
420 | } |
421 | |
422 | pub fn nodelay(&self) -> io::Result<bool> { |
423 | let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; |
424 | Ok(raw != 0) |
425 | } |
426 | |
427 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
428 | pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { |
429 | setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) |
430 | } |
431 | |
432 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
433 | pub fn quickack(&self) -> io::Result<bool> { |
434 | let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; |
435 | Ok(raw != 0) |
436 | } |
437 | |
438 | // bionic libc makes no use of this flag |
439 | #[cfg (target_os = "linux" )] |
440 | pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { |
441 | setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) |
442 | } |
443 | |
444 | #[cfg (target_os = "linux" )] |
445 | pub fn deferaccept(&self) -> io::Result<u32> { |
446 | let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; |
447 | Ok(raw as u32) |
448 | } |
449 | |
450 | #[cfg (any(target_os = "freebsd" , target_os = "netbsd" ))] |
451 | pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { |
452 | if !name.to_bytes().is_empty() { |
453 | const AF_NAME_MAX: usize = 16; |
454 | let mut buf = [0; AF_NAME_MAX]; |
455 | for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { |
456 | *dst = *src as libc::c_char; |
457 | } |
458 | let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; |
459 | arg.af_name = buf; |
460 | setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) |
461 | } else { |
462 | setsockopt( |
463 | self, |
464 | libc::SOL_SOCKET, |
465 | libc::SO_ACCEPTFILTER, |
466 | core::ptr::null_mut() as *mut c_void, |
467 | ) |
468 | } |
469 | } |
470 | |
471 | #[cfg (any(target_os = "freebsd" , target_os = "netbsd" ))] |
472 | pub fn acceptfilter(&self) -> io::Result<&CStr> { |
473 | let arg: libc::accept_filter_arg = |
474 | getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; |
475 | let s: &[u8] = |
476 | unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; |
477 | let name = CStr::from_bytes_with_nul(s).unwrap(); |
478 | Ok(name) |
479 | } |
480 | |
481 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
482 | pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { |
483 | setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) |
484 | } |
485 | |
486 | #[cfg (any(target_os = "android" , target_os = "linux" ,))] |
487 | pub fn passcred(&self) -> io::Result<bool> { |
488 | let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; |
489 | Ok(passcred != 0) |
490 | } |
491 | |
492 | #[cfg (target_os = "netbsd" )] |
493 | pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { |
494 | setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) |
495 | } |
496 | |
497 | #[cfg (target_os = "netbsd" )] |
498 | pub fn local_creds(&self) -> io::Result<bool> { |
499 | let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; |
500 | Ok(local_creds != 0) |
501 | } |
502 | |
503 | #[cfg (target_os = "freebsd" )] |
504 | pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { |
505 | setsockopt( |
506 | self, |
507 | libc::AF_LOCAL, |
508 | libc::LOCAL_CREDS_PERSISTENT, |
509 | local_creds_persistent as libc::c_int, |
510 | ) |
511 | } |
512 | |
513 | #[cfg (target_os = "freebsd" )] |
514 | pub fn local_creds_persistent(&self) -> io::Result<bool> { |
515 | let local_creds_persistent: libc::c_int = |
516 | getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; |
517 | Ok(local_creds_persistent != 0) |
518 | } |
519 | |
520 | #[cfg (not(any(target_os = "solaris" , target_os = "illumos" , target_os = "vita" )))] |
521 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { |
522 | let mut nonblocking = nonblocking as libc::c_int; |
523 | cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) |
524 | } |
525 | |
526 | #[cfg (target_os = "vita" )] |
527 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { |
528 | let option = nonblocking as libc::c_int; |
529 | setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) |
530 | } |
531 | |
532 | #[cfg (any(target_os = "solaris" , target_os = "illumos" ))] |
533 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { |
534 | // FIONBIO is inadequate for sockets on illumos/Solaris, so use the |
535 | // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. |
536 | self.0.set_nonblocking(nonblocking) |
537 | } |
538 | |
539 | #[cfg (any(target_os = "linux" , target_os = "freebsd" , target_os = "openbsd" ))] |
540 | pub fn set_mark(&self, mark: u32) -> io::Result<()> { |
541 | #[cfg (target_os = "linux" )] |
542 | let option = libc::SO_MARK; |
543 | #[cfg (target_os = "freebsd" )] |
544 | let option = libc::SO_USER_COOKIE; |
545 | #[cfg (target_os = "openbsd" )] |
546 | let option = libc::SO_RTABLE; |
547 | setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) |
548 | } |
549 | |
550 | pub fn take_error(&self) -> io::Result<Option<io::Error>> { |
551 | let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; |
552 | if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } |
553 | } |
554 | |
555 | // This is used by sys_common code to abstract over Windows and Unix. |
556 | pub fn as_raw(&self) -> RawFd { |
557 | self.as_raw_fd() |
558 | } |
559 | } |
560 | |
561 | impl AsInner<FileDesc> for Socket { |
562 | #[inline ] |
563 | fn as_inner(&self) -> &FileDesc { |
564 | &self.0 |
565 | } |
566 | } |
567 | |
568 | impl IntoInner<FileDesc> for Socket { |
569 | fn into_inner(self) -> FileDesc { |
570 | self.0 |
571 | } |
572 | } |
573 | |
574 | impl FromInner<FileDesc> for Socket { |
575 | fn from_inner(file_desc: FileDesc) -> Self { |
576 | Self(file_desc) |
577 | } |
578 | } |
579 | |
580 | impl AsFd for Socket { |
581 | fn as_fd(&self) -> BorrowedFd<'_> { |
582 | self.0.as_fd() |
583 | } |
584 | } |
585 | |
586 | impl AsRawFd for Socket { |
587 | #[inline ] |
588 | fn as_raw_fd(&self) -> RawFd { |
589 | self.0.as_raw_fd() |
590 | } |
591 | } |
592 | |
593 | impl IntoRawFd for Socket { |
594 | fn into_raw_fd(self) -> RawFd { |
595 | self.0.into_raw_fd() |
596 | } |
597 | } |
598 | |
599 | impl FromRawFd for Socket { |
600 | unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { |
601 | Self(FromRawFd::from_raw_fd(raw_fd)) |
602 | } |
603 | } |
604 | |
605 | // In versions of glibc prior to 2.26, there's a bug where the DNS resolver |
606 | // will cache the contents of /etc/resolv.conf, so changes to that file on disk |
607 | // can be ignored by a long-running program. That can break DNS lookups on e.g. |
608 | // laptops where the network comes and goes. See |
609 | // https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some |
610 | // distros including Debian have patched glibc to fix this for a long time. |
611 | // |
612 | // A workaround for this bug is to call the res_init libc function, to clear |
613 | // the cached configs. Unfortunately, while we believe glibc's implementation |
614 | // of res_init is thread-safe, we know that other implementations are not |
615 | // (https://github.com/rust-lang/rust/issues/43592). Code here in std could |
616 | // try to synchronize its res_init calls with a Mutex, but that wouldn't |
617 | // protect programs that call into libc in other ways. So instead of calling |
618 | // res_init unconditionally, we call it only when we detect we're linking |
619 | // against glibc version < 2.26. (That is, when we both know its needed and |
620 | // believe it's thread-safe). |
621 | #[cfg (all(target_os = "linux" , target_env = "gnu" ))] |
622 | fn on_resolver_failure() { |
623 | use crate::sys; |
624 | |
625 | // If the version fails to parse, we treat it the same as "not glibc". |
626 | if let Some(version: (usize, usize)) = sys::os::glibc_version() { |
627 | if version < (2, 26) { |
628 | unsafe { libc::res_init() }; |
629 | } |
630 | } |
631 | } |
632 | |
633 | #[cfg (not(all(target_os = "linux" , target_env = "gnu" )))] |
634 | fn on_resolver_failure() {} |
635 | |