]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
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. | |
4 | // | |
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. | |
10 | ||
11 | use prelude::v1::*; | |
12 | ||
13 | use ffi::CStr; | |
14 | use io; | |
7453a54e | 15 | use libc::{self, c_int, size_t, sockaddr, socklen_t}; |
92a42be0 | 16 | use net::{SocketAddr, Shutdown}; |
85aaf69f | 17 | use str; |
85aaf69f | 18 | use sys::fd::FileDesc; |
c1a9b12d | 19 | use sys_common::{AsInner, FromInner, IntoInner}; |
62682a34 SL |
20 | use sys_common::net::{getsockopt, setsockopt}; |
21 | use time::Duration; | |
85aaf69f SL |
22 | |
23 | pub use sys::{cvt, cvt_r}; | |
7453a54e | 24 | pub extern crate libc as netc; |
85aaf69f SL |
25 | |
26 | pub type wrlen_t = size_t; | |
27 | ||
7453a54e SL |
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; | |
37 | ||
85aaf69f SL |
38 | pub struct Socket(FileDesc); |
39 | ||
40 | pub fn init() {} | |
41 | ||
42 | pub fn cvt_gai(err: c_int) -> io::Result<()> { | |
43 | if err == 0 { return Ok(()) } | |
44 | ||
45 | let detail = unsafe { | |
92a42be0 | 46 | str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap() |
e9174d1e | 47 | .to_owned() |
85aaf69f SL |
48 | }; |
49 | Err(io::Error::new(io::ErrorKind::Other, | |
c34b1796 AL |
50 | &format!("failed to lookup address information: {}", |
51 | detail)[..])) | |
85aaf69f SL |
52 | } |
53 | ||
54 | impl Socket { | |
55 | pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { | |
c34b1796 AL |
56 | let fam = match *addr { |
57 | SocketAddr::V4(..) => libc::AF_INET, | |
58 | SocketAddr::V6(..) => libc::AF_INET6, | |
85aaf69f | 59 | }; |
54a0048b SL |
60 | Socket::new_raw(fam, ty) |
61 | } | |
62 | ||
63 | pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { | |
85aaf69f | 64 | unsafe { |
7453a54e SL |
65 | // On linux we first attempt to pass the SOCK_CLOEXEC flag to |
66 | // atomically create the socket and set it as CLOEXEC. Support for | |
67 | // this option, however, was added in 2.6.27, and we still support | |
68 | // 2.6.18 as a kernel, so if the returned error is EINVAL we | |
69 | // fallthrough to the fallback. | |
54a0048b | 70 | if cfg!(linux) { |
7453a54e SL |
71 | match cvt(libc::socket(fam, ty | SOCK_CLOEXEC, 0)) { |
72 | Ok(fd) => return Ok(Socket(FileDesc::new(fd))), | |
73 | Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} | |
74 | Err(e) => return Err(e), | |
75 | } | |
76 | } | |
77 | ||
54a0048b | 78 | let fd = cvt(libc::socket(fam, ty, 0))?; |
9346a6ac AL |
79 | let fd = FileDesc::new(fd); |
80 | fd.set_cloexec(); | |
81 | Ok(Socket(fd)) | |
85aaf69f SL |
82 | } |
83 | } | |
84 | ||
54a0048b SL |
85 | pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { |
86 | unsafe { | |
87 | let mut fds = [0, 0]; | |
88 | ||
89 | // Like above, see if we can set cloexec atomically | |
90 | if cfg!(linux) { | |
91 | match cvt(libc::socketpair(fam, ty | SOCK_CLOEXEC, 0, fds.as_mut_ptr())) { | |
92 | Ok(_) => { | |
93 | return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))); | |
94 | } | |
95 | Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}, | |
96 | Err(e) => return Err(e), | |
97 | } | |
98 | } | |
99 | ||
100 | cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; | |
101 | let a = FileDesc::new(fds[0]); | |
102 | a.set_cloexec(); | |
103 | let b = FileDesc::new(fds[1]); | |
104 | b.set_cloexec(); | |
105 | Ok((Socket(a), Socket(b))) | |
106 | } | |
107 | } | |
108 | ||
7453a54e SL |
109 | pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) |
110 | -> io::Result<Socket> { | |
111 | // Unfortunately the only known way right now to accept a socket and | |
112 | // atomically set the CLOEXEC flag is to use the `accept4` syscall on | |
113 | // Linux. This was added in 2.6.28, however, and because we support | |
114 | // 2.6.18 we must detect this support dynamically. | |
115 | if cfg!(target_os = "linux") { | |
116 | weak! { | |
117 | fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int | |
118 | } | |
119 | if let Some(accept) = accept4.get() { | |
120 | let res = cvt_r(|| unsafe { | |
121 | accept(self.0.raw(), storage, len, SOCK_CLOEXEC) | |
122 | }); | |
123 | match res { | |
124 | Ok(fd) => return Ok(Socket(FileDesc::new(fd))), | |
125 | Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} | |
126 | Err(e) => return Err(e), | |
127 | } | |
128 | } | |
129 | } | |
130 | ||
54a0048b | 131 | let fd = cvt_r(|| unsafe { |
85aaf69f | 132 | libc::accept(self.0.raw(), storage, len) |
54a0048b | 133 | })?; |
9346a6ac AL |
134 | let fd = FileDesc::new(fd); |
135 | fd.set_cloexec(); | |
136 | Ok(Socket(fd)) | |
85aaf69f SL |
137 | } |
138 | ||
139 | pub fn duplicate(&self) -> io::Result<Socket> { | |
7453a54e | 140 | self.0.duplicate().map(Socket) |
85aaf69f SL |
141 | } |
142 | ||
143 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { | |
144 | self.0.read(buf) | |
145 | } | |
62682a34 | 146 | |
54a0048b SL |
147 | pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { |
148 | self.0.read_to_end(buf) | |
149 | } | |
150 | ||
151 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { | |
152 | self.0.write(buf) | |
153 | } | |
154 | ||
62682a34 SL |
155 | pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> { |
156 | let timeout = match dur { | |
157 | Some(dur) => { | |
c1a9b12d | 158 | if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { |
62682a34 SL |
159 | return Err(io::Error::new(io::ErrorKind::InvalidInput, |
160 | "cannot set a 0 duration timeout")); | |
161 | } | |
162 | ||
c1a9b12d | 163 | let secs = if dur.as_secs() > libc::time_t::max_value() as u64 { |
62682a34 SL |
164 | libc::time_t::max_value() |
165 | } else { | |
c1a9b12d | 166 | dur.as_secs() as libc::time_t |
62682a34 SL |
167 | }; |
168 | let mut timeout = libc::timeval { | |
169 | tv_sec: secs, | |
c1a9b12d | 170 | tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, |
62682a34 SL |
171 | }; |
172 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { | |
173 | timeout.tv_usec = 1; | |
174 | } | |
175 | timeout | |
176 | } | |
177 | None => { | |
178 | libc::timeval { | |
179 | tv_sec: 0, | |
180 | tv_usec: 0, | |
181 | } | |
182 | } | |
183 | }; | |
184 | setsockopt(self, libc::SOL_SOCKET, kind, timeout) | |
185 | } | |
186 | ||
187 | pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> { | |
54a0048b | 188 | let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; |
62682a34 SL |
189 | if raw.tv_sec == 0 && raw.tv_usec == 0 { |
190 | Ok(None) | |
191 | } else { | |
192 | let sec = raw.tv_sec as u64; | |
193 | let nsec = (raw.tv_usec as u32) * 1000; | |
194 | Ok(Some(Duration::new(sec, nsec))) | |
195 | } | |
196 | } | |
92a42be0 SL |
197 | |
198 | pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { | |
199 | let how = match how { | |
200 | Shutdown::Write => libc::SHUT_WR, | |
201 | Shutdown::Read => libc::SHUT_RD, | |
202 | Shutdown::Both => libc::SHUT_RDWR, | |
203 | }; | |
54a0048b | 204 | cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; |
92a42be0 SL |
205 | Ok(()) |
206 | } | |
54a0048b SL |
207 | |
208 | pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { | |
209 | setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) | |
210 | } | |
211 | ||
212 | pub fn nodelay(&self) -> io::Result<bool> { | |
213 | let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; | |
214 | Ok(raw != 0) | |
215 | } | |
216 | ||
217 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { | |
218 | let mut nonblocking = nonblocking as libc::c_ulong; | |
219 | cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(|_| ()) | |
220 | } | |
221 | ||
222 | pub fn take_error(&self) -> io::Result<Option<io::Error>> { | |
223 | let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; | |
224 | if raw == 0 { | |
225 | Ok(None) | |
226 | } else { | |
227 | Ok(Some(io::Error::from_raw_os_error(raw as i32))) | |
228 | } | |
229 | } | |
85aaf69f SL |
230 | } |
231 | ||
232 | impl AsInner<c_int> for Socket { | |
233 | fn as_inner(&self) -> &c_int { self.0.as_inner() } | |
234 | } | |
c34b1796 AL |
235 | |
236 | impl FromInner<c_int> for Socket { | |
237 | fn from_inner(fd: c_int) -> Socket { Socket(FileDesc::new(fd)) } | |
238 | } | |
c1a9b12d SL |
239 | |
240 | impl IntoInner<c_int> for Socket { | |
241 | fn into_inner(self) -> c_int { self.0.into_raw() } | |
242 | } |