1 use std::convert::TryInto;
2 use std::io;
3 use std::mem::{size_of, MaybeUninit};
4 use std::net::{self, SocketAddr};
5 #[cfg(not(target_os = "hermit"))]
6 use std::os::fd::{AsRawFd, FromRawFd};
7 // TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
8 // can use `std::os::fd` and be merged with the above.
9 #[cfg(target_os = "hermit")]
10 use std::os::hermit::io::{AsRawFd, FromRawFd};
11 
12 use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr};
13 
new_for_addr(address: SocketAddr) -> io::Result<libc::c_int>14 pub(crate) fn new_for_addr(address: SocketAddr) -> io::Result<libc::c_int> {
15     let domain = match address {
16         SocketAddr::V4(_) => libc::AF_INET,
17         SocketAddr::V6(_) => libc::AF_INET6,
18     };
19     new_socket(domain, libc::SOCK_STREAM)
20 }
21 
bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()>22 pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()> {
23     let (raw_addr, raw_addr_length) = socket_addr(&addr);
24     syscall!(bind(socket.as_raw_fd(), raw_addr.as_ptr(), raw_addr_length))?;
25     Ok(())
26 }
27 
connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<()>28 pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<()> {
29     let (raw_addr, raw_addr_length) = socket_addr(&addr);
30 
31     match syscall!(connect(
32         socket.as_raw_fd(),
33         raw_addr.as_ptr(),
34         raw_addr_length
35     )) {
36         Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => Err(err),
37         _ => Ok(()),
38     }
39 }
40 
listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()>41 pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> {
42     let backlog = backlog.try_into().unwrap_or(i32::MAX);
43     syscall!(listen(socket.as_raw_fd(), backlog))?;
44     Ok(())
45 }
46 
set_reuseaddr(socket: &net::TcpListener, reuseaddr: bool) -> io::Result<()>47 pub(crate) fn set_reuseaddr(socket: &net::TcpListener, reuseaddr: bool) -> io::Result<()> {
48     let val: libc::c_int = i32::from(reuseaddr);
49     syscall!(setsockopt(
50         socket.as_raw_fd(),
51         libc::SOL_SOCKET,
52         libc::SO_REUSEADDR,
53         &val as *const libc::c_int as *const libc::c_void,
54         size_of::<libc::c_int>() as libc::socklen_t,
55     ))?;
56     Ok(())
57 }
58 
accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)>59 pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
60     let mut addr: MaybeUninit<libc::sockaddr_storage> = MaybeUninit::uninit();
61     let mut length = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
62 
63     // On platforms that support it we can use `accept4(2)` to set `NONBLOCK`
64     // and `CLOEXEC` in the call to accept the connection.
65     #[cfg(any(
66         // Android x86's seccomp profile forbids calls to `accept4(2)`
67         // See https://github.com/tokio-rs/mio/issues/1445 for details
68         all(not(target_arch="x86"), target_os = "android"),
69         target_os = "dragonfly",
70         target_os = "freebsd",
71         target_os = "fuchsia",
72         target_os = "hurd",
73         target_os = "illumos",
74         target_os = "linux",
75         target_os = "netbsd",
76         target_os = "openbsd",
77         target_os = "solaris",
78     ))]
79     let stream = {
80         syscall!(accept4(
81             listener.as_raw_fd(),
82             addr.as_mut_ptr() as *mut _,
83             &mut length,
84             libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK,
85         ))
86         .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) })
87     }?;
88 
89     // But not all platforms have the `accept4(2)` call. Luckily BSD (derived)
90     // OSs inherit the non-blocking flag from the listener, so we just have to
91     // set `CLOEXEC`.
92     #[cfg(any(
93         target_os = "aix",
94         target_os = "haiku",
95         target_os = "ios",
96         target_os = "macos",
97         target_os = "redox",
98         target_os = "tvos",
99         target_os = "visionos",
100         target_os = "watchos",
101         target_os = "espidf",
102         target_os = "vita",
103         target_os = "hermit",
104         target_os = "nto",
105         all(target_arch = "x86", target_os = "android"),
106     ))]
107     let stream = {
108         syscall!(accept(
109             listener.as_raw_fd(),
110             addr.as_mut_ptr() as *mut _,
111             &mut length
112         ))
113         .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) })
114         .and_then(|s| {
115             #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
116             syscall!(fcntl(s.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?;
117 
118             // See https://github.com/tokio-rs/mio/issues/1450
119             #[cfg(any(
120                 all(target_arch = "x86", target_os = "android"),
121                 target_os = "espidf",
122                 target_os = "vita",
123                 target_os = "hermit",
124                 target_os = "nto",
125             ))]
126             syscall!(fcntl(s.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK))?;
127 
128             Ok(s)
129         })
130     }?;
131 
132     // This is safe because `accept` calls above ensures the address
133     // initialised.
134     unsafe { to_socket_addr(addr.as_ptr()) }.map(|addr| (stream, addr))
135 }
136