| 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 | |
| 14 | pub(crate) fn new_for_addr(address: SocketAddr) -> io::Result<libc::c_int> { |
| 15 | let domain: i32 = match address { |
| 16 | SocketAddr::V4(_) => libc::AF_INET, |
| 17 | SocketAddr::V6(_) => libc::AF_INET6, |
| 18 | }; |
| 19 | new_socket(domain, socket_type:libc::SOCK_STREAM) |
| 20 | } |
| 21 | |
| 22 | pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()> { |
| 23 | let (raw_addr: SocketAddrCRepr, raw_addr_length: u32) = socket_addr(&addr); |
| 24 | syscall!(bind(socket.as_raw_fd(), raw_addr.as_ptr(), raw_addr_length))?; |
| 25 | Ok(()) |
| 26 | } |
| 27 | |
| 28 | pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<()> { |
| 29 | let (raw_addr: SocketAddrCRepr, raw_addr_length: u32) = 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: Error) if err.raw_os_error() != Some(libc::EINPROGRESS) => Err(err), |
| 37 | _ => Ok(()), |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> { |
| 42 | let backlog: i32 = backlog.try_into().unwrap_or(default:i32::MAX); |
| 43 | syscall!(listen(socket.as_raw_fd(), backlog))?; |
| 44 | Ok(()) |
| 45 | } |
| 46 | |
| 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 | |
| 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 | |