1 #[cfg(all(test, taget_env = "gnu"))]
7 use crate::io
::{IoSlice, IoSliceMut}
;
9 use crate::net
::{Shutdown, SocketAddr}
;
11 use crate::sys
::fd
::FileDesc
;
12 use crate::sys_common
::net
::{getsockopt, setsockopt, sockaddr_to_addr}
;
13 use crate::sys_common
::{AsInner, FromInner, IntoInner}
;
14 use crate::time
::{Duration, Instant}
;
15 use libc
::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}
;
17 pub use crate::sys
::{cvt, cvt_r}
;
19 #[allow(unused_extern_crates)]
20 pub extern crate libc
as netc
;
22 pub type wrlen_t
= size_t
;
24 pub struct Socket(FileDesc
);
28 pub fn cvt_gai(err
: c_int
) -> io
::Result
<()> {
33 // We may need to trigger a glibc workaround. See on_resolver_failure() for details.
34 on_resolver_failure();
36 if err
== EAI_SYSTEM
{
37 return Err(io
::Error
::last_os_error());
41 str::from_utf8(CStr
::from_ptr(libc
::gai_strerror(err
)).to_bytes()).unwrap().to_owned()
45 &format
!("failed to lookup address information: {}", detail
)[..],
50 pub fn new(addr
: &SocketAddr
, ty
: c_int
) -> io
::Result
<Socket
> {
51 let fam
= match *addr
{
52 SocketAddr
::V4(..) => libc
::AF_INET
,
53 SocketAddr
::V6(..) => libc
::AF_INET6
,
55 Socket
::new_raw(fam
, ty
)
58 pub fn new_raw(fam
: c_int
, ty
: c_int
) -> io
::Result
<Socket
> {
60 let fd
= cvt(libc
::socket(fam
, ty
, 0))?
;
61 let fd
= FileDesc
::new(fd
);
63 let socket
= Socket(fd
);
68 pub fn new_pair(_fam
: c_int
, _ty
: c_int
) -> io
::Result
<(Socket
, Socket
)> {
72 pub fn connect_timeout(&self, addr
: &SocketAddr
, timeout
: Duration
) -> io
::Result
<()> {
73 self.set_nonblocking(true)?
;
75 let (addrp
, len
) = addr
.into_inner();
76 cvt(libc
::connect(self.0.raw(), addrp
, len
))
78 self.set_nonblocking(false)?
;
81 Ok(_
) => return Ok(()),
82 // there's no ErrorKind for EINPROGRESS :(
83 Err(ref e
) if e
.raw_os_error() == Some(libc
::EINPROGRESS
) => {}
84 Err(e
) => return Err(e
),
87 let mut pollfd
= libc
::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }
;
89 if timeout
.as_secs() == 0 && timeout
.subsec_nanos() == 0 {
90 return Err(io
::Error
::new(
91 io
::ErrorKind
::InvalidInput
,
92 "cannot set a 0 duration timeout",
96 let start
= Instant
::now();
99 let elapsed
= start
.elapsed();
100 if elapsed
>= timeout
{
101 return Err(io
::Error
::new(io
::ErrorKind
::TimedOut
, "connection timed out"));
104 let timeout
= timeout
- elapsed
;
105 let mut timeout
= timeout
107 .saturating_mul(1_000)
108 .saturating_add(timeout
.subsec_nanos() as u64 / 1_000_000);
113 let timeout
= cmp
::min(timeout
, c_int
::MAX
as u64) as c_int
;
115 match unsafe { libc::poll(&mut pollfd, 1, timeout) }
{
117 let err
= io
::Error
::last_os_error();
118 if err
.kind() != io
::ErrorKind
::Interrupted
{
124 // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
125 // for POLLHUP rather than read readiness
126 if pollfd
.revents
& libc
::POLLHUP
!= 0 {
127 let e
= self.take_error()?
.unwrap_or_else(|| {
128 io
::Error
::new(io
::ErrorKind
::Other
, "no error set after POLLHUP")
139 pub fn accept(&self, storage
: *mut sockaddr
, len
: *mut socklen_t
) -> io
::Result
<Socket
> {
140 let fd
= cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) }
)?
;
141 let fd
= FileDesc
::new(fd
);
146 pub fn duplicate(&self) -> io
::Result
<Socket
> {
147 self.0.duplicate().map(Socket
)
150 fn recv_with_flags(&self, buf
: &mut [u8], flags
: c_int
) -> io
::Result
<usize> {
151 let ret
= cvt(unsafe {
152 libc
::recv(self.0.raw(), buf
.as_mut_ptr() as *mut c_void
, buf
.len(), flags
)
157 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
158 self.recv_with_flags(buf
, 0)
161 pub fn peek(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
162 self.recv_with_flags(buf
, MSG_PEEK
)
165 pub fn read_vectored(&self, bufs
: &mut [IoSliceMut
<'_
>]) -> io
::Result
<usize> {
166 self.0.read_vectored(bufs
)
170 pub fn is_read_vectored(&self) -> bool
{
171 self.0.is_read_vectored
()
174 fn recv_from_with_flags(
178 ) -> io
::Result
<(usize, SocketAddr
)> {
179 let mut storage
: libc
::sockaddr_storage
= unsafe { mem::zeroed() }
;
180 let mut addrlen
= mem
::size_of_val(&storage
) as libc
::socklen_t
;
185 buf
.as_mut_ptr() as *mut c_void
,
188 &mut storage
as *mut _
as *mut _
,
192 Ok((n
as usize, sockaddr_to_addr(&storage
, addrlen
as usize)?
))
195 pub fn recv_from(&self, buf
: &mut [u8]) -> io
::Result
<(usize, SocketAddr
)> {
196 self.recv_from_with_flags(buf
, 0)
199 pub fn peek_from(&self, buf
: &mut [u8]) -> io
::Result
<(usize, SocketAddr
)> {
200 self.recv_from_with_flags(buf
, MSG_PEEK
)
203 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
207 pub fn write_vectored(&self, bufs
: &[IoSlice
<'_
>]) -> io
::Result
<usize> {
208 self.0.write_vectored(bufs
)
212 pub fn is_write_vectored(&self) -> bool
{
213 self.0.is_write_vectored
()
216 pub fn set_timeout(&self, dur
: Option
<Duration
>, kind
: libc
::c_int
) -> io
::Result
<()> {
217 let timeout
= match dur
{
219 if dur
.as_secs() == 0 && dur
.subsec_nanos() == 0 {
220 return Err(io
::Error
::new(
221 io
::ErrorKind
::InvalidInput
,
222 "cannot set a 0 duration timeout",
226 let secs
= if dur
.as_secs() > libc
::time_t
::MAX
as u64 {
229 dur
.as_secs() as libc
::time_t
231 let mut timeout
= libc
::timeval
{
233 tv_usec
: (dur
.subsec_nanos() / 1000) as libc
::suseconds_t
,
235 if timeout
.tv_sec
== 0 && timeout
.tv_usec
== 0 {
240 None
=> libc
::timeval { tv_sec: 0, tv_usec: 0 }
,
242 setsockopt(self, libc
::SOL_SOCKET
, kind
, timeout
)
245 pub fn timeout(&self, kind
: libc
::c_int
) -> io
::Result
<Option
<Duration
>> {
246 let raw
: libc
::timeval
= getsockopt(self, libc
::SOL_SOCKET
, kind
)?
;
247 if raw
.tv_sec
== 0 && raw
.tv_usec
== 0 {
250 let sec
= raw
.tv_sec
as u64;
251 let nsec
= (raw
.tv_usec
as u32) * 1000;
252 Ok(Some(Duration
::new(sec
, nsec
)))
256 pub fn shutdown(&self, how
: Shutdown
) -> io
::Result
<()> {
257 let how
= match how
{
258 Shutdown
::Write
=> libc
::SHUT_WR
,
259 Shutdown
::Read
=> libc
::SHUT_RD
,
260 Shutdown
::Both
=> libc
::SHUT_RDWR
,
262 cvt(unsafe { libc::shutdown(self.0.raw(), how) }
)?
;
266 pub fn set_nodelay(&self, nodelay
: bool
) -> io
::Result
<()> {
267 setsockopt(self, libc
::IPPROTO_TCP
, libc
::TCP_NODELAY
, nodelay
as c_int
)
270 pub fn nodelay(&self) -> io
::Result
<bool
> {
271 let raw
: c_int
= getsockopt(self, libc
::IPPROTO_TCP
, libc
::TCP_NODELAY
)?
;
275 pub fn set_nonblocking(&self, nonblocking
: bool
) -> io
::Result
<()> {
276 let mut nonblocking
= nonblocking
as libc
::c_int
;
277 cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }
).map(drop
)
280 pub fn take_error(&self) -> io
::Result
<Option
<io
::Error
>> {
281 let raw
: c_int
= getsockopt(self, libc
::SOL_SOCKET
, libc
::SO_ERROR
)?
;
282 if raw
== 0 { Ok(None) }
else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
286 impl AsInner
<c_int
> for Socket
{
287 fn as_inner(&self) -> &c_int
{
292 impl FromInner
<c_int
> for Socket
{
293 fn from_inner(fd
: c_int
) -> Socket
{
294 Socket(FileDesc
::new(fd
))
298 impl IntoInner
<c_int
> for Socket
{
299 fn into_inner(self) -> c_int
{
304 // In versions of glibc prior to 2.26, there's a bug where the DNS resolver
305 // will cache the contents of /etc/resolv.conf, so changes to that file on disk
306 // can be ignored by a long-running program. That can break DNS lookups on e.g.
307 // laptops where the network comes and goes. See
308 // https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some
309 // distros including Debian have patched glibc to fix this for a long time.
311 // A workaround for this bug is to call the res_init libc function, to clear
312 // the cached configs. Unfortunately, while we believe glibc's implementation
313 // of res_init is thread-safe, we know that other implementations are not
314 // (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
315 // try to synchronize its res_init calls with a Mutex, but that wouldn't
316 // protect programs that call into libc in other ways. So instead of calling
317 // res_init unconditionally, we call it only when we detect we're linking
318 // against glibc version < 2.26. (That is, when we both know its needed and
319 // believe it's thread-safe).
320 #[cfg(target_env = "gnu")]
321 fn on_resolver_failure() {
325 // If the version fails to parse, we treat it the same as "not glibc".
326 if let Some(version) = sys::os::glibc_version() {
327 if version < (2, 26) {
328 unsafe { libc::res_init() };
334 #[cfg(not(target_env = "gnu"))]
335 fn on_resolver_failure() {}