1 use std::convert::TryInto;
2 use std::io;
3 use std::mem;
4 use std::mem::{size_of, MaybeUninit};
5 use std::net::{self, SocketAddr};
6 use std::os::unix::io::{AsRawFd, FromRawFd};
7 use std::time::Duration;
8 
9 use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr};
10 use crate::net::TcpKeepalive;
11 
12 #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))]
13 use libc::SO_KEEPALIVE as KEEPALIVE_TIME;
14 #[cfg(any(target_os = "macos", target_os = "ios"))]
15 use libc::TCP_KEEPALIVE as KEEPALIVE_TIME;
16 #[cfg(not(any(
17     target_os = "macos",
18     target_os = "ios",
19     target_os = "openbsd",
20     target_os = "netbsd",
21     target_os = "haiku"
22 )))]
23 use libc::TCP_KEEPIDLE as KEEPALIVE_TIME;
24 pub type TcpSocket = libc::c_int;
25 
new_v4_socket() -> io::Result<TcpSocket>26 pub(crate) fn new_v4_socket() -> io::Result<TcpSocket> {
27     new_socket(libc::AF_INET, libc::SOCK_STREAM)
28 }
29 
new_v6_socket() -> io::Result<TcpSocket>30 pub(crate) fn new_v6_socket() -> io::Result<TcpSocket> {
31     new_socket(libc::AF_INET6, libc::SOCK_STREAM)
32 }
33 
bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()>34 pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> {
35     let (raw_addr, raw_addr_length) = socket_addr(&addr);
36     syscall!(bind(socket, raw_addr.as_ptr(), raw_addr_length))?;
37     Ok(())
38 }
39 
connect(socket: TcpSocket, addr: SocketAddr) -> io::Result<net::TcpStream>40 pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result<net::TcpStream> {
41     let (raw_addr, raw_addr_length) = socket_addr(&addr);
42 
43     match syscall!(connect(socket, raw_addr.as_ptr(), raw_addr_length)) {
44         Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => {
45             Err(err)
46         }
47         _ => {
48             Ok(unsafe { net::TcpStream::from_raw_fd(socket) })
49         }
50     }
51 }
52 
listen(socket: TcpSocket, backlog: u32) -> io::Result<net::TcpListener>53 pub(crate) fn listen(socket: TcpSocket, backlog: u32) -> io::Result<net::TcpListener> {
54     let backlog = backlog.try_into().unwrap_or(i32::max_value());
55     syscall!(listen(socket, backlog))?;
56     Ok(unsafe { net::TcpListener::from_raw_fd(socket) })
57 }
58 
close(socket: TcpSocket)59 pub(crate) fn close(socket: TcpSocket) {
60     let _ = unsafe { net::TcpStream::from_raw_fd(socket) };
61 }
62 
set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()>63 pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()> {
64     let val: libc::c_int = if reuseaddr { 1 } else { 0 };
65     syscall!(setsockopt(
66         socket,
67         libc::SOL_SOCKET,
68         libc::SO_REUSEADDR,
69         &val as *const libc::c_int as *const libc::c_void,
70         size_of::<libc::c_int>() as libc::socklen_t,
71     ))
72     .map(|_| ())
73 }
74 
get_reuseaddr(socket: TcpSocket) -> io::Result<bool>75 pub(crate) fn get_reuseaddr(socket: TcpSocket) -> io::Result<bool> {
76     let mut optval: libc::c_int = 0;
77     let mut optlen = mem::size_of::<libc::c_int>() as libc::socklen_t;
78 
79     syscall!(getsockopt(
80         socket,
81         libc::SOL_SOCKET,
82         libc::SO_REUSEADDR,
83         &mut optval as *mut _ as *mut _,
84         &mut optlen,
85     ))?;
86 
87     Ok(optval != 0)
88 }
89 
90 #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
set_reuseport(socket: TcpSocket, reuseport: bool) -> io::Result<()>91 pub(crate) fn set_reuseport(socket: TcpSocket, reuseport: bool) -> io::Result<()> {
92     let val: libc::c_int = if reuseport { 1 } else { 0 };
93 
94     syscall!(setsockopt(
95         socket,
96         libc::SOL_SOCKET,
97         libc::SO_REUSEPORT,
98         &val as *const libc::c_int as *const libc::c_void,
99         size_of::<libc::c_int>() as libc::socklen_t,
100     ))
101     .map(|_| ())
102 }
103 
104 #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
get_reuseport(socket: TcpSocket) -> io::Result<bool>105 pub(crate) fn get_reuseport(socket: TcpSocket) -> io::Result<bool> {
106     let mut optval: libc::c_int = 0;
107     let mut optlen = mem::size_of::<libc::c_int>() as libc::socklen_t;
108 
109     syscall!(getsockopt(
110         socket,
111         libc::SOL_SOCKET,
112         libc::SO_REUSEPORT,
113         &mut optval as *mut _ as *mut _,
114         &mut optlen,
115     ))?;
116 
117     Ok(optval != 0)
118 }
119 
get_localaddr(socket: TcpSocket) -> io::Result<SocketAddr>120 pub(crate) fn get_localaddr(socket: TcpSocket) -> io::Result<SocketAddr> {
121     let mut addr: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
122     let mut length = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
123 
124     syscall!(getsockname(
125         socket,
126         &mut addr as *mut _ as *mut _,
127         &mut length
128     ))?;
129 
130     unsafe { to_socket_addr(&addr) }
131 }
132 
set_linger(socket: TcpSocket, dur: Option<Duration>) -> io::Result<()>133 pub(crate) fn set_linger(socket: TcpSocket, dur: Option<Duration>) -> io::Result<()> {
134     let val: libc::linger = libc::linger {
135         l_onoff: if dur.is_some() { 1 } else { 0 },
136         l_linger: dur
137             .map(|dur| dur.as_secs() as libc::c_int)
138             .unwrap_or_default(),
139     };
140     syscall!(setsockopt(
141         socket,
142         libc::SOL_SOCKET,
143         #[cfg(target_vendor = "apple")]
144         libc::SO_LINGER_SEC,
145         #[cfg(not(target_vendor = "apple"))]
146         libc::SO_LINGER,
147         &val as *const libc::linger as *const libc::c_void,
148         size_of::<libc::linger>() as libc::socklen_t,
149     ))
150     .map(|_| ())
151 }
152 
get_linger(socket: TcpSocket) -> io::Result<Option<Duration>>153 pub(crate) fn get_linger(socket: TcpSocket) -> io::Result<Option<Duration>> {
154     let mut val: libc::linger =  unsafe { std::mem::zeroed() };
155     let mut len = mem::size_of::<libc::linger>() as libc::socklen_t;
156 
157     syscall!(getsockopt(
158         socket,
159         libc::SOL_SOCKET,
160         #[cfg(target_vendor = "apple")]
161         libc::SO_LINGER_SEC,
162         #[cfg(not(target_vendor = "apple"))]
163         libc::SO_LINGER,
164         &mut val as *mut _ as *mut _,
165         &mut len,
166     ))?;
167 
168     if val.l_onoff == 0 {
169         Ok(None)
170     } else {
171         Ok(Some(Duration::from_secs(val.l_linger as u64)))
172     }
173 }
174 
set_recv_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()>175 pub(crate) fn set_recv_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> {
176     let size = size.try_into().ok().unwrap_or_else(i32::max_value);
177     syscall!(setsockopt(
178         socket,
179         libc::SOL_SOCKET,
180         libc::SO_RCVBUF,
181         &size as *const _ as *const libc::c_void,
182         size_of::<libc::c_int>() as libc::socklen_t
183     ))
184     .map(|_| ())
185 }
186 
get_recv_buffer_size(socket: TcpSocket) -> io::Result<u32>187 pub(crate) fn get_recv_buffer_size(socket: TcpSocket) -> io::Result<u32> {
188     let mut optval: libc::c_int = 0;
189     let mut optlen = size_of::<libc::c_int>() as libc::socklen_t;
190     syscall!(getsockopt(
191         socket,
192         libc::SOL_SOCKET,
193         libc::SO_RCVBUF,
194         &mut optval as *mut _ as *mut _,
195         &mut optlen,
196     ))?;
197 
198     Ok(optval as u32)
199 }
200 
set_send_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()>201 pub(crate) fn set_send_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> {
202     let size = size.try_into().ok().unwrap_or_else(i32::max_value);
203     syscall!(setsockopt(
204         socket,
205         libc::SOL_SOCKET,
206         libc::SO_SNDBUF,
207         &size as *const _ as *const libc::c_void,
208         size_of::<libc::c_int>() as libc::socklen_t
209     ))
210     .map(|_| ())
211 }
212 
get_send_buffer_size(socket: TcpSocket) -> io::Result<u32>213 pub(crate) fn get_send_buffer_size(socket: TcpSocket) -> io::Result<u32> {
214     let mut optval: libc::c_int = 0;
215     let mut optlen = size_of::<libc::c_int>() as libc::socklen_t;
216 
217     syscall!(getsockopt(
218         socket,
219         libc::SOL_SOCKET,
220         libc::SO_SNDBUF,
221         &mut optval as *mut _ as *mut _,
222         &mut optlen,
223     ))?;
224 
225     Ok(optval as u32)
226 }
227 
set_keepalive(socket: TcpSocket, keepalive: bool) -> io::Result<()>228 pub(crate) fn set_keepalive(socket: TcpSocket, keepalive: bool) -> io::Result<()> {
229     let val: libc::c_int = if keepalive { 1 } else { 0 };
230     syscall!(setsockopt(
231         socket,
232         libc::SOL_SOCKET,
233         libc::SO_KEEPALIVE,
234         &val as *const _ as *const libc::c_void,
235         size_of::<libc::c_int>() as libc::socklen_t
236     ))
237     .map(|_| ())
238 }
239 
get_keepalive(socket: TcpSocket) -> io::Result<bool>240 pub(crate) fn get_keepalive(socket: TcpSocket) -> io::Result<bool> {
241     let mut optval: libc::c_int = 0;
242     let mut optlen = mem::size_of::<libc::c_int>() as libc::socklen_t;
243 
244     syscall!(getsockopt(
245         socket,
246         libc::SOL_SOCKET,
247         libc::SO_KEEPALIVE,
248         &mut optval as *mut _ as *mut _,
249         &mut optlen,
250     ))?;
251 
252     Ok(optval != 0)
253 }
254 
set_keepalive_params(socket: TcpSocket, keepalive: TcpKeepalive) -> io::Result<()>255 pub(crate) fn set_keepalive_params(socket: TcpSocket, keepalive: TcpKeepalive) -> io::Result<()> {
256     if let Some(dur) = keepalive.time {
257         set_keepalive_time(socket, dur)?;
258     }
259 
260     #[cfg(any(
261         target_os = "linux",
262         target_os = "macos",
263         target_os = "ios",
264         target_os = "freebsd",
265         target_os = "netbsd",
266     ))]
267     {
268         if let Some(dur) = keepalive.interval {
269             set_keepalive_interval(socket, dur)?;
270         }
271 
272         if let Some(retries) = keepalive.retries {
273             set_keepalive_retries(socket, retries)?;
274         }
275     }
276 
277 
278     Ok(())
279 }
280 
set_keepalive_time(socket: TcpSocket, time: Duration) -> io::Result<()>281 fn set_keepalive_time(socket: TcpSocket, time: Duration) -> io::Result<()> {
282     let time_secs = time
283         .as_secs()
284         .try_into()
285         .ok()
286         .unwrap_or_else(i32::max_value);
287     syscall!(setsockopt(
288         socket,
289         libc::IPPROTO_TCP,
290         KEEPALIVE_TIME,
291         &(time_secs as libc::c_int) as *const _ as *const libc::c_void,
292         size_of::<libc::c_int>() as libc::socklen_t
293     ))
294     .map(|_| ())
295 }
296 
get_keepalive_time(socket: TcpSocket) -> io::Result<Option<Duration>>297 pub(crate) fn get_keepalive_time(socket: TcpSocket) -> io::Result<Option<Duration>> {
298     if !get_keepalive(socket)? {
299         return Ok(None);
300     }
301 
302     let mut optval: libc::c_int = 0;
303     let mut optlen = mem::size_of::<libc::c_int>() as libc::socklen_t;
304     syscall!(getsockopt(
305         socket,
306         libc::IPPROTO_TCP,
307         KEEPALIVE_TIME,
308         &mut optval as *mut _ as *mut _,
309         &mut optlen,
310     ))?;
311 
312     Ok(Some(Duration::from_secs(optval as u64)))
313 }
314 
315 /// Linux, FreeBSD, and NetBSD support setting the keepalive interval via
316 /// `TCP_KEEPINTVL`.
317 /// See:
318 /// - https://man7.org/linux/man-pages/man7/tcp.7.html
319 /// - https://www.freebsd.org/cgi/man.cgi?query=tcp#end
320 /// - http://man.netbsd.org/tcp.4#DESCRIPTION
321 ///
322 /// OpenBSD does not:
323 /// https://man.openbsd.org/tcp
324 #[cfg(any(
325     target_os = "linux",
326     target_os = "macos",
327     target_os = "ios",
328     target_os = "freebsd",
329     target_os = "netbsd",
330 ))]
set_keepalive_interval(socket: TcpSocket, interval: Duration) -> io::Result<()>331 fn set_keepalive_interval(socket: TcpSocket, interval: Duration) -> io::Result<()> {
332     let interval_secs = interval
333         .as_secs()
334         .try_into()
335         .ok()
336         .unwrap_or_else(i32::max_value);
337     syscall!(setsockopt(
338         socket,
339         libc::IPPROTO_TCP,
340         libc::TCP_KEEPINTVL,
341         &(interval_secs as libc::c_int) as *const _ as *const libc::c_void,
342         size_of::<libc::c_int>() as libc::socklen_t
343     ))
344     .map(|_| ())
345 }
346 
347 #[cfg(any(
348     target_os = "linux",
349     target_os = "macos",
350     target_os = "ios",
351     target_os = "freebsd",
352     target_os = "netbsd",
353 ))]
get_keepalive_interval(socket: TcpSocket) -> io::Result<Option<Duration>>354 pub(crate) fn get_keepalive_interval(socket: TcpSocket) -> io::Result<Option<Duration>> {
355     if !get_keepalive(socket)? {
356         return Ok(None);
357     }
358 
359     let mut optval: libc::c_int = 0;
360     let mut optlen = mem::size_of::<libc::c_int>() as libc::socklen_t;
361     syscall!(getsockopt(
362         socket,
363         libc::IPPROTO_TCP,
364         libc::TCP_KEEPINTVL,
365         &mut optval as *mut _ as *mut _,
366         &mut optlen,
367     ))?;
368 
369     Ok(Some(Duration::from_secs(optval as u64)))
370 }
371 
372 /// Linux, macOS/iOS, FreeBSD, and NetBSD support setting the number of TCP
373 /// keepalive retries via `TCP_KEEPCNT`.
374 /// See:
375 /// - https://man7.org/linux/man-pages/man7/tcp.7.html
376 /// - https://www.freebsd.org/cgi/man.cgi?query=tcp#end
377 /// - http://man.netbsd.org/tcp.4#DESCRIPTION
378 ///
379 /// OpenBSD does not:
380 /// https://man.openbsd.org/tcp
381 #[cfg(any(
382     target_os = "linux",
383     target_os = "macos",
384     target_os = "ios",
385     target_os = "freebsd",
386     target_os = "netbsd",
387 ))]
set_keepalive_retries(socket: TcpSocket, retries: u32) -> io::Result<()>388 fn set_keepalive_retries(socket: TcpSocket, retries: u32) -> io::Result<()> {
389     let retries = retries.try_into().ok().unwrap_or_else(i32::max_value);
390     syscall!(setsockopt(
391         socket,
392         libc::IPPROTO_TCP,
393         libc::TCP_KEEPCNT,
394         &(retries as libc::c_int) as *const _ as *const libc::c_void,
395         size_of::<libc::c_int>() as libc::socklen_t
396     ))
397     .map(|_| ())
398 }
399 
400 #[cfg(any(
401     target_os = "linux",
402     target_os = "macos",
403     target_os = "ios",
404     target_os = "freebsd",
405     target_os = "netbsd",
406 ))]
get_keepalive_retries(socket: TcpSocket) -> io::Result<Option<u32>>407 pub(crate) fn get_keepalive_retries(socket: TcpSocket) -> io::Result<Option<u32>> {
408     if !get_keepalive(socket)? {
409         return Ok(None);
410     }
411 
412     let mut optval: libc::c_int = 0;
413     let mut optlen = mem::size_of::<libc::c_int>() as libc::socklen_t;
414     syscall!(getsockopt(
415         socket,
416         libc::IPPROTO_TCP,
417         libc::TCP_KEEPCNT,
418         &mut optval as *mut _ as *mut _,
419         &mut optlen,
420     ))?;
421 
422     Ok(Some(optval as u32))
423 }
424 
accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)>425 pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
426     let mut addr: MaybeUninit<libc::sockaddr_storage> = MaybeUninit::uninit();
427     let mut length = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
428 
429     // On platforms that support it we can use `accept4(2)` to set `NONBLOCK`
430     // and `CLOEXEC` in the call to accept the connection.
431     #[cfg(any(
432         // Android x86's seccomp profile forbids calls to `accept4(2)`
433         // See https://github.com/tokio-rs/mio/issues/1445 for details
434         all(
435             not(target_arch="x86"),
436             target_os = "android"
437         ),
438         target_os = "dragonfly",
439         target_os = "freebsd",
440         target_os = "illumos",
441         target_os = "linux",
442         target_os = "netbsd",
443         target_os = "openbsd"
444     ))]
445     let stream = {
446         syscall!(accept4(
447             listener.as_raw_fd(),
448             addr.as_mut_ptr() as *mut _,
449             &mut length,
450             libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK,
451         ))
452         .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) })
453     }?;
454 
455     // But not all platforms have the `accept4(2)` call. Luckily BSD (derived)
456     // OSes inherit the non-blocking flag from the listener, so we just have to
457     // set `CLOEXEC`.
458     #[cfg(any(
459         all(
460             target_arch = "x86",
461             target_os = "android"
462         ),
463         target_os = "ios",
464         target_os = "macos",
465         target_os = "solaris"
466     ))]
467     let stream = {
468         syscall!(accept(
469             listener.as_raw_fd(),
470             addr.as_mut_ptr() as *mut _,
471             &mut length
472         ))
473         .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) })
474         .and_then(|s| {
475             syscall!(fcntl(s.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?;
476 
477             // See https://github.com/tokio-rs/mio/issues/1450
478             #[cfg(all(target_arch = "x86",target_os = "android"))]
479             syscall!(fcntl(s.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK))?;
480 
481             Ok(s)
482         })
483     }?;
484 
485     // This is safe because `accept` calls above ensures the address
486     // initialised.
487     unsafe { to_socket_addr(addr.as_ptr()) }.map(|addr| (stream, addr))
488 }
489