]>
Commit | Line | Data |
---|---|---|
0531ce1d XL |
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 std::cmp; | |
5869c6ff XL |
12 | #[cfg(target_os = "linux")] |
13 | use std::ffi::{CStr, CString}; | |
0531ce1d | 14 | use std::fmt; |
0531ce1d | 15 | use std::io; |
dfeec247 | 16 | use std::io::{ErrorKind, Read, Write}; |
0531ce1d | 17 | use std::mem; |
5869c6ff XL |
18 | #[cfg(target_os = "linux")] |
19 | use std::mem::MaybeUninit; | |
0531ce1d XL |
20 | use std::net::Shutdown; |
21 | use std::net::{self, Ipv4Addr, Ipv6Addr}; | |
22 | use std::ops::Neg; | |
0531ce1d XL |
23 | #[cfg(feature = "unix")] |
24 | use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; | |
dfeec247 | 25 | use std::os::unix::prelude::*; |
5869c6ff XL |
26 | #[cfg(target_os = "linux")] |
27 | use std::ptr; | |
28 | #[cfg(target_os = "linux")] | |
29 | use std::slice; | |
dfeec247 XL |
30 | use std::sync::atomic::{AtomicBool, Ordering}; |
31 | use std::time::{Duration, Instant}; | |
0531ce1d | 32 | |
ba9703b0 XL |
33 | use libc::{self, c_void, socklen_t, ssize_t}; |
34 | ||
35 | use crate::{Domain, Type}; | |
36 | ||
37 | pub use libc::c_int; | |
38 | ||
39 | // Used in `Domain`. | |
40 | pub(crate) use libc::{AF_INET, AF_INET6}; | |
41 | // Used in `Type`. | |
5869c6ff XL |
42 | #[cfg(not(target_os = "redox"))] |
43 | pub(crate) use libc::SOCK_RAW; | |
44 | pub(crate) use libc::{SOCK_DGRAM, SOCK_SEQPACKET, SOCK_STREAM}; | |
ba9703b0 XL |
45 | // Used in `Protocol`. |
46 | pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP}; | |
0531ce1d | 47 | |
dfeec247 | 48 | cfg_if::cfg_if! { |
0531ce1d XL |
49 | if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", |
50 | target_os = "ios", target_os = "macos", | |
51 | target_os = "openbsd", target_os = "netbsd", | |
ba9703b0 XL |
52 | target_os = "solaris", target_os = "illumos", |
53 | target_os = "haiku"))] { | |
0531ce1d XL |
54 | use libc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; |
55 | use libc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; | |
56 | } else { | |
57 | use libc::IPV6_ADD_MEMBERSHIP; | |
58 | use libc::IPV6_DROP_MEMBERSHIP; | |
59 | } | |
60 | } | |
61 | ||
dfeec247 | 62 | cfg_if::cfg_if! { |
0531ce1d XL |
63 | if #[cfg(any(target_os = "macos", target_os = "ios"))] { |
64 | use libc::TCP_KEEPALIVE as KEEPALIVE_OPTION; | |
65 | } else if #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))] { | |
66 | use libc::SO_KEEPALIVE as KEEPALIVE_OPTION; | |
67 | } else { | |
68 | use libc::TCP_KEEPIDLE as KEEPALIVE_OPTION; | |
69 | } | |
70 | } | |
71 | ||
dfeec247 XL |
72 | use crate::utils::One; |
73 | use crate::SockAddr; | |
0531ce1d | 74 | |
ba9703b0 XL |
75 | /// Unix only API. |
76 | impl Domain { | |
77 | /// Domain for Unix socket communication, corresponding to `AF_UNIX`. | |
78 | pub fn unix() -> Domain { | |
79 | Domain(libc::AF_UNIX) | |
80 | } | |
81 | ||
82 | /// Domain for low-level packet interface, corresponding to `AF_PACKET`. | |
83 | /// | |
84 | /// # Notes | |
85 | /// | |
86 | /// This function is only available on Linux. | |
87 | #[cfg(target_os = "linux")] | |
88 | pub fn packet() -> Domain { | |
89 | Domain(libc::AF_PACKET) | |
90 | } | |
91 | } | |
92 | ||
93 | impl_debug!( | |
94 | Domain, | |
95 | libc::AF_INET, | |
96 | libc::AF_INET6, | |
97 | libc::AF_UNIX, | |
98 | libc::AF_UNSPEC, // = 0. | |
99 | ); | |
100 | ||
101 | /// Unix only API. | |
102 | impl Type { | |
103 | /// Set `SOCK_NONBLOCK` on the `Type`. | |
104 | /// | |
105 | /// # Notes | |
106 | /// | |
107 | /// This function is only available on Android, DragonFlyBSD, FreeBSD, | |
108 | /// Linux, NetBSD and OpenBSD. | |
109 | #[cfg(any( | |
110 | target_os = "android", | |
111 | target_os = "dragonfly", | |
112 | target_os = "freebsd", | |
113 | target_os = "linux", | |
114 | target_os = "netbsd", | |
115 | target_os = "openbsd" | |
116 | ))] | |
117 | pub fn non_blocking(self) -> Type { | |
118 | Type(self.0 | libc::SOCK_NONBLOCK) | |
119 | } | |
120 | ||
121 | /// Set `SOCK_CLOEXEC` on the `Type`. | |
122 | /// | |
123 | /// # Notes | |
124 | /// | |
125 | /// This function is only available on Android, DragonFlyBSD, FreeBSD, | |
126 | /// Linux, NetBSD and OpenBSD. | |
127 | #[cfg(any( | |
128 | target_os = "android", | |
129 | target_os = "dragonfly", | |
130 | target_os = "freebsd", | |
131 | target_os = "linux", | |
132 | target_os = "netbsd", | |
133 | target_os = "openbsd" | |
134 | ))] | |
135 | pub fn cloexec(self) -> Type { | |
136 | Type(self.0 | libc::SOCK_CLOEXEC) | |
137 | } | |
138 | } | |
139 | ||
140 | impl_debug!( | |
141 | crate::Type, | |
142 | libc::SOCK_STREAM, | |
143 | libc::SOCK_DGRAM, | |
5869c6ff | 144 | #[cfg(not(target_os = "redox"))] |
ba9703b0 | 145 | libc::SOCK_RAW, |
5869c6ff | 146 | #[cfg(not(any(target_os = "haiku", target_os = "redox")))] |
ba9703b0 XL |
147 | libc::SOCK_RDM, |
148 | libc::SOCK_SEQPACKET, | |
149 | /* TODO: add these optional bit OR-ed flags: | |
150 | #[cfg(any( | |
151 | target_os = "android", | |
152 | target_os = "dragonfly", | |
153 | target_os = "freebsd", | |
154 | target_os = "linux", | |
155 | target_os = "netbsd", | |
156 | target_os = "openbsd" | |
157 | ))] | |
158 | libc::SOCK_NONBLOCK, | |
159 | #[cfg(any( | |
160 | target_os = "android", | |
161 | target_os = "dragonfly", | |
162 | target_os = "freebsd", | |
163 | target_os = "linux", | |
164 | target_os = "netbsd", | |
165 | target_os = "openbsd" | |
166 | ))] | |
167 | libc::SOCK_CLOEXEC, | |
168 | */ | |
169 | ); | |
170 | ||
171 | impl_debug!( | |
172 | crate::Protocol, | |
173 | libc::IPPROTO_ICMP, | |
174 | libc::IPPROTO_ICMPV6, | |
175 | libc::IPPROTO_TCP, | |
176 | libc::IPPROTO_UDP, | |
177 | ); | |
0531ce1d | 178 | |
0531ce1d XL |
179 | pub struct Socket { |
180 | fd: c_int, | |
181 | } | |
182 | ||
183 | impl Socket { | |
184 | pub fn new(family: c_int, ty: c_int, protocol: c_int) -> io::Result<Socket> { | |
185 | unsafe { | |
186 | // On linux we first attempt to pass the SOCK_CLOEXEC flag to | |
187 | // atomically create the socket and set it as CLOEXEC. Support for | |
188 | // this option, however, was added in 2.6.27, and we still support | |
189 | // 2.6.18 as a kernel, so if the returned error is EINVAL we | |
190 | // fallthrough to the fallback. | |
191 | #[cfg(target_os = "linux")] | |
192 | { | |
193 | match cvt(libc::socket(family, ty | libc::SOCK_CLOEXEC, protocol)) { | |
194 | Ok(fd) => return Ok(Socket::from_raw_fd(fd)), | |
195 | Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} | |
196 | Err(e) => return Err(e), | |
197 | } | |
198 | } | |
199 | ||
200 | let fd = cvt(libc::socket(family, ty, protocol))?; | |
201 | let fd = Socket::from_raw_fd(fd); | |
202 | set_cloexec(fd.as_raw_fd())?; | |
fc512014 | 203 | #[cfg(any(target_os = "macos", target_os = "ios"))] |
0531ce1d XL |
204 | { |
205 | fd.setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?; | |
206 | } | |
207 | Ok(fd) | |
208 | } | |
209 | } | |
210 | ||
211 | pub fn pair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<(Socket, Socket)> { | |
212 | unsafe { | |
213 | let mut fds = [0, 0]; | |
214 | cvt(libc::socketpair(family, ty, protocol, fds.as_mut_ptr()))?; | |
215 | let fds = (Socket::from_raw_fd(fds[0]), Socket::from_raw_fd(fds[1])); | |
216 | set_cloexec(fds.0.as_raw_fd())?; | |
217 | set_cloexec(fds.1.as_raw_fd())?; | |
fc512014 | 218 | #[cfg(any(target_os = "macos", target_os = "ios"))] |
0531ce1d XL |
219 | { |
220 | fds.0 | |
221 | .setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?; | |
222 | fds.1 | |
223 | .setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?; | |
224 | } | |
225 | Ok(fds) | |
226 | } | |
227 | } | |
228 | ||
229 | pub fn bind(&self, addr: &SockAddr) -> io::Result<()> { | |
230 | unsafe { cvt(libc::bind(self.fd, addr.as_ptr(), addr.len() as _)).map(|_| ()) } | |
231 | } | |
232 | ||
233 | pub fn listen(&self, backlog: i32) -> io::Result<()> { | |
234 | unsafe { cvt(libc::listen(self.fd, backlog)).map(|_| ()) } | |
235 | } | |
236 | ||
237 | pub fn connect(&self, addr: &SockAddr) -> io::Result<()> { | |
238 | unsafe { cvt(libc::connect(self.fd, addr.as_ptr(), addr.len())).map(|_| ()) } | |
239 | } | |
240 | ||
241 | pub fn connect_timeout(&self, addr: &SockAddr, timeout: Duration) -> io::Result<()> { | |
242 | self.set_nonblocking(true)?; | |
243 | let r = self.connect(addr); | |
244 | self.set_nonblocking(false)?; | |
245 | ||
246 | match r { | |
247 | Ok(()) => return Ok(()), | |
248 | // there's no io::ErrorKind conversion registered for EINPROGRESS :( | |
249 | Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} | |
250 | Err(e) => return Err(e), | |
251 | } | |
252 | ||
253 | let mut pollfd = libc::pollfd { | |
254 | fd: self.fd, | |
255 | events: libc::POLLOUT, | |
256 | revents: 0, | |
257 | }; | |
258 | ||
259 | if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { | |
260 | return Err(io::Error::new( | |
261 | io::ErrorKind::InvalidInput, | |
262 | "cannot set a 0 duration timeout", | |
263 | )); | |
264 | } | |
265 | ||
266 | let start = Instant::now(); | |
267 | ||
268 | loop { | |
269 | let elapsed = start.elapsed(); | |
270 | if elapsed >= timeout { | |
271 | return Err(io::Error::new( | |
272 | io::ErrorKind::TimedOut, | |
273 | "connection timed out", | |
274 | )); | |
275 | } | |
276 | ||
277 | let timeout = timeout - elapsed; | |
278 | let mut timeout = timeout | |
279 | .as_secs() | |
280 | .saturating_mul(1_000) | |
281 | .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); | |
282 | if timeout == 0 { | |
283 | timeout = 1; | |
284 | } | |
285 | ||
286 | let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int; | |
287 | ||
288 | match unsafe { libc::poll(&mut pollfd, 1, timeout) } { | |
289 | -1 => { | |
290 | let err = io::Error::last_os_error(); | |
291 | if err.kind() != io::ErrorKind::Interrupted { | |
292 | return Err(err); | |
293 | } | |
294 | } | |
295 | 0 => { | |
296 | return Err(io::Error::new( | |
297 | io::ErrorKind::TimedOut, | |
298 | "connection timed out", | |
299 | )) | |
300 | } | |
301 | _ => { | |
302 | // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look | |
303 | // for POLLHUP rather than read readiness | |
304 | if pollfd.revents & libc::POLLHUP != 0 { | |
305 | let e = self.take_error()?.unwrap_or_else(|| { | |
306 | io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") | |
307 | }); | |
308 | return Err(e); | |
309 | } | |
310 | return Ok(()); | |
311 | } | |
312 | } | |
313 | } | |
314 | } | |
315 | ||
316 | pub fn local_addr(&self) -> io::Result<SockAddr> { | |
317 | unsafe { | |
318 | let mut storage: libc::sockaddr_storage = mem::zeroed(); | |
319 | let mut len = mem::size_of_val(&storage) as libc::socklen_t; | |
320 | cvt(libc::getsockname( | |
321 | self.fd, | |
322 | &mut storage as *mut _ as *mut _, | |
323 | &mut len, | |
324 | ))?; | |
325 | Ok(SockAddr::from_raw_parts( | |
326 | &storage as *const _ as *const _, | |
327 | len, | |
328 | )) | |
329 | } | |
330 | } | |
331 | ||
332 | pub fn peer_addr(&self) -> io::Result<SockAddr> { | |
333 | unsafe { | |
334 | let mut storage: libc::sockaddr_storage = mem::zeroed(); | |
335 | let mut len = mem::size_of_val(&storage) as libc::socklen_t; | |
336 | cvt(libc::getpeername( | |
337 | self.fd, | |
338 | &mut storage as *mut _ as *mut _, | |
339 | &mut len, | |
340 | ))?; | |
341 | Ok(SockAddr::from_raw_parts( | |
342 | &storage as *const _ as *const _, | |
343 | len, | |
344 | )) | |
345 | } | |
346 | } | |
347 | ||
348 | pub fn try_clone(&self) -> io::Result<Socket> { | |
349 | // implementation lifted from libstd | |
350 | #[cfg(any(target_os = "android", target_os = "haiku"))] | |
351 | use libc::F_DUPFD as F_DUPFD_CLOEXEC; | |
352 | #[cfg(not(any(target_os = "android", target_os = "haiku")))] | |
353 | use libc::F_DUPFD_CLOEXEC; | |
354 | ||
dfeec247 | 355 | static CLOEXEC_FAILED: AtomicBool = AtomicBool::new(false); |
0531ce1d XL |
356 | unsafe { |
357 | if !CLOEXEC_FAILED.load(Ordering::Relaxed) { | |
358 | match cvt(libc::fcntl(self.fd, F_DUPFD_CLOEXEC, 0)) { | |
359 | Ok(fd) => { | |
360 | let fd = Socket::from_raw_fd(fd); | |
361 | if cfg!(target_os = "linux") { | |
362 | set_cloexec(fd.as_raw_fd())?; | |
363 | } | |
364 | return Ok(fd); | |
365 | } | |
366 | Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { | |
367 | CLOEXEC_FAILED.store(true, Ordering::Relaxed); | |
368 | } | |
369 | Err(e) => return Err(e), | |
370 | } | |
371 | } | |
372 | let fd = cvt(libc::fcntl(self.fd, libc::F_DUPFD, 0))?; | |
373 | let fd = Socket::from_raw_fd(fd); | |
374 | set_cloexec(fd.as_raw_fd())?; | |
375 | Ok(fd) | |
376 | } | |
377 | } | |
378 | ||
379 | #[allow(unused_mut)] | |
380 | pub fn accept(&self) -> io::Result<(Socket, SockAddr)> { | |
381 | let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; | |
382 | let mut len = mem::size_of_val(&storage) as socklen_t; | |
383 | ||
384 | let mut socket = None; | |
385 | #[cfg(target_os = "linux")] | |
386 | { | |
dfeec247 XL |
387 | let res = cvt_r(|| unsafe { |
388 | libc::syscall( | |
389 | libc::SYS_accept4, | |
390 | self.fd as libc::c_long, | |
391 | &mut storage as *mut _ as libc::c_long, | |
392 | &mut len, | |
393 | libc::SOCK_CLOEXEC as libc::c_long, | |
394 | ) as libc::c_int | |
395 | }); | |
396 | match res { | |
397 | Ok(fd) => socket = Some(Socket { fd: fd }), | |
398 | Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} | |
399 | Err(e) => return Err(e), | |
0531ce1d XL |
400 | } |
401 | } | |
402 | ||
403 | let socket = match socket { | |
404 | Some(socket) => socket, | |
405 | None => unsafe { | |
406 | let fd = | |
407 | cvt_r(|| libc::accept(self.fd, &mut storage as *mut _ as *mut _, &mut len))?; | |
408 | let fd = Socket::from_raw_fd(fd); | |
409 | set_cloexec(fd.as_raw_fd())?; | |
410 | fd | |
411 | }, | |
412 | }; | |
413 | let addr = unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, len) }; | |
414 | Ok((socket, addr)) | |
415 | } | |
416 | ||
417 | pub fn take_error(&self) -> io::Result<Option<io::Error>> { | |
418 | unsafe { | |
419 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_ERROR)?; | |
420 | if raw == 0 { | |
421 | Ok(None) | |
422 | } else { | |
423 | Ok(Some(io::Error::from_raw_os_error(raw as i32))) | |
424 | } | |
425 | } | |
426 | } | |
427 | ||
428 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { | |
429 | unsafe { | |
430 | let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; | |
431 | let new = if nonblocking { | |
432 | previous | libc::O_NONBLOCK | |
433 | } else { | |
434 | previous & !libc::O_NONBLOCK | |
435 | }; | |
436 | if new != previous { | |
437 | cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; | |
438 | } | |
439 | Ok(()) | |
440 | } | |
441 | } | |
442 | ||
443 | pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { | |
444 | let how = match how { | |
445 | Shutdown::Write => libc::SHUT_WR, | |
446 | Shutdown::Read => libc::SHUT_RD, | |
447 | Shutdown::Both => libc::SHUT_RDWR, | |
448 | }; | |
449 | cvt(unsafe { libc::shutdown(self.fd, how) })?; | |
450 | Ok(()) | |
451 | } | |
452 | ||
ba9703b0 | 453 | pub fn recv(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { |
0531ce1d XL |
454 | unsafe { |
455 | let n = cvt({ | |
456 | libc::recv( | |
457 | self.fd, | |
458 | buf.as_mut_ptr() as *mut c_void, | |
459 | cmp::min(buf.len(), max_len()), | |
ba9703b0 | 460 | flags, |
0531ce1d XL |
461 | ) |
462 | })?; | |
463 | Ok(n as usize) | |
464 | } | |
465 | } | |
466 | ||
467 | pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { | |
468 | unsafe { | |
469 | let n = cvt({ | |
470 | libc::recv( | |
471 | self.fd, | |
472 | buf.as_mut_ptr() as *mut c_void, | |
473 | cmp::min(buf.len(), max_len()), | |
474 | libc::MSG_PEEK, | |
475 | ) | |
476 | })?; | |
477 | Ok(n as usize) | |
478 | } | |
479 | } | |
480 | ||
0531ce1d | 481 | pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SockAddr)> { |
ba9703b0 | 482 | self.recv_from(buf, libc::MSG_PEEK) |
0531ce1d XL |
483 | } |
484 | ||
ba9703b0 | 485 | pub fn recv_from(&self, buf: &mut [u8], flags: c_int) -> io::Result<(usize, SockAddr)> { |
0531ce1d XL |
486 | unsafe { |
487 | let mut storage: libc::sockaddr_storage = mem::zeroed(); | |
488 | let mut addrlen = mem::size_of_val(&storage) as socklen_t; | |
489 | ||
490 | let n = cvt({ | |
491 | libc::recvfrom( | |
492 | self.fd, | |
493 | buf.as_mut_ptr() as *mut c_void, | |
494 | cmp::min(buf.len(), max_len()), | |
495 | flags, | |
496 | &mut storage as *mut _ as *mut _, | |
497 | &mut addrlen, | |
498 | ) | |
499 | })?; | |
500 | let addr = SockAddr::from_raw_parts(&storage as *const _ as *const _, addrlen); | |
501 | Ok((n as usize, addr)) | |
502 | } | |
503 | } | |
504 | ||
ba9703b0 | 505 | pub fn send(&self, buf: &[u8], flags: c_int) -> io::Result<usize> { |
0531ce1d XL |
506 | unsafe { |
507 | let n = cvt({ | |
508 | libc::send( | |
509 | self.fd, | |
510 | buf.as_ptr() as *const c_void, | |
511 | cmp::min(buf.len(), max_len()), | |
ba9703b0 | 512 | flags, |
0531ce1d XL |
513 | ) |
514 | })?; | |
515 | Ok(n as usize) | |
516 | } | |
517 | } | |
518 | ||
ba9703b0 | 519 | pub fn send_to(&self, buf: &[u8], flags: c_int, addr: &SockAddr) -> io::Result<usize> { |
0531ce1d XL |
520 | unsafe { |
521 | let n = cvt({ | |
522 | libc::sendto( | |
523 | self.fd, | |
524 | buf.as_ptr() as *const c_void, | |
525 | cmp::min(buf.len(), max_len()), | |
ba9703b0 | 526 | flags, |
0531ce1d XL |
527 | addr.as_ptr(), |
528 | addr.len(), | |
529 | ) | |
530 | })?; | |
531 | Ok(n as usize) | |
532 | } | |
533 | } | |
534 | ||
535 | // ================================================ | |
536 | ||
537 | pub fn ttl(&self) -> io::Result<u32> { | |
538 | unsafe { | |
539 | let raw: c_int = self.getsockopt(libc::IPPROTO_IP, libc::IP_TTL)?; | |
540 | Ok(raw as u32) | |
541 | } | |
542 | } | |
543 | ||
544 | pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { | |
545 | unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_TTL, ttl as c_int) } | |
546 | } | |
547 | ||
5869c6ff XL |
548 | #[cfg(not(target_os = "redox"))] |
549 | pub fn mss(&self) -> io::Result<u32> { | |
550 | unsafe { | |
551 | let raw: c_int = self.getsockopt(libc::IPPROTO_TCP, libc::TCP_MAXSEG)?; | |
552 | Ok(raw as u32) | |
553 | } | |
554 | } | |
555 | ||
556 | #[cfg(not(target_os = "redox"))] | |
557 | pub fn set_mss(&self, mss: u32) -> io::Result<()> { | |
558 | unsafe { self.setsockopt(libc::IPPROTO_TCP, libc::TCP_MAXSEG, mss as c_int) } | |
559 | } | |
560 | ||
561 | #[cfg(target_os = "linux")] | |
562 | pub fn mark(&self) -> io::Result<u32> { | |
563 | unsafe { | |
564 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_MARK)?; | |
565 | Ok(raw as u32) | |
566 | } | |
567 | } | |
568 | ||
569 | #[cfg(target_os = "linux")] | |
570 | pub fn set_mark(&self, mark: u32) -> io::Result<()> { | |
571 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_MARK, mark as c_int) } | |
572 | } | |
573 | ||
574 | #[cfg(target_os = "linux")] | |
575 | pub fn device(&self) -> io::Result<Option<CString>> { | |
576 | // TODO: replace with `MaybeUninit::uninit_array` once stable. | |
577 | let mut buf: [MaybeUninit<u8>; libc::IFNAMSIZ] = | |
578 | unsafe { MaybeUninit::<[MaybeUninit<u8>; libc::IFNAMSIZ]>::uninit().assume_init() }; | |
579 | let mut len = buf.len() as libc::socklen_t; | |
580 | let len = unsafe { | |
581 | cvt(libc::getsockopt( | |
582 | self.fd, | |
583 | libc::SOL_SOCKET, | |
584 | libc::SO_BINDTODEVICE, | |
585 | buf.as_mut_ptr().cast(), | |
586 | &mut len, | |
587 | ))? | |
588 | }; | |
589 | if len == 0 { | |
590 | Ok(None) | |
591 | } else { | |
592 | // Allocate a buffer for `CString` with the length including the | |
593 | // null terminator. | |
594 | let len = len as usize; | |
595 | let mut name = Vec::with_capacity(len); | |
596 | ||
597 | // TODO: use `MaybeUninit::slice_assume_init_ref` once stable. | |
598 | // Safety: `len` bytes are writen by the OS, this includes a null | |
599 | // terminator. However we don't copy the null terminator because | |
600 | // `CString::from_vec_unchecked` adds its own null terminator. | |
601 | let buf = unsafe { slice::from_raw_parts(buf.as_ptr().cast(), len - 1) }; | |
602 | name.extend_from_slice(buf); | |
603 | ||
604 | // Safety: the OS initialised the string for us, which shouldn't | |
605 | // include any null bytes. | |
606 | Ok(Some(unsafe { CString::from_vec_unchecked(name) })) | |
607 | } | |
608 | } | |
609 | ||
610 | #[cfg(target_os = "linux")] | |
611 | pub fn bind_device(&self, interface: Option<&CStr>) -> io::Result<()> { | |
612 | let (value, len) = if let Some(interface) = interface { | |
613 | (interface.as_ptr(), interface.to_bytes_with_nul().len()) | |
614 | } else { | |
615 | (ptr::null(), 0) | |
616 | }; | |
617 | ||
618 | unsafe { | |
619 | cvt(libc::setsockopt( | |
620 | self.fd, | |
621 | libc::SOL_SOCKET, | |
622 | libc::SO_BINDTODEVICE, | |
623 | value.cast(), | |
624 | len as libc::socklen_t, | |
625 | )) | |
626 | .map(|_| ()) | |
627 | } | |
628 | } | |
629 | ||
0531ce1d XL |
630 | pub fn unicast_hops_v6(&self) -> io::Result<u32> { |
631 | unsafe { | |
632 | let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS)?; | |
633 | Ok(raw as u32) | |
634 | } | |
635 | } | |
636 | ||
637 | pub fn set_unicast_hops_v6(&self, hops: u32) -> io::Result<()> { | |
638 | unsafe { | |
639 | self.setsockopt( | |
640 | libc::IPPROTO_IPV6 as c_int, | |
641 | libc::IPV6_UNICAST_HOPS, | |
642 | hops as c_int, | |
643 | ) | |
644 | } | |
645 | } | |
646 | ||
647 | pub fn only_v6(&self) -> io::Result<bool> { | |
648 | unsafe { | |
649 | let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_V6ONLY)?; | |
650 | Ok(raw != 0) | |
651 | } | |
652 | } | |
653 | ||
654 | pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { | |
655 | unsafe { self.setsockopt(libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, only_v6 as c_int) } | |
656 | } | |
657 | ||
658 | pub fn read_timeout(&self) -> io::Result<Option<Duration>> { | |
659 | unsafe { | |
dfeec247 XL |
660 | Ok(timeval2dur( |
661 | self.getsockopt(libc::SOL_SOCKET, libc::SO_RCVTIMEO)?, | |
662 | )) | |
0531ce1d XL |
663 | } |
664 | } | |
665 | ||
666 | pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | |
667 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_RCVTIMEO, dur2timeval(dur)?) } | |
668 | } | |
669 | ||
670 | pub fn write_timeout(&self) -> io::Result<Option<Duration>> { | |
671 | unsafe { | |
dfeec247 XL |
672 | Ok(timeval2dur( |
673 | self.getsockopt(libc::SOL_SOCKET, libc::SO_SNDTIMEO)?, | |
674 | )) | |
0531ce1d XL |
675 | } |
676 | } | |
677 | ||
678 | pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | |
679 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_SNDTIMEO, dur2timeval(dur)?) } | |
680 | } | |
681 | ||
682 | pub fn nodelay(&self) -> io::Result<bool> { | |
683 | unsafe { | |
684 | let raw: c_int = self.getsockopt(libc::IPPROTO_TCP, libc::TCP_NODELAY)?; | |
685 | Ok(raw != 0) | |
686 | } | |
687 | } | |
688 | ||
689 | pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { | |
690 | unsafe { self.setsockopt(libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) } | |
691 | } | |
692 | ||
693 | pub fn broadcast(&self) -> io::Result<bool> { | |
694 | unsafe { | |
695 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_BROADCAST)?; | |
696 | Ok(raw != 0) | |
697 | } | |
698 | } | |
699 | ||
700 | pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { | |
701 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_BROADCAST, broadcast as c_int) } | |
702 | } | |
703 | ||
704 | pub fn multicast_loop_v4(&self) -> io::Result<bool> { | |
705 | unsafe { | |
706 | let raw: c_int = self.getsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP)?; | |
707 | Ok(raw != 0) | |
708 | } | |
709 | } | |
710 | ||
711 | pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { | |
712 | unsafe { | |
713 | self.setsockopt( | |
714 | libc::IPPROTO_IP, | |
715 | libc::IP_MULTICAST_LOOP, | |
716 | multicast_loop_v4 as c_int, | |
717 | ) | |
718 | } | |
719 | } | |
720 | ||
721 | pub fn multicast_ttl_v4(&self) -> io::Result<u32> { | |
722 | unsafe { | |
723 | let raw: c_int = self.getsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_TTL)?; | |
724 | Ok(raw as u32) | |
725 | } | |
726 | } | |
727 | ||
728 | pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { | |
729 | unsafe { | |
730 | self.setsockopt( | |
731 | libc::IPPROTO_IP, | |
732 | libc::IP_MULTICAST_TTL, | |
733 | multicast_ttl_v4 as c_int, | |
734 | ) | |
735 | } | |
736 | } | |
737 | ||
738 | pub fn multicast_hops_v6(&self) -> io::Result<u32> { | |
739 | unsafe { | |
740 | let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_HOPS)?; | |
741 | Ok(raw as u32) | |
742 | } | |
743 | } | |
744 | ||
745 | pub fn set_multicast_hops_v6(&self, hops: u32) -> io::Result<()> { | |
746 | unsafe { self.setsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_HOPS, hops as c_int) } | |
747 | } | |
748 | ||
749 | pub fn multicast_if_v4(&self) -> io::Result<Ipv4Addr> { | |
750 | unsafe { | |
751 | let imr_interface: libc::in_addr = | |
752 | self.getsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_IF)?; | |
753 | Ok(from_s_addr(imr_interface.s_addr)) | |
754 | } | |
755 | } | |
756 | ||
757 | pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> { | |
758 | let interface = to_s_addr(interface); | |
759 | let imr_interface = libc::in_addr { s_addr: interface }; | |
760 | ||
761 | unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_IF, imr_interface) } | |
762 | } | |
763 | ||
764 | pub fn multicast_if_v6(&self) -> io::Result<u32> { | |
765 | unsafe { | |
766 | let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_IF)?; | |
767 | Ok(raw as u32) | |
768 | } | |
769 | } | |
770 | ||
771 | pub fn set_multicast_if_v6(&self, interface: u32) -> io::Result<()> { | |
772 | unsafe { | |
773 | self.setsockopt( | |
774 | libc::IPPROTO_IPV6, | |
775 | libc::IPV6_MULTICAST_IF, | |
776 | interface as c_int, | |
777 | ) | |
778 | } | |
779 | } | |
780 | ||
781 | pub fn multicast_loop_v6(&self) -> io::Result<bool> { | |
782 | unsafe { | |
783 | let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_LOOP)?; | |
784 | Ok(raw != 0) | |
785 | } | |
786 | } | |
787 | ||
788 | pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { | |
789 | unsafe { | |
790 | self.setsockopt( | |
791 | libc::IPPROTO_IPV6, | |
792 | libc::IPV6_MULTICAST_LOOP, | |
793 | multicast_loop_v6 as c_int, | |
794 | ) | |
795 | } | |
796 | } | |
797 | ||
798 | pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { | |
799 | let multiaddr = to_s_addr(multiaddr); | |
800 | let interface = to_s_addr(interface); | |
801 | let mreq = libc::ip_mreq { | |
802 | imr_multiaddr: libc::in_addr { s_addr: multiaddr }, | |
803 | imr_interface: libc::in_addr { s_addr: interface }, | |
804 | }; | |
805 | unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, mreq) } | |
806 | } | |
807 | ||
808 | pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { | |
809 | let multiaddr = to_in6_addr(multiaddr); | |
810 | let mreq = libc::ipv6_mreq { | |
811 | ipv6mr_multiaddr: multiaddr, | |
812 | ipv6mr_interface: to_ipv6mr_interface(interface), | |
813 | }; | |
814 | unsafe { self.setsockopt(libc::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) } | |
815 | } | |
816 | ||
817 | pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { | |
818 | let multiaddr = to_s_addr(multiaddr); | |
819 | let interface = to_s_addr(interface); | |
820 | let mreq = libc::ip_mreq { | |
821 | imr_multiaddr: libc::in_addr { s_addr: multiaddr }, | |
822 | imr_interface: libc::in_addr { s_addr: interface }, | |
823 | }; | |
824 | unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, mreq) } | |
825 | } | |
826 | ||
827 | pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { | |
828 | let multiaddr = to_in6_addr(multiaddr); | |
829 | let mreq = libc::ipv6_mreq { | |
830 | ipv6mr_multiaddr: multiaddr, | |
831 | ipv6mr_interface: to_ipv6mr_interface(interface), | |
832 | }; | |
833 | unsafe { self.setsockopt(libc::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) } | |
834 | } | |
835 | ||
836 | pub fn linger(&self) -> io::Result<Option<Duration>> { | |
837 | unsafe { | |
dfeec247 XL |
838 | Ok(linger2dur( |
839 | self.getsockopt(libc::SOL_SOCKET, libc::SO_LINGER)?, | |
840 | )) | |
0531ce1d XL |
841 | } |
842 | } | |
843 | ||
844 | pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> { | |
845 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_LINGER, dur2linger(dur)) } | |
846 | } | |
847 | ||
848 | pub fn set_reuse_address(&self, reuse: bool) -> io::Result<()> { | |
849 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_REUSEADDR, reuse as c_int) } | |
850 | } | |
851 | ||
852 | pub fn reuse_address(&self) -> io::Result<bool> { | |
853 | unsafe { | |
854 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_REUSEADDR)?; | |
855 | Ok(raw != 0) | |
856 | } | |
857 | } | |
858 | ||
859 | pub fn recv_buffer_size(&self) -> io::Result<usize> { | |
860 | unsafe { | |
861 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_RCVBUF)?; | |
862 | Ok(raw as usize) | |
863 | } | |
864 | } | |
865 | ||
866 | pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> { | |
867 | unsafe { | |
868 | // TODO: casting usize to a c_int should be a checked cast | |
869 | self.setsockopt(libc::SOL_SOCKET, libc::SO_RCVBUF, size as c_int) | |
870 | } | |
871 | } | |
872 | ||
873 | pub fn send_buffer_size(&self) -> io::Result<usize> { | |
874 | unsafe { | |
875 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_SNDBUF)?; | |
876 | Ok(raw as usize) | |
877 | } | |
878 | } | |
879 | ||
880 | pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> { | |
881 | unsafe { | |
882 | // TODO: casting usize to a c_int should be a checked cast | |
883 | self.setsockopt(libc::SOL_SOCKET, libc::SO_SNDBUF, size as c_int) | |
884 | } | |
885 | } | |
886 | ||
887 | pub fn keepalive(&self) -> io::Result<Option<Duration>> { | |
888 | unsafe { | |
889 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE)?; | |
890 | if raw == 0 { | |
891 | return Ok(None); | |
892 | } | |
893 | let secs: c_int = self.getsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION)?; | |
894 | Ok(Some(Duration::new(secs as u64, 0))) | |
895 | } | |
896 | } | |
897 | ||
898 | pub fn set_keepalive(&self, keepalive: Option<Duration>) -> io::Result<()> { | |
899 | unsafe { | |
900 | self.setsockopt( | |
901 | libc::SOL_SOCKET, | |
902 | libc::SO_KEEPALIVE, | |
903 | keepalive.is_some() as c_int, | |
904 | )?; | |
905 | if let Some(dur) = keepalive { | |
906 | // TODO: checked cast here | |
dfeec247 | 907 | self.setsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION, dur.as_secs() as c_int)?; |
0531ce1d XL |
908 | } |
909 | Ok(()) | |
910 | } | |
911 | } | |
912 | ||
ba9703b0 XL |
913 | #[cfg(all( |
914 | unix, | |
915 | not(any(target_os = "solaris", target_os = "illumos")), | |
916 | feature = "reuseport" | |
917 | ))] | |
0531ce1d XL |
918 | pub fn reuse_port(&self) -> io::Result<bool> { |
919 | unsafe { | |
920 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_REUSEPORT)?; | |
921 | Ok(raw != 0) | |
922 | } | |
923 | } | |
924 | ||
ba9703b0 XL |
925 | #[cfg(all( |
926 | unix, | |
927 | not(any(target_os = "solaris", target_os = "illumos")), | |
928 | feature = "reuseport" | |
929 | ))] | |
0531ce1d XL |
930 | pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> { |
931 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_REUSEPORT, reuse as c_int) } | |
932 | } | |
933 | ||
ba9703b0 XL |
934 | pub fn out_of_band_inline(&self) -> io::Result<bool> { |
935 | unsafe { | |
936 | let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_OOBINLINE)?; | |
937 | Ok(raw != 0) | |
938 | } | |
939 | } | |
940 | ||
941 | pub fn set_out_of_band_inline(&self, oob_inline: bool) -> io::Result<()> { | |
942 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_OOBINLINE, oob_inline as c_int) } | |
943 | } | |
944 | ||
0531ce1d XL |
945 | unsafe fn setsockopt<T>(&self, opt: c_int, val: c_int, payload: T) -> io::Result<()> |
946 | where | |
947 | T: Copy, | |
948 | { | |
949 | let payload = &payload as *const T as *const c_void; | |
950 | cvt(libc::setsockopt( | |
951 | self.fd, | |
952 | opt, | |
953 | val, | |
954 | payload, | |
955 | mem::size_of::<T>() as libc::socklen_t, | |
956 | ))?; | |
957 | Ok(()) | |
958 | } | |
959 | ||
960 | unsafe fn getsockopt<T: Copy>(&self, opt: c_int, val: c_int) -> io::Result<T> { | |
961 | let mut slot: T = mem::zeroed(); | |
962 | let mut len = mem::size_of::<T>() as libc::socklen_t; | |
963 | cvt(libc::getsockopt( | |
964 | self.fd, | |
965 | opt, | |
966 | val, | |
967 | &mut slot as *mut _ as *mut _, | |
968 | &mut len, | |
969 | ))?; | |
970 | assert_eq!(len as usize, mem::size_of::<T>()); | |
971 | Ok(slot) | |
972 | } | |
973 | } | |
974 | ||
975 | impl Read for Socket { | |
976 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
977 | <&Socket>::read(&mut &*self, buf) | |
978 | } | |
979 | } | |
980 | ||
981 | impl<'a> Read for &'a Socket { | |
982 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
983 | unsafe { | |
984 | let n = cvt({ | |
985 | libc::read( | |
986 | self.fd, | |
987 | buf.as_mut_ptr() as *mut c_void, | |
988 | cmp::min(buf.len(), max_len()), | |
989 | ) | |
990 | })?; | |
991 | Ok(n as usize) | |
992 | } | |
993 | } | |
994 | } | |
995 | ||
996 | impl Write for Socket { | |
997 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
998 | <&Socket>::write(&mut &*self, buf) | |
999 | } | |
1000 | ||
1001 | fn flush(&mut self) -> io::Result<()> { | |
1002 | <&Socket>::flush(&mut &*self) | |
1003 | } | |
1004 | } | |
1005 | ||
1006 | impl<'a> Write for &'a Socket { | |
1007 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
ba9703b0 | 1008 | self.send(buf, 0) |
0531ce1d XL |
1009 | } |
1010 | ||
1011 | fn flush(&mut self) -> io::Result<()> { | |
1012 | Ok(()) | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | impl fmt::Debug for Socket { | |
1017 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1018 | let mut f = f.debug_struct("Socket"); | |
1019 | f.field("fd", &self.fd); | |
1020 | if let Ok(addr) = self.local_addr() { | |
1021 | f.field("local_addr", &addr); | |
1022 | } | |
1023 | if let Ok(addr) = self.peer_addr() { | |
1024 | f.field("peer_addr", &addr); | |
1025 | } | |
1026 | f.finish() | |
1027 | } | |
1028 | } | |
1029 | ||
1030 | impl AsRawFd for Socket { | |
1031 | fn as_raw_fd(&self) -> c_int { | |
1032 | self.fd | |
1033 | } | |
1034 | } | |
1035 | ||
1036 | impl IntoRawFd for Socket { | |
1037 | fn into_raw_fd(self) -> c_int { | |
1038 | let fd = self.fd; | |
1039 | mem::forget(self); | |
1040 | return fd; | |
1041 | } | |
1042 | } | |
1043 | ||
1044 | impl FromRawFd for Socket { | |
1045 | unsafe fn from_raw_fd(fd: c_int) -> Socket { | |
1046 | Socket { fd: fd } | |
1047 | } | |
1048 | } | |
1049 | ||
dfeec247 | 1050 | impl AsRawFd for crate::Socket { |
0531ce1d XL |
1051 | fn as_raw_fd(&self) -> c_int { |
1052 | self.inner.as_raw_fd() | |
1053 | } | |
1054 | } | |
1055 | ||
dfeec247 | 1056 | impl IntoRawFd for crate::Socket { |
0531ce1d XL |
1057 | fn into_raw_fd(self) -> c_int { |
1058 | self.inner.into_raw_fd() | |
1059 | } | |
1060 | } | |
1061 | ||
dfeec247 XL |
1062 | impl FromRawFd for crate::Socket { |
1063 | unsafe fn from_raw_fd(fd: c_int) -> crate::Socket { | |
1064 | crate::Socket { | |
0531ce1d XL |
1065 | inner: Socket::from_raw_fd(fd), |
1066 | } | |
1067 | } | |
1068 | } | |
1069 | ||
1070 | impl Drop for Socket { | |
1071 | fn drop(&mut self) { | |
1072 | unsafe { | |
1073 | let _ = libc::close(self.fd); | |
1074 | } | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | impl From<Socket> for net::TcpStream { | |
1079 | fn from(socket: Socket) -> net::TcpStream { | |
1080 | unsafe { net::TcpStream::from_raw_fd(socket.into_raw_fd()) } | |
1081 | } | |
1082 | } | |
1083 | ||
1084 | impl From<Socket> for net::TcpListener { | |
1085 | fn from(socket: Socket) -> net::TcpListener { | |
1086 | unsafe { net::TcpListener::from_raw_fd(socket.into_raw_fd()) } | |
1087 | } | |
1088 | } | |
1089 | ||
1090 | impl From<Socket> for net::UdpSocket { | |
1091 | fn from(socket: Socket) -> net::UdpSocket { | |
1092 | unsafe { net::UdpSocket::from_raw_fd(socket.into_raw_fd()) } | |
1093 | } | |
1094 | } | |
1095 | ||
1096 | #[cfg(all(unix, feature = "unix"))] | |
1097 | impl From<Socket> for UnixStream { | |
1098 | fn from(socket: Socket) -> UnixStream { | |
1099 | unsafe { UnixStream::from_raw_fd(socket.into_raw_fd()) } | |
1100 | } | |
1101 | } | |
1102 | ||
1103 | #[cfg(all(unix, feature = "unix"))] | |
1104 | impl From<Socket> for UnixListener { | |
1105 | fn from(socket: Socket) -> UnixListener { | |
1106 | unsafe { UnixListener::from_raw_fd(socket.into_raw_fd()) } | |
1107 | } | |
1108 | } | |
1109 | ||
1110 | #[cfg(all(unix, feature = "unix"))] | |
1111 | impl From<Socket> for UnixDatagram { | |
1112 | fn from(socket: Socket) -> UnixDatagram { | |
1113 | unsafe { UnixDatagram::from_raw_fd(socket.into_raw_fd()) } | |
1114 | } | |
1115 | } | |
1116 | ||
1117 | impl From<net::TcpStream> for Socket { | |
1118 | fn from(socket: net::TcpStream) -> Socket { | |
1119 | unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } | |
1120 | } | |
1121 | } | |
1122 | ||
1123 | impl From<net::TcpListener> for Socket { | |
1124 | fn from(socket: net::TcpListener) -> Socket { | |
1125 | unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } | |
1126 | } | |
1127 | } | |
1128 | ||
1129 | impl From<net::UdpSocket> for Socket { | |
1130 | fn from(socket: net::UdpSocket) -> Socket { | |
1131 | unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } | |
1132 | } | |
1133 | } | |
1134 | ||
1135 | #[cfg(all(unix, feature = "unix"))] | |
1136 | impl From<UnixStream> for Socket { | |
1137 | fn from(socket: UnixStream) -> Socket { | |
1138 | unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } | |
1139 | } | |
1140 | } | |
1141 | ||
1142 | #[cfg(all(unix, feature = "unix"))] | |
1143 | impl From<UnixListener> for Socket { | |
1144 | fn from(socket: UnixListener) -> Socket { | |
1145 | unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } | |
1146 | } | |
1147 | } | |
1148 | ||
1149 | #[cfg(all(unix, feature = "unix"))] | |
1150 | impl From<UnixDatagram> for Socket { | |
1151 | fn from(socket: UnixDatagram) -> Socket { | |
1152 | unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } | |
1153 | } | |
1154 | } | |
1155 | ||
1156 | fn max_len() -> usize { | |
1157 | // The maximum read limit on most posix-like systems is `SSIZE_MAX`, | |
1158 | // with the man page quoting that if the count of bytes to read is | |
1159 | // greater than `SSIZE_MAX` the result is "unspecified". | |
1160 | // | |
1161 | // On macOS, however, apparently the 64-bit libc is either buggy or | |
1162 | // intentionally showing odd behavior by rejecting any read with a size | |
1163 | // larger than or equal to INT_MAX. To handle both of these the read | |
1164 | // size is capped on both platforms. | |
1165 | if cfg!(target_os = "macos") { | |
1166 | <c_int>::max_value() as usize - 1 | |
1167 | } else { | |
1168 | <ssize_t>::max_value() as usize | |
1169 | } | |
1170 | } | |
1171 | ||
1172 | fn cvt<T: One + PartialEq + Neg<Output = T>>(t: T) -> io::Result<T> { | |
1173 | let one: T = T::one(); | |
1174 | if t == -one { | |
1175 | Err(io::Error::last_os_error()) | |
1176 | } else { | |
1177 | Ok(t) | |
1178 | } | |
1179 | } | |
1180 | ||
1181 | fn cvt_r<F, T>(mut f: F) -> io::Result<T> | |
1182 | where | |
1183 | F: FnMut() -> T, | |
1184 | T: One + PartialEq + Neg<Output = T>, | |
1185 | { | |
1186 | loop { | |
1187 | match cvt(f()) { | |
1188 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} | |
1189 | other => return other, | |
1190 | } | |
1191 | } | |
1192 | } | |
1193 | ||
1194 | fn set_cloexec(fd: c_int) -> io::Result<()> { | |
1195 | unsafe { | |
1196 | let previous = cvt(libc::fcntl(fd, libc::F_GETFD))?; | |
1197 | let new = previous | libc::FD_CLOEXEC; | |
1198 | if new != previous { | |
1199 | cvt(libc::fcntl(fd, libc::F_SETFD, new))?; | |
1200 | } | |
1201 | Ok(()) | |
1202 | } | |
1203 | } | |
1204 | ||
1205 | fn dur2timeval(dur: Option<Duration>) -> io::Result<libc::timeval> { | |
1206 | match dur { | |
1207 | Some(dur) => { | |
1208 | if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { | |
1209 | return Err(io::Error::new( | |
1210 | io::ErrorKind::InvalidInput, | |
1211 | "cannot set a 0 duration timeout", | |
1212 | )); | |
1213 | } | |
1214 | ||
1215 | let secs = if dur.as_secs() > libc::time_t::max_value() as u64 { | |
1216 | libc::time_t::max_value() | |
1217 | } else { | |
1218 | dur.as_secs() as libc::time_t | |
1219 | }; | |
1220 | let mut timeout = libc::timeval { | |
1221 | tv_sec: secs, | |
1222 | tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, | |
1223 | }; | |
1224 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { | |
1225 | timeout.tv_usec = 1; | |
1226 | } | |
1227 | Ok(timeout) | |
1228 | } | |
1229 | None => Ok(libc::timeval { | |
1230 | tv_sec: 0, | |
1231 | tv_usec: 0, | |
1232 | }), | |
1233 | } | |
1234 | } | |
1235 | ||
1236 | fn timeval2dur(raw: libc::timeval) -> Option<Duration> { | |
1237 | if raw.tv_sec == 0 && raw.tv_usec == 0 { | |
1238 | None | |
1239 | } else { | |
1240 | let sec = raw.tv_sec as u64; | |
1241 | let nsec = (raw.tv_usec as u32) * 1000; | |
1242 | Some(Duration::new(sec, nsec)) | |
1243 | } | |
1244 | } | |
1245 | ||
1246 | fn to_s_addr(addr: &Ipv4Addr) -> libc::in_addr_t { | |
1247 | let octets = addr.octets(); | |
dfeec247 XL |
1248 | crate::hton( |
1249 | ((octets[0] as libc::in_addr_t) << 24) | |
1250 | | ((octets[1] as libc::in_addr_t) << 16) | |
1251 | | ((octets[2] as libc::in_addr_t) << 8) | |
1252 | | ((octets[3] as libc::in_addr_t) << 0), | |
0531ce1d XL |
1253 | ) |
1254 | } | |
1255 | ||
1256 | fn from_s_addr(in_addr: libc::in_addr_t) -> Ipv4Addr { | |
dfeec247 | 1257 | let h_addr = crate::ntoh(in_addr); |
0531ce1d XL |
1258 | |
1259 | let a: u8 = (h_addr >> 24) as u8; | |
1260 | let b: u8 = (h_addr >> 16) as u8; | |
1261 | let c: u8 = (h_addr >> 8) as u8; | |
1262 | let d: u8 = (h_addr >> 0) as u8; | |
1263 | ||
1264 | Ipv4Addr::new(a, b, c, d) | |
1265 | } | |
1266 | ||
1267 | fn to_in6_addr(addr: &Ipv6Addr) -> libc::in6_addr { | |
1268 | let mut ret: libc::in6_addr = unsafe { mem::zeroed() }; | |
1269 | ret.s6_addr = addr.octets(); | |
1270 | return ret; | |
1271 | } | |
1272 | ||
1273 | #[cfg(target_os = "android")] | |
1274 | fn to_ipv6mr_interface(value: u32) -> c_int { | |
1275 | value as c_int | |
1276 | } | |
1277 | ||
1278 | #[cfg(not(target_os = "android"))] | |
1279 | fn to_ipv6mr_interface(value: u32) -> libc::c_uint { | |
1280 | value as libc::c_uint | |
1281 | } | |
1282 | ||
1283 | fn linger2dur(linger_opt: libc::linger) -> Option<Duration> { | |
1284 | if linger_opt.l_onoff == 0 { | |
1285 | None | |
1286 | } else { | |
1287 | Some(Duration::from_secs(linger_opt.l_linger as u64)) | |
1288 | } | |
1289 | } | |
1290 | ||
1291 | fn dur2linger(dur: Option<Duration>) -> libc::linger { | |
1292 | match dur { | |
1293 | Some(d) => libc::linger { | |
1294 | l_onoff: 1, | |
1295 | l_linger: d.as_secs() as c_int, | |
1296 | }, | |
1297 | None => libc::linger { | |
1298 | l_onoff: 0, | |
1299 | l_linger: 0, | |
1300 | }, | |
1301 | } | |
1302 | } | |
1303 | ||
1304 | #[test] | |
1305 | fn test_ip() { | |
1306 | let ip = Ipv4Addr::new(127, 0, 0, 1); | |
1307 | assert_eq!(ip, from_s_addr(to_s_addr(&ip))); | |
1308 | } | |
ba9703b0 XL |
1309 | |
1310 | #[test] | |
1311 | fn test_out_of_band_inline() { | |
1312 | let tcp = Socket::new(libc::AF_INET, libc::SOCK_STREAM, 0).unwrap(); | |
1313 | assert_eq!(tcp.out_of_band_inline().unwrap(), false); | |
1314 | ||
1315 | tcp.set_out_of_band_inline(true).unwrap(); | |
1316 | assert_eq!(tcp.out_of_band_inline().unwrap(), true); | |
1317 | } |