1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use libc
::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}
;
15 use net
::{SocketAddr, Shutdown}
;
17 use sys
::fd
::FileDesc
;
18 use sys_common
::{AsInner, FromInner, IntoInner}
;
19 use sys_common
::net
::{getsockopt, setsockopt, sockaddr_to_addr}
;
20 use time
::{Duration, Instant}
;
23 pub use sys
::{cvt, cvt_r}
;
24 pub extern crate libc
as netc
;
26 pub type wrlen_t
= size_t
;
28 // See below for the usage of SOCK_CLOEXEC, but this constant is only defined on
29 // Linux currently (e.g. support doesn't exist on other platforms). In order to
30 // get name resolution to work and things to compile we just define a dummy
31 // SOCK_CLOEXEC here for other platforms. Note that the dummy constant isn't
32 // actually ever used (the blocks below are wrapped in `if cfg!` as well.
33 #[cfg(target_os = "linux")]
34 use libc
::SOCK_CLOEXEC
;
35 #[cfg(not(target_os = "linux"))]
36 const SOCK_CLOEXEC
: c_int
= 0;
38 // Another conditional contant for name resolution: Macos et iOS use
39 // SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket.
40 // Other platforms do otherwise.
41 #[cfg(target_vendor = "apple")]
42 use libc
::SO_NOSIGPIPE
;
43 #[cfg(not(target_vendor = "apple"))]
44 const SO_NOSIGPIPE
: c_int
= 0;
46 pub struct Socket(FileDesc
);
50 pub fn cvt_gai(err
: c_int
) -> io
::Result
<()> {
54 if err
== EAI_SYSTEM
{
55 return Err(io
::Error
::last_os_error())
59 str::from_utf8(CStr
::from_ptr(libc
::gai_strerror(err
)).to_bytes()).unwrap()
62 Err(io
::Error
::new(io
::ErrorKind
::Other
,
63 &format
!("failed to lookup address information: {}",
68 pub fn new(addr
: &SocketAddr
, ty
: c_int
) -> io
::Result
<Socket
> {
69 let fam
= match *addr
{
70 SocketAddr
::V4(..) => libc
::AF_INET
,
71 SocketAddr
::V6(..) => libc
::AF_INET6
,
73 Socket
::new_raw(fam
, ty
)
76 pub fn new_raw(fam
: c_int
, ty
: c_int
) -> io
::Result
<Socket
> {
78 // On linux we first attempt to pass the SOCK_CLOEXEC flag to
79 // atomically create the socket and set it as CLOEXEC. Support for
80 // this option, however, was added in 2.6.27, and we still support
81 // 2.6.18 as a kernel, so if the returned error is EINVAL we
82 // fallthrough to the fallback.
83 if cfg
!(target_os
= "linux") {
84 match cvt(libc
::socket(fam
, ty
| SOCK_CLOEXEC
, 0)) {
85 Ok(fd
) => return Ok(Socket(FileDesc
::new(fd
))),
86 Err(ref e
) if e
.raw_os_error() == Some(libc
::EINVAL
) => {}
87 Err(e
) => return Err(e
),
91 let fd
= cvt(libc
::socket(fam
, ty
, 0))?
;
92 let fd
= FileDesc
::new(fd
);
94 let socket
= Socket(fd
);
95 if cfg
!(target_vendor
= "apple") {
96 setsockopt(&socket
, libc
::SOL_SOCKET
, SO_NOSIGPIPE
, 1)?
;
102 pub fn new_pair(fam
: c_int
, ty
: c_int
) -> io
::Result
<(Socket
, Socket
)> {
104 let mut fds
= [0, 0];
106 // Like above, see if we can set cloexec atomically
107 if cfg
!(target_os
= "linux") {
108 match cvt(libc
::socketpair(fam
, ty
| SOCK_CLOEXEC
, 0, fds
.as_mut_ptr())) {
110 return Ok((Socket(FileDesc
::new(fds
[0])), Socket(FileDesc
::new(fds
[1]))));
112 Err(ref e
) if e
.raw_os_error() == Some(libc
::EINVAL
) => {}
,
113 Err(e
) => return Err(e
),
117 cvt(libc
::socketpair(fam
, ty
, 0, fds
.as_mut_ptr()))?
;
118 let a
= FileDesc
::new(fds
[0]);
119 let b
= FileDesc
::new(fds
[1]);
122 Ok((Socket(a
), Socket(b
)))
126 pub fn connect_timeout(&self, addr
: &SocketAddr
, timeout
: Duration
) -> io
::Result
<()> {
127 self.set_nonblocking(true)?
;
129 let (addrp
, len
) = addr
.into_inner();
130 cvt(libc
::connect(self.0.raw(), addrp
, len
))
132 self.set_nonblocking(false)?
;
135 Ok(_
) => return Ok(()),
136 // there's no ErrorKind for EINPROGRESS :(
137 Err(ref e
) if e
.raw_os_error() == Some(libc
::EINPROGRESS
) => {}
138 Err(e
) => return Err(e
),
141 let mut pollfd
= libc
::pollfd
{
143 events
: libc
::POLLOUT
,
147 if timeout
.as_secs() == 0 && timeout
.subsec_nanos() == 0 {
148 return Err(io
::Error
::new(io
::ErrorKind
::InvalidInput
,
149 "cannot set a 0 duration timeout"));
152 let start
= Instant
::now();
155 let elapsed
= start
.elapsed();
156 if elapsed
>= timeout
{
157 return Err(io
::Error
::new(io
::ErrorKind
::TimedOut
, "connection timed out"));
160 let timeout
= timeout
- elapsed
;
161 let mut timeout
= timeout
.as_secs()
162 .saturating_mul(1_000)
163 .saturating_add(timeout
.subsec_nanos() as u64 / 1_000_000);
168 let timeout
= cmp
::min(timeout
, c_int
::max_value() as u64) as c_int
;
170 match unsafe { libc::poll(&mut pollfd, 1, timeout) }
{
172 let err
= io
::Error
::last_os_error();
173 if err
.kind() != io
::ErrorKind
::Interrupted
{
179 // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
180 // for POLLHUP rather than read readiness
181 if pollfd
.revents
& libc
::POLLHUP
!= 0 {
182 let e
= self.take_error()?
184 io
::Error
::new(io
::ErrorKind
::Other
, "no error set after POLLHUP")
195 pub fn accept(&self, storage
: *mut sockaddr
, len
: *mut socklen_t
)
196 -> io
::Result
<Socket
> {
197 // Unfortunately the only known way right now to accept a socket and
198 // atomically set the CLOEXEC flag is to use the `accept4` syscall on
199 // Linux. This was added in 2.6.28, however, and because we support
200 // 2.6.18 we must detect this support dynamically.
201 if cfg
!(target_os
= "linux") {
203 fn accept4(c_int
, *mut sockaddr
, *mut socklen_t
, c_int
) -> c_int
205 if let Some(accept
) = accept4
.get() {
206 let res
= cvt_r(|| unsafe {
207 accept(self.0.raw(), storage
, len
, SOCK_CLOEXEC
)
210 Ok(fd
) => return Ok(Socket(FileDesc
::new(fd
))),
211 Err(ref e
) if e
.raw_os_error() == Some(libc
::ENOSYS
) => {}
212 Err(e
) => return Err(e
),
217 let fd
= cvt_r(|| unsafe {
218 libc
::accept(self.0.raw(), storage
, len
)
220 let fd
= FileDesc
::new(fd
);
225 pub fn duplicate(&self) -> io
::Result
<Socket
> {
226 self.0.duplicate().map(Socket
)
229 fn recv_with_flags(&self, buf
: &mut [u8], flags
: c_int
) -> io
::Result
<usize> {
230 let ret
= cvt(unsafe {
231 libc
::recv(self.0.raw(),
232 buf
.as_mut_ptr() as *mut c_void
,
239 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
240 self.recv_with_flags(buf
, 0)
243 pub fn peek(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
244 self.recv_with_flags(buf
, MSG_PEEK
)
247 fn recv_from_with_flags(&self, buf
: &mut [u8], flags
: c_int
)
248 -> io
::Result
<(usize, SocketAddr
)> {
249 let mut storage
: libc
::sockaddr_storage
= unsafe { mem::zeroed() }
;
250 let mut addrlen
= mem
::size_of_val(&storage
) as libc
::socklen_t
;
253 libc
::recvfrom(self.0.raw(),
254 buf
.as_mut_ptr() as *mut c_void
,
257 &mut storage
as *mut _
as *mut _
,
260 Ok((n
as usize, sockaddr_to_addr(&storage
, addrlen
as usize)?
))
263 pub fn recv_from(&self, buf
: &mut [u8]) -> io
::Result
<(usize, SocketAddr
)> {
264 self.recv_from_with_flags(buf
, 0)
267 pub fn peek_from(&self, buf
: &mut [u8]) -> io
::Result
<(usize, SocketAddr
)> {
268 self.recv_from_with_flags(buf
, MSG_PEEK
)
271 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
275 pub fn set_timeout(&self, dur
: Option
<Duration
>, kind
: libc
::c_int
) -> io
::Result
<()> {
276 let timeout
= match dur
{
278 if dur
.as_secs() == 0 && dur
.subsec_nanos() == 0 {
279 return Err(io
::Error
::new(io
::ErrorKind
::InvalidInput
,
280 "cannot set a 0 duration timeout"));
283 let secs
= if dur
.as_secs() > libc
::time_t
::max_value() as u64 {
284 libc
::time_t
::max_value()
286 dur
.as_secs() as libc
::time_t
288 let mut timeout
= libc
::timeval
{
290 tv_usec
: (dur
.subsec_nanos() / 1000) as libc
::suseconds_t
,
292 if timeout
.tv_sec
== 0 && timeout
.tv_usec
== 0 {
304 setsockopt(self, libc
::SOL_SOCKET
, kind
, timeout
)
307 pub fn timeout(&self, kind
: libc
::c_int
) -> io
::Result
<Option
<Duration
>> {
308 let raw
: libc
::timeval
= getsockopt(self, libc
::SOL_SOCKET
, kind
)?
;
309 if raw
.tv_sec
== 0 && raw
.tv_usec
== 0 {
312 let sec
= raw
.tv_sec
as u64;
313 let nsec
= (raw
.tv_usec
as u32) * 1000;
314 Ok(Some(Duration
::new(sec
, nsec
)))
318 pub fn shutdown(&self, how
: Shutdown
) -> io
::Result
<()> {
319 let how
= match how
{
320 Shutdown
::Write
=> libc
::SHUT_WR
,
321 Shutdown
::Read
=> libc
::SHUT_RD
,
322 Shutdown
::Both
=> libc
::SHUT_RDWR
,
324 cvt(unsafe { libc::shutdown(self.0.raw(), how) }
)?
;
328 pub fn set_nodelay(&self, nodelay
: bool
) -> io
::Result
<()> {
329 setsockopt(self, libc
::IPPROTO_TCP
, libc
::TCP_NODELAY
, nodelay
as c_int
)
332 pub fn nodelay(&self) -> io
::Result
<bool
> {
333 let raw
: c_int
= getsockopt(self, libc
::IPPROTO_TCP
, libc
::TCP_NODELAY
)?
;
337 pub fn set_nonblocking(&self, nonblocking
: bool
) -> io
::Result
<()> {
338 let mut nonblocking
= nonblocking
as libc
::c_int
;
339 cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }
).map(|_
| ())
342 pub fn take_error(&self) -> io
::Result
<Option
<io
::Error
>> {
343 let raw
: c_int
= getsockopt(self, libc
::SOL_SOCKET
, libc
::SO_ERROR
)?
;
347 Ok(Some(io
::Error
::from_raw_os_error(raw
as i32)))
352 impl AsInner
<c_int
> for Socket
{
353 fn as_inner(&self) -> &c_int { self.0.as_inner() }
356 impl FromInner
<c_int
> for Socket
{
357 fn from_inner(fd
: c_int
) -> Socket { Socket(FileDesc::new(fd)) }
360 impl IntoInner
<c_int
> for Socket
{
361 fn into_inner(self) -> c_int { self.0.into_raw() }
364 // In versions of glibc prior to 2.26, there's a bug where the DNS resolver
365 // will cache the contents of /etc/resolv.conf, so changes to that file on disk
366 // can be ignored by a long-running program. That can break DNS lookups on e.g.
367 // laptops where the network comes and goes. See
368 // https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some
369 // distros including Debian have patched glibc to fix this for a long time.
371 // A workaround for this bug is to call the res_init libc function, to clear
372 // the cached configs. Unfortunately, while we believe glibc's implementation
373 // of res_init is thread-safe, we know that other implementations are not
374 // (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
375 // try to synchronize its res_init calls with a Mutex, but that wouldn't
376 // protect programs that call into libc in other ways. So instead of calling
377 // res_init unconditionally, we call it only when we detect we're linking
378 // against glibc version < 2.26. (That is, when we both know its needed and
379 // believe it's thread-safe).
380 pub fn res_init_if_glibc_before_2_26() -> io
::Result
<()> {
381 // If the version fails to parse, we treat it the same as "not glibc".
382 if let Some(Ok(version_str
)) = glibc_version_cstr().map(CStr
::to_str
) {
383 if let Some(version
) = parse_glibc_version(version_str
) {
384 if version
< (2, 26) {
385 let ret
= unsafe { libc::res_init() }
;
387 return Err(io
::Error
::last_os_error());
395 fn glibc_version_cstr() -> Option
<&'
static CStr
> {
397 fn gnu_get_libc_version() -> *const libc
::c_char
399 if let Some(f
) = gnu_get_libc_version
.get() {
400 unsafe { Some(CStr::from_ptr(f())) }
406 // Returns Some((major, minor)) if the string is a valid "x.y" version,
407 // ignoring any extra dot-separated parts. Otherwise return None.
408 fn parse_glibc_version(version
: &str) -> Option
<(usize, usize)> {
409 let mut parsed_ints
= version
.split(".").map(str::parse
::<usize>).fuse();
410 match (parsed_ints
.next(), parsed_ints
.next()) {
411 (Some(Ok(major
)), Some(Ok(minor
))) => Some((major
, minor
)),
422 // This mostly just tests that the weak linkage doesn't panic wildly...
423 res_init_if_glibc_before_2_26().unwrap();
427 fn test_parse_glibc_version() {
429 ("0.0", Some((0, 0))),
430 ("01.+2", Some((1, 2))),
431 ("3.4.5.six", Some((3, 4))),
437 for &(version_str
, parsed
) in cases
.iter() {
438 assert_eq
!(parsed
, parse_glibc_version(version_str
));