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