xref: /drstd/src/std/sys/unix/net.rs (revision 0fe3ff0054d3aec7fbf9bddecfecb10bc7d23a51)
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