]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/net.rs
Rollup merge of #82554 - SkiFire13:fix-string-retain-unsoundness, r=m-ou-se
[rust.git] / library / std / src / sys / unix / net.rs
1 use crate::cmp;
2 use crate::ffi::CStr;
3 use crate::io::{self, IoSlice, IoSliceMut};
4 use crate::mem;
5 use crate::net::{Shutdown, SocketAddr};
6 use crate::str;
7 use crate::sys::fd::FileDesc;
8 use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
9 use crate::sys_common::{AsInner, FromInner, IntoInner};
10 use crate::time::{Duration, Instant};
11
12 use libc::{c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK};
13
14 pub use crate::sys::{cvt, cvt_r};
15
16 #[allow(unused_extern_crates)]
17 pub extern crate libc as netc;
18
19 pub type wrlen_t = size_t;
20
21 pub struct Socket(FileDesc);
22
23 pub fn init() {}
24
25 pub fn cvt_gai(err: c_int) -> io::Result<()> {
26     if err == 0 {
27         return Ok(());
28     }
29
30     // We may need to trigger a glibc workaround. See on_resolver_failure() for details.
31     on_resolver_failure();
32
33     if err == EAI_SYSTEM {
34         return Err(io::Error::last_os_error());
35     }
36
37     let detail = unsafe {
38         str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned()
39     };
40     Err(io::Error::new(
41         io::ErrorKind::Other,
42         &format!("failed to lookup address information: {}", detail)[..],
43     ))
44 }
45
46 impl Socket {
47     pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
48         let fam = match *addr {
49             SocketAddr::V4(..) => libc::AF_INET,
50             SocketAddr::V6(..) => libc::AF_INET6,
51         };
52         Socket::new_raw(fam, ty)
53     }
54
55     pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
56         unsafe {
57             cfg_if::cfg_if! {
58                 if #[cfg(any(
59                     target_os = "android",
60                     target_os = "dragonfly",
61                     target_os = "freebsd",
62                     target_os = "illumos",
63                     target_os = "linux",
64                     target_os = "netbsd",
65                     target_os = "opensbd",
66                 ))] {
67                     // On platforms that support it we pass the SOCK_CLOEXEC
68                     // flag to atomically create the socket and set it as
69                     // CLOEXEC. On Linux this was added in 2.6.27.
70                     let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
71                     Ok(Socket(FileDesc::new(fd)))
72                 } else {
73                     let fd = cvt(libc::socket(fam, ty, 0))?;
74                     let fd = FileDesc::new(fd);
75                     fd.set_cloexec()?;
76                     let socket = Socket(fd);
77
78                     // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt`
79                     // flag to disable `SIGPIPE` emission on socket.
80                     #[cfg(target_vendor = "apple")]
81                     setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?;
82
83                     Ok(socket)
84                 }
85             }
86         }
87     }
88
89     #[cfg(not(target_os = "vxworks"))]
90     pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
91         unsafe {
92             let mut fds = [0, 0];
93
94             cfg_if::cfg_if! {
95                 if #[cfg(any(
96                     target_os = "android",
97                     target_os = "dragonfly",
98                     target_os = "freebsd",
99                     target_os = "illumos",
100                     target_os = "linux",
101                     target_os = "netbsd",
102                     target_os = "opensbd",
103                 ))] {
104                     // Like above, set cloexec atomically
105                     cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
106                     Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))))
107                 } else {
108                     cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
109                     let a = FileDesc::new(fds[0]);
110                     let b = FileDesc::new(fds[1]);
111                     a.set_cloexec()?;
112                     b.set_cloexec()?;
113                     Ok((Socket(a), Socket(b)))
114                 }
115             }
116         }
117     }
118
119     #[cfg(target_os = "vxworks")]
120     pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
121         unimplemented!()
122     }
123
124     pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
125         self.set_nonblocking(true)?;
126         let r = unsafe {
127             let (addrp, len) = addr.into_inner();
128             cvt(libc::connect(self.0.raw(), addrp, len))
129         };
130         self.set_nonblocking(false)?;
131
132         match r {
133             Ok(_) => return Ok(()),
134             // there's no ErrorKind for EINPROGRESS :(
135             Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
136             Err(e) => return Err(e),
137         }
138
139         let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 };
140
141         if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
142             return Err(io::Error::new(
143                 io::ErrorKind::InvalidInput,
144                 "cannot set a 0 duration timeout",
145             ));
146         }
147
148         let start = Instant::now();
149
150         loop {
151             let elapsed = start.elapsed();
152             if elapsed >= timeout {
153                 return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out"));
154             }
155
156             let timeout = timeout - elapsed;
157             let mut timeout = timeout
158                 .as_secs()
159                 .saturating_mul(1_000)
160                 .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
161             if timeout == 0 {
162                 timeout = 1;
163             }
164
165             let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int;
166
167             match unsafe { libc::poll(&mut pollfd, 1, timeout) } {
168                 -1 => {
169                     let err = io::Error::last_os_error();
170                     if err.kind() != io::ErrorKind::Interrupted {
171                         return Err(err);
172                     }
173                 }
174                 0 => {}
175                 _ => {
176                     // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
177                     // for POLLHUP rather than read readiness
178                     if pollfd.revents & libc::POLLHUP != 0 {
179                         let e = self.take_error()?.unwrap_or_else(|| {
180                             io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP")
181                         });
182                         return Err(e);
183                     }
184
185                     return Ok(());
186                 }
187             }
188         }
189     }
190
191     pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> {
192         // Unfortunately the only known way right now to accept a socket and
193         // atomically set the CLOEXEC flag is to use the `accept4` syscall on
194         // platforms that support it. On Linux, this was added in 2.6.28,
195         // glibc 2.10 and musl 0.9.5.
196         cfg_if::cfg_if! {
197             if #[cfg(any(
198                 target_os = "android",
199                 target_os = "dragonfly",
200                 target_os = "freebsd",
201                 target_os = "illumos",
202                 target_os = "linux",
203                 target_os = "netbsd",
204                 target_os = "opensbd",
205             ))] {
206                 let fd = cvt_r(|| unsafe {
207                     libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC)
208                 })?;
209                 Ok(Socket(FileDesc::new(fd)))
210             } else {
211                 let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
212                 let fd = FileDesc::new(fd);
213                 fd.set_cloexec()?;
214                 Ok(Socket(fd))
215             }
216         }
217     }
218
219     pub fn duplicate(&self) -> io::Result<Socket> {
220         self.0.duplicate().map(Socket)
221     }
222
223     fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
224         let ret = cvt(unsafe {
225             libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
226         })?;
227         Ok(ret as usize)
228     }
229
230     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
231         self.recv_with_flags(buf, 0)
232     }
233
234     pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
235         self.recv_with_flags(buf, MSG_PEEK)
236     }
237
238     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
239         self.0.read_vectored(bufs)
240     }
241
242     #[inline]
243     pub fn is_read_vectored(&self) -> bool {
244         self.0.is_read_vectored()
245     }
246
247     fn recv_from_with_flags(
248         &self,
249         buf: &mut [u8],
250         flags: c_int,
251     ) -> io::Result<(usize, SocketAddr)> {
252         let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
253         let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t;
254
255         let n = cvt(unsafe {
256             libc::recvfrom(
257                 self.0.raw(),
258                 buf.as_mut_ptr() as *mut c_void,
259                 buf.len(),
260                 flags,
261                 &mut storage as *mut _ as *mut _,
262                 &mut addrlen,
263             )
264         })?;
265         Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
266     }
267
268     pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
269         self.recv_from_with_flags(buf, 0)
270     }
271
272     #[cfg(any(
273         target_os = "android",
274         target_os = "dragonfly",
275         target_os = "emscripten",
276         target_os = "freebsd",
277         target_os = "linux",
278         target_os = "netbsd",
279         target_os = "openbsd",
280     ))]
281     pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
282         let n = cvt(unsafe { libc::recvmsg(self.0.raw(), msg, libc::MSG_CMSG_CLOEXEC) })?;
283         Ok(n as usize)
284     }
285
286     pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
287         self.recv_from_with_flags(buf, MSG_PEEK)
288     }
289
290     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
291         self.0.write(buf)
292     }
293
294     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
295         self.0.write_vectored(bufs)
296     }
297
298     #[inline]
299     pub fn is_write_vectored(&self) -> bool {
300         self.0.is_write_vectored()
301     }
302
303     #[cfg(any(
304         target_os = "android",
305         target_os = "dragonfly",
306         target_os = "emscripten",
307         target_os = "freebsd",
308         target_os = "linux",
309         target_os = "netbsd",
310         target_os = "openbsd",
311     ))]
312     pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
313         let n = cvt(unsafe { libc::sendmsg(self.0.raw(), msg, 0) })?;
314         Ok(n as usize)
315     }
316
317     pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
318         let timeout = match dur {
319             Some(dur) => {
320                 if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
321                     return Err(io::Error::new(
322                         io::ErrorKind::InvalidInput,
323                         "cannot set a 0 duration timeout",
324                     ));
325                 }
326
327                 let secs = if dur.as_secs() > libc::time_t::MAX as u64 {
328                     libc::time_t::MAX
329                 } else {
330                     dur.as_secs() as libc::time_t
331                 };
332                 let mut timeout = libc::timeval {
333                     tv_sec: secs,
334                     tv_usec: dur.subsec_micros() as libc::suseconds_t,
335                 };
336                 if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
337                     timeout.tv_usec = 1;
338                 }
339                 timeout
340             }
341             None => libc::timeval { tv_sec: 0, tv_usec: 0 },
342         };
343         setsockopt(self, libc::SOL_SOCKET, kind, timeout)
344     }
345
346     pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> {
347         let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?;
348         if raw.tv_sec == 0 && raw.tv_usec == 0 {
349             Ok(None)
350         } else {
351             let sec = raw.tv_sec as u64;
352             let nsec = (raw.tv_usec as u32) * 1000;
353             Ok(Some(Duration::new(sec, nsec)))
354         }
355     }
356
357     pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
358         let how = match how {
359             Shutdown::Write => libc::SHUT_WR,
360             Shutdown::Read => libc::SHUT_RD,
361             Shutdown::Both => libc::SHUT_RDWR,
362         };
363         cvt(unsafe { libc::shutdown(self.0.raw(), how) })?;
364         Ok(())
365     }
366
367     pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
368         setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int)
369     }
370
371     pub fn nodelay(&self) -> io::Result<bool> {
372         let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?;
373         Ok(raw != 0)
374     }
375
376     #[cfg(any(target_os = "android", target_os = "linux",))]
377     pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
378         setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int)
379     }
380
381     #[cfg(any(target_os = "android", target_os = "linux",))]
382     pub fn passcred(&self) -> io::Result<bool> {
383         let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?;
384         Ok(passcred != 0)
385     }
386
387     #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
388     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
389         let mut nonblocking = nonblocking as libc::c_int;
390         cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop)
391     }
392
393     #[cfg(any(target_os = "solaris", target_os = "illumos"))]
394     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
395         // FIONBIO is inadequate for sockets on illumos/Solaris, so use the
396         // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead.
397         self.0.set_nonblocking(nonblocking)
398     }
399
400     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
401         let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
402         if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
403     }
404 }
405
406 impl AsInner<c_int> for Socket {
407     fn as_inner(&self) -> &c_int {
408         self.0.as_inner()
409     }
410 }
411
412 impl FromInner<c_int> for Socket {
413     fn from_inner(fd: c_int) -> Socket {
414         Socket(FileDesc::new(fd))
415     }
416 }
417
418 impl IntoInner<c_int> for Socket {
419     fn into_inner(self) -> c_int {
420         self.0.into_raw()
421     }
422 }
423
424 // In versions of glibc prior to 2.26, there's a bug where the DNS resolver
425 // will cache the contents of /etc/resolv.conf, so changes to that file on disk
426 // can be ignored by a long-running program. That can break DNS lookups on e.g.
427 // laptops where the network comes and goes. See
428 // https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some
429 // distros including Debian have patched glibc to fix this for a long time.
430 //
431 // A workaround for this bug is to call the res_init libc function, to clear
432 // the cached configs. Unfortunately, while we believe glibc's implementation
433 // of res_init is thread-safe, we know that other implementations are not
434 // (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
435 // try to synchronize its res_init calls with a Mutex, but that wouldn't
436 // protect programs that call into libc in other ways. So instead of calling
437 // res_init unconditionally, we call it only when we detect we're linking
438 // against glibc version < 2.26. (That is, when we both know its needed and
439 // believe it's thread-safe).
440 #[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
441 fn on_resolver_failure() {
442     use crate::sys;
443
444     // If the version fails to parse, we treat it the same as "not glibc".
445     if let Some(version) = sys::os::glibc_version() {
446         if version < (2, 26) {
447             unsafe { libc::res_init() };
448         }
449     }
450 }
451
452 #[cfg(any(not(target_env = "gnu"), target_os = "vxworks"))]
453 fn on_resolver_failure() {}