diff --git a/src/lib.rs b/src/lib.rs index 725e3125..8f7bee5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,7 @@ impl Domain { pub const IPV6: Domain = Domain(sys::AF_INET6); /// Returns the correct domain for `address`. - pub fn for_address(address: SocketAddr) -> Domain { + pub const fn for_address(address: SocketAddr) -> Domain { match address { SocketAddr::V4(_) => Domain::IPV4, SocketAddr::V6(_) => Domain::IPV6, @@ -252,9 +252,9 @@ impl RecvFlags { /// This flag is only used for datagram-based sockets, /// not for stream sockets. /// - /// On Unix this corresponds to the MSG_TRUNC flag. - /// On Windows this corresponds to the WSAEMSGSIZE error code. - pub fn is_truncated(self) -> bool { + /// On Unix this corresponds to the `MSG_TRUNC` flag. + /// On Windows this corresponds to the `WSAEMSGSIZE` error code. + pub const fn is_truncated(self) -> bool { self.0 & sys::MSG_TRUNC != 0 } } diff --git a/src/sockaddr.rs b/src/sockaddr.rs index f43bb32a..16e57d30 100644 --- a/src/sockaddr.rs +++ b/src/sockaddr.rs @@ -39,6 +39,11 @@ impl SockAddr { } } + /// Constructs a `SockAddr` from its raw components. + pub(crate) const fn from_raw(storage: sockaddr_storage, len: socklen_t) -> SockAddr { + SockAddr { storage, len } + } + /// Returns this address's family. pub fn family(&self) -> sa_family_t { self.storage.ss_family @@ -54,6 +59,12 @@ impl SockAddr { &self.storage as *const _ as *const _ } + /// Returns a raw pointer to the address storage. + #[cfg(unix)] + pub(crate) fn as_storage_ptr(&self) -> *const sockaddr_storage { + &self.storage + } + /// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IP v4) /// or `AF_INET6` (IP v6) family, otherwise returns `None`. pub fn as_std(&self) -> Option { diff --git a/src/socket.rs b/src/socket.rs index 5c47b3d8..2ab0a30a 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -11,15 +11,12 @@ use std::io::{self, Read, Write}; #[cfg(not(target_os = "redox"))] use std::io::{IoSlice, IoSliceMut}; use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown}; -#[cfg(all(feature = "all", unix))] -use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; +#[cfg(unix)] +use std::os::unix::io::{FromRawFd, IntoRawFd}; +#[cfg(windows)] +use std::os::windows::io::{FromRawSocket, IntoRawSocket}; use std::time::Duration; -#[cfg(all(unix, feature = "all", not(target_os = "redox")))] -use libc::MSG_OOB; -#[cfg(all(windows, feature = "all"))] -use winapi::um::winsock2::MSG_OOB; - use crate::sys; #[cfg(not(target_os = "redox"))] use crate::RecvFlags; @@ -50,41 +47,6 @@ use crate::{Domain, Protocol, SockAddr, Type}; /// can lead to a data race when two threads are changing options in parallel. /// /// # Examples -/// -/// Creating a new socket setting all advisable flags. -/// -#[cfg_attr(feature = "all", doc = "```")] // Protocol::cloexec requires the `all` feature. -#[cfg_attr(not(feature = "all"), doc = "```ignore")] -/// # fn main() -> std::io::Result<()> { -/// use socket2::{Protocol, Domain, Type, Socket}; -/// -/// let domain = Domain::IPV4; -/// let ty = Type::STREAM; -/// let protocol = Protocol::TCP; -/// -/// // On platforms that support it set `SOCK_CLOEXEC`. -/// #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] -/// let ty = ty.cloexec(); -/// -/// // On windows set `WSA_FLAG_NO_HANDLE_INHERIT`. -/// #[cfg(windows)] -/// let ty = ty.no_inherit(); -/// -/// let socket = Socket::new(domain, ty, Some(protocol))?; -/// -/// // On platforms that don't support `SOCK_CLOEXEC`, use `FD_CLOEXEC`. -/// #[cfg(all(not(windows), not(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd"))))] -/// socket.set_cloexec(true)?; -/// -/// // On macOS and iOS set `NOSIGPIPE`. -/// #[cfg(target_vendor = "apple")] -/// socket.set_nosigpipe(true)?; -/// -/// # drop(socket); -/// # Ok(()) -/// # } -/// ``` -/// /// ```no_run /// # fn main() -> std::io::Result<()> { /// use std::net::{SocketAddr, TcpListener}; @@ -110,29 +72,26 @@ pub struct Socket { } impl Socket { - /// Creates a new socket ready to be configured. + /// Creates a new socket and sets common flags. /// /// This function corresponds to `socket(2)` on Unix and `WSASocketW` on - /// Windows and simply creates a new socket, no other configuration is done - /// and further functions must be invoked to configure this socket. - /// - /// # Notes - /// - /// The standard library sets the `CLOEXEC` flag on Unix on sockets, this - /// function does **not** do this, but its advisable. On supported platforms - /// [`Type::cloexec`] can be used for this, or by using - /// [`Socket::set_cloexec`]. - /// - /// Furthermore on macOS and iOS `NOSIGPIPE` is not set, this can be done - /// using [`Socket::set_nosigpipe`]. + /// Windows. /// - /// Similarly on Windows the `HANDLE_FLAG_INHERIT` is **not** set to zero, - /// but again in most cases its advisable to do so. This can be doing using - /// [`Socket::set_no_inherit`]. + /// On Unix-like systems, the close-on-exec flag is set on the new socket. + /// Additionally, on Apple platforms `SOCK_NOSIGPIPE` is set. On Windows, + /// the socket is made non-inheritable. /// - /// See the `Socket` documentation for a full example of setting all the - /// above mentioned flags. + /// [`Socket::new_raw`] can be used if you don't want these flags to be set. pub fn new(domain: Domain, ty: Type, protocol: Option) -> io::Result { + let ty = set_common_type(ty); + Socket::new_raw(domain, ty, protocol).and_then(set_common_flags) + } + + /// Creates a new socket ready to be configured. + /// + /// This function corresponds to `socket(2)` on Unix and `WSASocketW` on + /// Windows and simply creates a new socket, no other configuration is done. + pub fn new_raw(domain: Domain, ty: Type, protocol: Option) -> io::Result { let protocol = protocol.map(|p| p.0).unwrap_or(0); sys::socket(domain.0, ty.0, protocol).map(|inner| Socket { inner }) } @@ -141,10 +100,10 @@ impl Socket { /// /// This function corresponds to `socketpair(2)`. /// - /// # Notes + /// This function sets the same flags as in done for [`Socket::new`], + /// [`Socket::pair_raw`] can be used if you don't want to set those flags. /// - /// Much like [`Socket::new`] this doesn't set any flags, which might be - /// advisable. + /// # Notes /// /// This function is only available on Unix. #[cfg(all(feature = "all", unix))] @@ -152,6 +111,26 @@ impl Socket { domain: Domain, ty: Type, protocol: Option, + ) -> io::Result<(Socket, Socket)> { + let ty = set_common_type(ty); + let (a, b) = Socket::pair_raw(domain, ty, protocol)?; + let a = set_common_flags(a)?; + let b = set_common_flags(b)?; + Ok((a, b)) + } + + /// Creates a pair of sockets which are connected to each other. + /// + /// This function corresponds to `socketpair(2)`. + /// + /// # Notes + /// + /// This function is only available on Unix. + #[cfg(all(feature = "all", unix))] + pub fn pair_raw( + domain: Domain, + ty: Type, + protocol: Option, ) -> io::Result<(Socket, Socket)> { let protocol = protocol.map(|p| p.0).unwrap_or(0); sys::socketpair(domain.0, ty.0, protocol) @@ -191,19 +170,43 @@ impl Socket { /// Accept a new incoming connection from this listener. /// - /// This function directly corresponds to the `accept(2)` function on - /// Windows and Unix. + /// This function uses `accept4(2)` on platforms that support it and + /// `accept(2)` platforms that do not. /// - /// This function will block the calling thread until a new connection is - /// established. When established, the corresponding `Socket` and the - /// remote peer's address will be returned. - /// - /// # Notes - /// - /// Like [`Socket::new`] this will not set any flags. If that is desirable, - /// e.g. setting `CLOEXEC`, [`Socket::accept4`] can be used on supported - /// OSes or [`Socket::set_cloexec`] can be called. + /// This function sets the same flags as in done for [`Socket::new`], + /// [`Socket::accept_raw`] can be used if you don't want to set those flags. pub fn accept(&self) -> io::Result<(Socket, SockAddr)> { + // Use `accept4` on platforms that support it. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ))] + return self._accept4(libc::SOCK_CLOEXEC); + + // Fall back to `accept` on platforms that do not support `accept4`. + #[cfg(not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + )))] + { + let (socket, addr) = self.accept_raw()?; + set_common_flags(socket).map(|socket| (socket, addr)) + } + } + + /// Accept a new incoming connection from this listener. + /// + /// This function directly corresponds to the `accept(2)` function on + /// Windows and Unix. + pub fn accept_raw(&self) -> io::Result<(Socket, SockAddr)> { sys::accept(self.inner).map(|(inner, addr)| (Socket { inner }, addr)) } @@ -279,47 +282,62 @@ impl Socket { /// Receives data on the socket from the remote address to which it is /// connected. /// - /// The [`connect`] method will connect this socket to a remote address. This - /// method will fail if the socket is not connected. + /// The [`connect`] method will connect this socket to a remote address. + /// This method might fail if the socket is not connected. /// - /// [`connect`]: #method.connect + /// [`connect`]: Socket::connect pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.inner().recv(buf, 0) + self.recv_with_flags(buf, 0) + } + + /// Receives out-of-band (OOB) data on the socket from the remote address to + /// which it is connected by setting the `MSG_OOB` flag for this call. + /// + /// For more information, see [`recv`], [`out_of_band_inline`]. + /// + /// [`recv`]: Socket::recv + /// [`out_of_band_inline`]: Socket::out_of_band_inline + #[cfg(all(feature = "all", not(target_os = "redox")))] + pub fn recv_out_of_band(&self, buf: &mut [u8]) -> io::Result { + self.recv_with_flags(buf, sys::MSG_OOB) } - /// Identical to [`recv`] but allows for specification of arbitrary flags to the underlying - /// `recv` call. + /// Identical to [`recv`] but allows for specification of arbitrary flags to + /// the underlying `recv` call. /// - /// [`recv`]: #method.recv - pub fn recv_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result { - self.inner().recv(buf, flags) + /// [`recv`]: Socket::recv + pub fn recv_with_flags(&self, buf: &mut [u8], flags: sys::c_int) -> io::Result { + sys::recv(self.inner, buf, flags) } - /// Identical to [`recv_with_flags`] but reads into a slice of buffers. + /// Receives data on the socket from the remote address to which it is + /// connected. Unlike [`recv`] this allows passing multiple buffers. + /// + /// The [`connect`] method will connect this socket to a remote address. + /// This method might fail if the socket is not connected. + /// + /// In addition to the number of bytes read, this function returns the flags + /// for the received message. See [`RecvFlags`] for more information about + /// the returned flags. /// - /// In addition to the number of bytes read, this function returns the flags for the received message. - /// See [`RecvFlags`] for more information about the flags. + /// [`recv`]: Socket::recv + /// [`connect`]: Socket::connect + #[cfg(not(target_os = "redox"))] + pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<(usize, RecvFlags)> { + self.recv_vectored_with_flags(bufs, 0) + } + + /// Identical to [`recv_vectored`] but allows for specification of arbitrary + /// flags to the underlying `recvmsg`/`WSARecv` call. /// - /// [`recv_with_flags`]: #method.recv_with_flags + /// [`recv_vectored`]: Socket::recv_vectored #[cfg(not(target_os = "redox"))] - pub fn recv_vectored( + pub fn recv_vectored_with_flags( &self, bufs: &mut [IoSliceMut<'_>], flags: i32, ) -> io::Result<(usize, RecvFlags)> { - self.inner().recv_vectored(bufs, flags) - } - - /// Receives out-of-band (OOB) data on the socket from the remote address to - /// which it is connected by setting the `MSG_OOB` flag for this call. - /// - /// For more information, see [`recv`], [`out_of_band_inline`]. - /// - /// [`recv`]: #method.recv - /// [`out_of_band_inline`]: #method.out_of_band_inline - #[cfg(all(feature = "all", not(target_os = "redox")))] - pub fn recv_out_of_band(&self, buf: &mut [u8]) -> io::Result { - self.inner().recv(buf, MSG_OOB) + sys::recv_vectored(self.inner, bufs, flags) } /// Receives data on the socket from the remote adress to which it is @@ -329,40 +347,51 @@ impl Socket { /// Successive calls return the same data. This is accomplished by passing /// `MSG_PEEK` as a flag to the underlying `recv` system call. pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.inner().peek(buf) + self.recv_with_flags(buf, sys::MSG_PEEK) } /// Receives data from the socket. On success, returns the number of bytes /// read and the address from whence the data came. pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SockAddr)> { - self.inner().recv_from(buf, 0) + self.recv_from_with_flags(buf, 0) } - /// Identical to [`recv_from`] but allows for specification of arbitrary flags to the underlying - /// `recvfrom` call. + /// Identical to [`recv_from`] but allows for specification of arbitrary + /// flags to the underlying `recvfrom` call. /// - /// [`recv_from`]: #method.recv_from + /// [`recv_from`]: Socket::recv_from pub fn recv_from_with_flags( &self, buf: &mut [u8], flags: i32, ) -> io::Result<(usize, SockAddr)> { - self.inner().recv_from(buf, flags) + sys::recv_from(self.inner, buf, flags) } - /// Identical to [`recv_from_with_flags`] but reads into a slice of buffers. - /// - /// In addition to the number of bytes read, this function returns the flags for the received message. - /// See [`RecvFlags`] for more information about the flags. + /// Receives data from the socket. Returns the amount of bytes read, the + /// [`RecvFlags`] and the remote address from the data is coming. Unlike + /// [`recv_from`] this allows passing multiple buffers. /// - /// [`recv_from_with_flags`]: #method.recv_from_with_flags + /// [`recv_from`]: Socket::recv_from #[cfg(not(target_os = "redox"))] pub fn recv_from_vectored( &self, bufs: &mut [IoSliceMut<'_>], + ) -> io::Result<(usize, RecvFlags, SockAddr)> { + self.recv_from_vectored_with_flags(bufs, 0) + } + + /// Identical to [`recv_from_vectored`] but allows for specification of + /// arbitrary flags to the underlying `recvmsg`/`WSARecvFrom` call. + /// + /// [`recv_from_vectored`]: Socket::recv_from_vectored + #[cfg(not(target_os = "redox"))] + pub fn recv_from_vectored_with_flags( + &self, + bufs: &mut [IoSliceMut<'_>], flags: i32, ) -> io::Result<(usize, RecvFlags, SockAddr)> { - self.inner().recv_from_vectored(bufs, flags) + sys::recv_from_vectored(self.inner, bufs, flags) } /// Receives data from the socket, without removing it from the queue. @@ -373,7 +402,7 @@ impl Socket { /// On success, returns the number of bytes peeked and the address from /// whence the data came. pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SockAddr)> { - self.inner().peek_from(buf) + self.recv_from_with_flags(buf, sys::MSG_PEEK) } /// Sends data on the socket to a connected peer. @@ -383,7 +412,7 @@ impl Socket { /// /// On success returns the number of bytes that were sent. pub fn send(&self, buf: &[u8]) -> io::Result { - self.inner().send(buf, 0) + self.send_with_flags(buf, 0) } /// Identical to [`send`] but allows for specification of arbitrary flags to the underlying @@ -391,15 +420,22 @@ impl Socket { /// /// [`send`]: #method.send pub fn send_with_flags(&self, buf: &[u8], flags: i32) -> io::Result { - self.inner().send(buf, flags) + sys::send(self.inner, buf, flags) } - /// Identical to [`send_with_flags`] but writes from a slice of buffers. + /// Send data to the connected peer. Returns the amount of bytes written. + #[cfg(not(target_os = "redox"))] + pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.send_vectored_with_flags(bufs, 0) + } + + /// Identical to [`send_vectored`] but allows for specification of arbitrary + /// flags to the underlying `sendmsg`/`WSASend` call. /// - /// [`send_with_flags`]: #method.send_with_flags + /// [`send_vectored`]: Socket::send_vectored #[cfg(not(target_os = "redox"))] - pub fn send_vectored(&self, bufs: &[IoSlice<'_>], flags: i32) -> io::Result { - self.inner().send_vectored(bufs, flags) + pub fn send_vectored_with_flags(&self, bufs: &[IoSlice<'_>], flags: i32) -> io::Result { + sys::send_vectored(self.inner, bufs, flags) } /// Sends out-of-band (OOB) data on the socket to connected peer @@ -411,41 +447,46 @@ impl Socket { /// [`out_of_band_inline`]: #method.out_of_band_inline #[cfg(all(feature = "all", not(target_os = "redox")))] pub fn send_out_of_band(&self, buf: &[u8]) -> io::Result { - self.inner().send(buf, MSG_OOB) + self.send_with_flags(buf, sys::MSG_OOB) } /// Sends data on the socket to the given address. On success, returns the /// number of bytes written. /// - /// This is typically used on UDP or datagram-oriented sockets. On success - /// returns the number of bytes that were sent. + /// This is typically used on UDP or datagram-oriented sockets. pub fn send_to(&self, buf: &[u8], addr: &SockAddr) -> io::Result { - self.inner().send_to(buf, 0, addr) + self.send_to_with_flags(buf, addr, 0) } - /// Identical to [`send_to`] but allows for specification of arbitrary flags to the underlying - /// `sendto` call. + /// Identical to [`send_to`] but allows for specification of arbitrary flags + /// to the underlying `sendto` call. /// - /// [`send_to`]: #method.send_to + /// [`send_to`]: Socket::send_to pub fn send_to_with_flags(&self, buf: &[u8], addr: &SockAddr, flags: i32) -> io::Result { - self.inner().send_to(buf, flags, addr) + sys::send_to(self.inner, buf, addr, flags) + } + + /// Send data to a peer listening on `addr`. Returns the amount of bytes + /// written. + #[cfg(not(target_os = "redox"))] + pub fn send_to_vectored(&self, bufs: &[IoSlice<'_>], addr: &SockAddr) -> io::Result { + self.send_to_vectored_with_flags(bufs, addr, 0) } - /// Identical to [`send_with_flags`] but writes from a slice of buffers. + /// Identical to [`send_to_vectored`] but allows for specification of + /// arbitrary flags to the underlying `sendmsg`/`WSASendTo` call. /// - /// [`send_with_flags`]: #method.send_with_flags + /// [`send_to_vectored`]: Socket::send_to_vectored #[cfg(not(target_os = "redox"))] - pub fn send_to_vectored( + pub fn send_to_vectored_with_flags( &self, bufs: &[IoSlice<'_>], addr: &SockAddr, flags: i32, ) -> io::Result { - self.inner().send_to_vectored(bufs, flags, addr) + sys::send_to_vectored(self.inner, bufs, addr, flags) } - // ================================================ - /// Gets the value of the `IP_TTL` option for this socket. /// /// For more information about this option, see [`set_ttl`][link]. @@ -876,131 +917,161 @@ impl Socket { } } +/// Set `SOCK_CLOEXEC` and `NO_HANDLE_INHERIT` on the `ty`pe on platforms that +/// support it. +#[inline(always)] +fn set_common_type(ty: Type) -> Type { + // On platforms that support it set `SOCK_CLOEXEC`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ))] + let ty = ty._cloexec(); + + // On windows set `NO_HANDLE_INHERIT`. + #[cfg(windows)] + let ty = ty._no_inherit(); + + ty +} + +/// Set `FD_CLOEXEC` and `NOSIGPIPE` on the `socket` for platforms that need it. +#[inline(always)] +fn set_common_flags(socket: Socket) -> io::Result { + // On platforms that don't have `SOCK_CLOEXEC` use `FD_CLOEXEC`. + #[cfg(all( + unix, + not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + )) + ))] + socket._set_cloexec(true)?; + + // On Apple platforms set `NOSIGPIPE`. + #[cfg(target_vendor = "apple")] + socket._set_nosigpipe(true)?; + + Ok(socket) +} + impl Read for Socket { fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner().read(buf) + self.recv(buf) + } + + #[cfg(not(target_os = "redox"))] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.recv_vectored(bufs).map(|(n, _)| n) } } impl<'a> Read for &'a Socket { fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner().read(buf) + self.recv(buf) + } + + #[cfg(not(target_os = "redox"))] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.recv_vectored(bufs).map(|(n, _)| n) } } impl Write for Socket { fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner().write(buf) + self.send(buf) + } + + #[cfg(not(target_os = "redox"))] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.send_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { - self.inner().flush() + Ok(()) } } impl<'a> Write for &'a Socket { fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner().write(buf) + self.send(buf) + } + + #[cfg(not(target_os = "redox"))] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.send_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { - self.inner().flush() + Ok(()) } } impl fmt::Debug for Socket { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner().fmt(f) + f.debug_struct("Socket") + .field("raw", &self.inner) + .field("local_addr", &self.local_addr().ok()) + .field("peer_addr", &self.peer_addr().ok()) + .finish() } } +/// Macro to convert from one network type to another. +macro_rules! from_raw { + ($ty: ty, $socket: expr) => {{ + #[cfg(unix)] + unsafe { + <$ty>::from_raw_fd($socket.into_raw_fd()) + } + #[cfg(windows)] + unsafe { + <$ty>::from_raw_socket($socket.into_raw_socket()) + } + }}; +} + impl From for Socket { fn from(socket: net::TcpStream) -> Socket { - Socket { - inner: sys::Socket::from(socket).inner(), - } + from_raw!(Socket, socket) } } impl From for Socket { fn from(socket: net::TcpListener) -> Socket { - Socket { - inner: sys::Socket::from(socket).inner(), - } + from_raw!(Socket, socket) } } impl From for Socket { fn from(socket: net::UdpSocket) -> Socket { - Socket { - inner: sys::Socket::from(socket).inner(), - } - } -} - -#[cfg(all(feature = "all", unix))] -impl From for Socket { - fn from(socket: UnixStream) -> Socket { - Socket { - inner: sys::Socket::from(socket).inner(), - } - } -} - -#[cfg(all(feature = "all", unix))] -impl From for Socket { - fn from(socket: UnixListener) -> Socket { - Socket { - inner: sys::Socket::from(socket).inner(), - } - } -} - -#[cfg(all(feature = "all", unix))] -impl From for Socket { - fn from(socket: UnixDatagram) -> Socket { - Socket { - inner: sys::Socket::from(socket).inner(), - } + from_raw!(Socket, socket) } } impl From for net::TcpStream { fn from(socket: Socket) -> net::TcpStream { - sys::Socket::from_inner(socket.inner).into() + from_raw!(net::TcpStream, socket) } } impl From for net::TcpListener { fn from(socket: Socket) -> net::TcpListener { - sys::Socket::from_inner(socket.inner).into() + from_raw!(net::TcpListener, socket) } } impl From for net::UdpSocket { fn from(socket: Socket) -> net::UdpSocket { - sys::Socket::from_inner(socket.inner).into() - } -} - -#[cfg(all(feature = "all", unix))] -impl From for UnixStream { - fn from(socket: Socket) -> UnixStream { - sys::Socket::from_inner(socket.inner).into() - } -} - -#[cfg(all(feature = "all", unix))] -impl From for UnixListener { - fn from(socket: Socket) -> UnixListener { - sys::Socket::from_inner(socket.inner).into() - } -} - -#[cfg(all(feature = "all", unix))] -impl From for UnixDatagram { - fn from(socket: Socket) -> UnixDatagram { - sys::Socket::from_inner(socket.inner).into() + from_raw!(net::UdpSocket, socket) } } diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 2b02309c..b67cd779 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -6,24 +6,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cmp::min; #[cfg(not(target_os = "redox"))] use std::io::{IoSlice, IoSliceMut}; -use std::io::{Read, Write}; use std::mem::{self, size_of, size_of_val, MaybeUninit}; use std::net::Shutdown; -use std::net::{self, Ipv4Addr, Ipv6Addr}; +use std::net::{Ipv4Addr, Ipv6Addr}; #[cfg(feature = "all")] use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; +#[cfg(feature = "all")] use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; #[cfg(feature = "all")] use std::path::Path; -#[cfg(feature = "all")] -use std::ptr; use std::time::Duration; -use std::{cmp, fmt, io}; +use std::{fmt, io, ptr}; -use libc::{self, c_void, in6_addr, in_addr, ssize_t}; +#[cfg(not(target_vendor = "apple"))] +use libc::ssize_t; +use libc::{c_void, in6_addr, in_addr}; #[cfg(not(target_os = "redox"))] use crate::RecvFlags; @@ -43,6 +44,13 @@ pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP}; pub(crate) use libc::{ sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, }; +// Used in `RecvFlags`. +#[cfg(not(target_os = "redox"))] +pub(crate) use libc::MSG_TRUNC; +// Used in `Socket`. +#[cfg(all(unix, feature = "all", not(target_os = "redox")))] +pub(crate) use libc::MSG_OOB; +pub(crate) use libc::MSG_PEEK; cfg_if::cfg_if! { if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", @@ -83,9 +91,20 @@ macro_rules! syscall { }}; } -// Re-export message flags for the RecvFlags struct. -#[cfg(not(target_os = "redox"))] -pub(crate) use libc::MSG_TRUNC; +/// Maximum size of a buffer passed to system call like `recv` and `send`. +#[cfg(not(target_vendor = "apple"))] +const MAX_BUF_LEN: usize = ::max_value() as usize; + +// The maximum read limit on most posix-like systems is `SSIZE_MAX`, with the +// man page quoting that if the count of bytes to read is greater than +// `SSIZE_MAX` the result is "unspecified". +// +// On macOS, however, apparently the 64-bit libc is either buggy or +// intentionally showing odd behavior by rejecting any read with a size larger +// than or equal to INT_MAX. To handle both of these the read size is capped on +// both platforms. +#[cfg(target_vendor = "apple")] +const MAX_BUF_LEN: usize = ::max_value() as usize - 1; #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] type IovLen = usize; @@ -94,7 +113,7 @@ type IovLen = usize; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", - all(target_os = "linux", target_env = "musl",), + all(target_os = "linux", target_env = "musl"), target_os = "macos", target_os = "netbsd", target_os = "openbsd", @@ -167,6 +186,18 @@ impl Type { ) ))] pub const fn cloexec(self) -> Type { + self._cloexec() + } + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + ))] + pub(crate) const fn _cloexec(self) -> Type { Type(self.0 | libc::SOCK_CLOEXEC) } } @@ -221,7 +252,7 @@ impl RecvFlags { /// a record is terminated by sending a message with the end-of-record flag set. /// /// On Unix this corresponds to the MSG_EOR flag. - pub fn is_end_of_record(self) -> bool { + pub const fn is_end_of_record(self) -> bool { self.0 & libc::MSG_EOR != 0 } @@ -231,7 +262,7 @@ impl RecvFlags { /// mixed in with the normal data stream. /// /// On Unix this corresponds to the MSG_OOB flag. - pub fn is_out_of_band(self) -> bool { + pub const fn is_out_of_band(self) -> bool { self.0 & libc::MSG_OOB != 0 } } @@ -374,6 +405,162 @@ pub(crate) fn shutdown(fd: SysSocket, how: Shutdown) -> io::Result<()> { syscall!(shutdown(fd, how)).map(|_| ()) } +pub(crate) fn recv(fd: SysSocket, buf: &mut [u8], flags: c_int) -> io::Result { + syscall!(recv( + fd, + buf.as_mut_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + )) + .map(|n| n as usize) +} + +pub(crate) fn recv_from( + fd: SysSocket, + buf: &mut [u8], + flags: c_int, +) -> io::Result<(usize, SockAddr)> { + let mut storage: MaybeUninit = MaybeUninit::zeroed(); + let mut addrlen = size_of_val(&storage) as socklen_t; + syscall!(recvfrom( + fd, + buf.as_mut_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + storage.as_mut_ptr().cast(), + &mut addrlen, + )) + .map(|n| { + // Safety: `recvfrom` wrote an address of `addrlen` bytes for us. The + // remaining bytes are initialised to zero (which is valid for + // `sockaddr_storage`). + let addr = SockAddr::from_raw(unsafe { storage.assume_init() }, addrlen); + (n as usize, addr) + }) +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn recv_vectored( + fd: SysSocket, + bufs: &mut [IoSliceMut<'_>], + flags: c_int, +) -> io::Result<(usize, RecvFlags)> { + recvmsg(fd, ptr::null_mut(), bufs, flags).map(|(n, _, recv_flags)| (n, recv_flags)) +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn recv_from_vectored( + fd: SysSocket, + bufs: &mut [IoSliceMut<'_>], + flags: c_int, +) -> io::Result<(usize, RecvFlags, SockAddr)> { + let mut storage: MaybeUninit = MaybeUninit::zeroed(); + recvmsg(fd, storage.as_mut_ptr(), bufs, flags).map(|(n, addrlen, recv_flags)| { + // Safety: `recvmsg` wrote an address of `addrlen` bytes for us. The + // remaining bytes are initialised to zero (which is valid for + // `sockaddr_storage`). + let addr = SockAddr::from_raw(unsafe { storage.assume_init() }, addrlen); + (n as usize, recv_flags, addr) + }) +} + +/// Returns the (bytes received, sending address len, `RecvFlags`). +#[cfg(not(target_os = "redox"))] +fn recvmsg( + fd: SysSocket, + msg_name: *mut sockaddr_storage, + bufs: &mut [IoSliceMut<'_>], + flags: c_int, +) -> io::Result<(usize, libc::socklen_t, RecvFlags)> { + let msg_namelen = if msg_name.is_null() { + 0 + } else { + size_of::() as libc::socklen_t + }; + let mut msg = libc::msghdr { + msg_name: msg_name.cast(), + msg_namelen, + msg_iov: bufs.as_mut_ptr().cast(), + msg_iovlen: min(bufs.len(), IovLen::MAX as usize) as IovLen, + msg_control: ptr::null_mut(), + msg_controllen: 0, + msg_flags: 0, + }; + syscall!(recvmsg(fd, &mut msg, flags)) + .map(|n| (n as usize, msg.msg_namelen, RecvFlags(msg.msg_flags))) +} + +pub(crate) fn send(fd: SysSocket, buf: &[u8], flags: c_int) -> io::Result { + syscall!(send( + fd, + buf.as_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + )) + .map(|n| n as usize) +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn send_vectored( + fd: SysSocket, + bufs: &[IoSlice<'_>], + flags: c_int, +) -> io::Result { + sendmsg(fd, ptr::null(), 0, bufs, flags) +} + +pub(crate) fn send_to( + fd: SysSocket, + buf: &[u8], + addr: &SockAddr, + flags: c_int, +) -> io::Result { + syscall!(sendto( + fd, + buf.as_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + addr.as_ptr(), + addr.len(), + )) + .map(|n| n as usize) +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn send_to_vectored( + fd: SysSocket, + bufs: &[IoSlice<'_>], + addr: &SockAddr, + flags: c_int, +) -> io::Result { + sendmsg(fd, addr.as_storage_ptr(), addr.len(), bufs, flags) +} + +/// Returns the (bytes received, sending address len, `RecvFlags`). +#[cfg(not(target_os = "redox"))] +fn sendmsg( + fd: SysSocket, + msg_name: *const sockaddr_storage, + msg_namelen: socklen_t, + bufs: &[IoSlice<'_>], + flags: c_int, +) -> io::Result { + let mut msg = libc::msghdr { + // Safety: we're creating a `*mut` pointer from a reference, which is UB + // once actually used. However the OS should not write to it in the + // `sendmsg` system call. + msg_name: (msg_name as *mut sockaddr_storage).cast(), + msg_namelen, + // Safety: Same as above about `*const` -> `*mut`. + msg_iov: bufs.as_ptr() as *mut _, + msg_iovlen: min(bufs.len(), IovLen::MAX as usize) as IovLen, + msg_control: ptr::null_mut(), + msg_controllen: 0, + msg_flags: 0, + }; + syscall!(sendmsg(fd, &mut msg, flags)).map(|n| n as usize) +} + /// Unix only API. impl crate::Socket { /// Accept a new incoming connection from this listener. @@ -396,24 +583,32 @@ impl crate::Socket { ) ))] pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> { + self._accept4(flags) + } + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + ))] + pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> { + // Safety: zeroed `sockaddr_storage` is valid. let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; let mut len = mem::size_of_val(&storage) as socklen_t; - - let res = syscall!(accept4( + syscall!(accept4( self.inner, &mut storage as *mut _ as *mut _, &mut len, - flags, - )); - match res { - Ok(inner) => { - let socket = crate::Socket { inner }; - let addr = - unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, len) }; - Ok((socket, addr)) - } - Err(e) => Err(e), - } + flags + )) + .map(|inner| { + let addr = unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, len) }; + (crate::Socket { inner }, addr) + }) } /// Sets `CLOEXEC` on the socket. @@ -421,7 +616,12 @@ impl crate::Socket { /// # Notes /// /// On supported platforms you can use [`Protocol::cloexec`]. + #[cfg(feature = "all")] pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> { + self._set_cloexec(close_on_exec) + } + + pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> { if close_on_exec { fcntl_add(self.inner, libc::F_GETFD, libc::F_SETFD, libc::FD_CLOEXEC) } else { @@ -436,12 +636,17 @@ impl crate::Socket { /// Only supported on Apple platforms (`target_vendor = "apple"`). #[cfg(all(feature = "all", target_vendor = "apple"))] pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> { + self._set_nosigpipe(nosigpipe) + } + + #[cfg(target_vendor = "apple")] + pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> { unsafe { - setsockopt::( + setsockopt( self.inner, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, - nosigpipe as _, + nosigpipe as c_int, ) } } @@ -490,7 +695,7 @@ unsafe fn getsockopt(fd: SysSocket, opt: c_int, val: c_int) -> io::Result } /// Caller must ensure `T` is the correct type for `opt` and `val`. -#[cfg(all(feature = "all", target_vendor = "apple"))] +#[cfg(target_vendor = "apple")] unsafe fn setsockopt(fd: SysSocket, opt: c_int, val: c_int, payload: T) -> io::Result<()> { let payload = &payload as *const T as *const c_void; syscall!(setsockopt( @@ -503,163 +708,12 @@ unsafe fn setsockopt(fd: SysSocket, opt: c_int, val: c_int, payload: T) -> io .map(|_| ()) } -/* - setsockopt::( - self.inner, - libc::SOL_SOCKET, - libc::SO_NOSIGPIPE, - nosigpipe as _, - ) -*/ - #[repr(transparent)] // Required during rewriting. pub struct Socket { fd: SysSocket, } impl Socket { - pub fn recv(&self, buf: &mut [u8], flags: c_int) -> io::Result { - let n = syscall!(recv( - self.fd, - buf.as_mut_ptr() as *mut c_void, - cmp::min(buf.len(), max_len()), - flags, - ))?; - Ok(n as usize) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let n = syscall!(recv( - self.fd, - buf.as_mut_ptr() as *mut c_void, - cmp::min(buf.len(), max_len()), - libc::MSG_PEEK, - ))?; - Ok(n as usize) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SockAddr)> { - self.recv_from(buf, libc::MSG_PEEK) - } - - pub fn recv_from(&self, buf: &mut [u8], flags: c_int) -> io::Result<(usize, SockAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as socklen_t; - - let n = syscall!(recvfrom( - self.fd, - buf.as_mut_ptr() as *mut c_void, - cmp::min(buf.len(), max_len()), - flags, - &mut storage as *mut _ as *mut _, - &mut addrlen, - ))?; - let addr = unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, addrlen) }; - Ok((n as usize, addr)) - } - - #[cfg(not(target_os = "redox"))] - pub fn recv_vectored( - &self, - bufs: &mut [IoSliceMut<'_>], - flags: c_int, - ) -> io::Result<(usize, RecvFlags)> { - let mut msg = libc::msghdr { - msg_name: std::ptr::null_mut(), - msg_namelen: 0, - msg_iov: bufs.as_mut_ptr().cast(), - msg_iovlen: bufs.len().min(IovLen::MAX as usize) as IovLen, - msg_control: std::ptr::null_mut(), - msg_controllen: 0, - msg_flags: 0, - }; - - let n = syscall!(recvmsg(self.fd, &mut msg as *mut _, flags))?; - Ok((n as usize, RecvFlags(msg.msg_flags))) - } - - #[cfg(not(target_os = "redox"))] - pub fn recv_from_vectored( - &self, - bufs: &mut [IoSliceMut<'_>], - flags: c_int, - ) -> io::Result<(usize, RecvFlags, SockAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut msg = libc::msghdr { - msg_name: &mut storage as *mut libc::sockaddr_storage as *mut c_void, - msg_namelen: mem::size_of_val(&storage) as socklen_t, - msg_iov: bufs.as_mut_ptr().cast(), - msg_iovlen: bufs.len().min(IovLen::MAX as usize) as IovLen, - msg_control: std::ptr::null_mut(), - msg_controllen: 0, - msg_flags: 0, - }; - - let n = syscall!(recvmsg(self.fd, &mut msg as *mut _, flags))?; - let addr = - unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, msg.msg_namelen) }; - Ok((n as usize, RecvFlags(msg.msg_flags), addr)) - } - - pub fn send(&self, buf: &[u8], flags: c_int) -> io::Result { - let n = syscall!(send( - self.fd, - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), max_len()), - flags, - ))?; - Ok(n as usize) - } - - pub fn send_to(&self, buf: &[u8], flags: c_int, addr: &SockAddr) -> io::Result { - let n = syscall!(sendto( - self.fd, - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), max_len()), - flags, - addr.as_ptr(), - addr.len(), - ))?; - Ok(n as usize) - } - - #[cfg(not(target_os = "redox"))] - pub fn send_vectored(&self, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result { - let mut msg = libc::msghdr { - msg_name: std::ptr::null_mut(), - msg_namelen: 0, - msg_iov: bufs.as_ptr() as *mut libc::iovec, - msg_iovlen: bufs.len().min(IovLen::MAX as usize) as IovLen, - msg_control: std::ptr::null_mut(), - msg_controllen: 0, - msg_flags: 0, - }; - - let n = syscall!(sendmsg(self.fd, &mut msg as *mut libc::msghdr, flags))?; - Ok(n as usize) - } - - #[cfg(not(target_os = "redox"))] - pub fn send_to_vectored( - &self, - bufs: &[IoSlice<'_>], - flags: c_int, - addr: &SockAddr, - ) -> io::Result { - let mut msg = libc::msghdr { - msg_name: addr.as_ptr() as *mut c_void, - msg_namelen: addr.len(), - msg_iov: bufs.as_ptr() as *mut libc::iovec, - msg_iovlen: bufs.len().min(IovLen::MAX as usize) as IovLen, - msg_control: std::ptr::null_mut(), - msg_controllen: 0, - msg_flags: 0, - }; - - let n = syscall!(sendmsg(self.fd, &mut msg as *mut libc::msghdr, flags))?; - Ok(n as usize) - } - pub fn ttl(&self) -> io::Result { unsafe { let raw: c_int = self.getsockopt(libc::IPPROTO_IP, libc::IP_TTL)?; @@ -954,7 +1008,10 @@ impl Socket { } } - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(all( + feature = "all", + not(any(target_os = "solaris", target_os = "illumos")) + ))] pub fn reuse_port(&self) -> io::Result { unsafe { let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_REUSEPORT)?; @@ -962,7 +1019,10 @@ impl Socket { } } - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(all( + feature = "all", + not(any(target_os = "solaris", target_os = "illumos")) + ))] pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> { unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_REUSEPORT, reuse as c_int) } } @@ -1012,47 +1072,6 @@ impl Socket { pub fn inner(self) -> SysSocket { self.fd } - - pub fn from_inner(fd: SysSocket) -> Socket { - Socket { fd } - } -} - -impl Read for Socket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - <&Socket>::read(&mut &*self, buf) - } -} - -impl<'a> Read for &'a Socket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = syscall!(read( - self.fd, - buf.as_mut_ptr() as *mut c_void, - cmp::min(buf.len(), max_len()), - ))?; - Ok(n as usize) - } -} - -impl Write for Socket { - fn write(&mut self, buf: &[u8]) -> io::Result { - <&Socket>::write(&mut &*self, buf) - } - - fn flush(&mut self) -> io::Result<()> { - <&Socket>::flush(&mut &*self) - } -} - -impl<'a> Write for &'a Socket { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.send(buf, 0) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } } impl fmt::Debug for Socket { @@ -1111,75 +1130,51 @@ impl FromRawFd for crate::Socket { } } -impl From for net::TcpStream { - fn from(socket: Socket) -> net::TcpStream { - unsafe { net::TcpStream::from_raw_fd(socket.into_raw_fd()) } - } -} - -impl From for net::TcpListener { - fn from(socket: Socket) -> net::TcpListener { - unsafe { net::TcpListener::from_raw_fd(socket.into_raw_fd()) } - } -} - -impl From for net::UdpSocket { - fn from(socket: Socket) -> net::UdpSocket { - unsafe { net::UdpSocket::from_raw_fd(socket.into_raw_fd()) } - } -} - -impl From for UnixStream { - fn from(socket: Socket) -> UnixStream { +#[cfg(feature = "all")] +impl From for UnixStream { + fn from(socket: crate::Socket) -> UnixStream { unsafe { UnixStream::from_raw_fd(socket.into_raw_fd()) } } } -impl From for UnixListener { - fn from(socket: Socket) -> UnixListener { +#[cfg(feature = "all")] +impl From for UnixListener { + fn from(socket: crate::Socket) -> UnixListener { unsafe { UnixListener::from_raw_fd(socket.into_raw_fd()) } } } -impl From for UnixDatagram { - fn from(socket: Socket) -> UnixDatagram { +#[cfg(feature = "all")] +impl From for UnixDatagram { + fn from(socket: crate::Socket) -> UnixDatagram { unsafe { UnixDatagram::from_raw_fd(socket.into_raw_fd()) } } } -impl From for Socket { - fn from(socket: net::TcpStream) -> Socket { - unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } - } -} - -impl From for Socket { - fn from(socket: net::TcpListener) -> Socket { - unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } - } -} - -impl From for Socket { - fn from(socket: net::UdpSocket) -> Socket { - unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } - } -} - -impl From for Socket { - fn from(socket: UnixStream) -> Socket { - unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } +#[cfg(feature = "all")] +impl From for crate::Socket { + fn from(socket: UnixStream) -> crate::Socket { + crate::Socket { + inner: socket.into_raw_fd(), + } } } -impl From for Socket { - fn from(socket: UnixListener) -> Socket { - unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } +#[cfg(feature = "all")] +impl From for crate::Socket { + fn from(socket: UnixListener) -> crate::Socket { + crate::Socket { + inner: socket.into_raw_fd(), + } } } -impl From for Socket { - fn from(socket: UnixDatagram) -> Socket { - unsafe { Socket::from_raw_fd(socket.into_raw_fd()) } +#[cfg(feature = "all")] +impl From for crate::Socket { + fn from(socket: UnixDatagram) -> crate::Socket { + crate::Socket { + inner: socket.into_raw_fd(), + } } } @@ -1189,22 +1184,6 @@ pub(crate) fn close(fd: SysSocket) { } } -fn max_len() -> usize { - // The maximum read limit on most posix-like systems is `SSIZE_MAX`, - // with the man page quoting that if the count of bytes to read is - // greater than `SSIZE_MAX` the result is "unspecified". - // - // On macOS, however, apparently the 64-bit libc is either buggy or - // intentionally showing odd behavior by rejecting any read with a size - // larger than or equal to INT_MAX. To handle both of these the read - // size is capped on both platforms. - if cfg!(target_os = "macos") { - ::max_value() as usize - 1 - } else { - ::max_value() as usize - } -} - fn dur2timeval(dur: Option) -> io::Result { match dur { Some(dur) => { diff --git a/src/sys/windows.rs b/src/sys/windows.rs index fa109be1..7cd0de0e 100644 --- a/src/sys/windows.rs +++ b/src/sys/windows.rs @@ -6,17 +6,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cmp; -use std::fmt; -use std::io; -use std::io::{IoSlice, IoSliceMut, Read, Write}; +use std::cmp::min; +use std::io::{self, IoSlice, IoSliceMut}; use std::mem::{self, size_of, size_of_val, MaybeUninit}; -use std::net::Shutdown; -use std::net::{self, Ipv4Addr, Ipv6Addr}; +use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown}; use std::os::windows::prelude::*; -use std::ptr; use std::sync::Once; use std::time::Duration; +use std::{fmt, ptr}; use winapi::ctypes::{c_char, c_long, c_ulong}; use winapi::shared::in6addr::*; @@ -33,15 +30,14 @@ use winapi::um::winsock2::{self as sock, u_long, SD_BOTH, SD_RECEIVE, SD_SEND}; use crate::{RecvFlags, SockAddr, Type}; -const MSG_PEEK: c_int = 0x2; const SIO_KEEPALIVE_VALS: DWORD = 0x98000004; pub use winapi::ctypes::c_int; /// Fake MSG_TRUNC flag for the [`RecvFlags`] struct. /// -/// The flag is enabled when a `WSARecv[From]` call returns `WSAEMSGSIZE`. -/// The value of the flag is defined by us. +/// The flag is enabled when a `WSARecv[From]` call returns `WSAEMSGSIZE`. The +/// value of the flag is defined by us. pub(crate) const MSG_TRUNC: c_int = 0x01; // Used in `Domain`. @@ -62,6 +58,13 @@ pub(crate) use winapi::shared::ws2def::{ }; pub(crate) use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6; pub(crate) use winapi::um::ws2tcpip::socklen_t; +// Used in `Socket`. +#[cfg(all(windows, feature = "all"))] +pub(crate) use winapi::um::winsock2::MSG_OOB; +pub(crate) use winapi::um::winsock2::MSG_PEEK; + +/// Maximum size of a buffer passed to system call like `recv` and `send`. +const MAX_BUF_LEN: usize = ::max_value() as usize; /// Helper macro to execute a system call that returns an `io::Result`. macro_rules! syscall { @@ -93,6 +96,10 @@ impl Type { /// Set `WSA_FLAG_NO_HANDLE_INHERIT` on the socket. #[cfg(feature = "all")] pub const fn no_inherit(self) -> Type { + self._no_inherit() + } + + pub(crate) const fn _no_inherit(self) -> Type { Type(self.0 | Type::NO_INHERIT) } } @@ -269,6 +276,234 @@ pub(crate) fn shutdown(socket: SysSocket, how: Shutdown) -> io::Result<()> { syscall!(shutdown(socket, how), PartialEq::eq, sock::SOCKET_ERROR).map(|_| ()) } +pub(crate) fn recv(socket: SysSocket, buf: &mut [u8], flags: c_int) -> io::Result { + let res = syscall!( + recv( + socket, + buf.as_mut_ptr().cast(), + min(buf.len(), MAX_BUF_LEN) as c_int, + flags, + ), + PartialEq::eq, + sock::SOCKET_ERROR + ); + match res { + Ok(n) => Ok(n as usize), + Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => Ok(0), + Err(err) => Err(err), + } +} + +pub(crate) fn recv_vectored( + socket: SysSocket, + bufs: &mut [IoSliceMut<'_>], + flags: c_int, +) -> io::Result<(usize, RecvFlags)> { + let mut nread = 0; + let mut flags = flags as DWORD; + let res = syscall!( + WSARecv( + socket, + bufs.as_mut_ptr().cast(), + min(bufs.len(), DWORD::max_value() as usize) as DWORD, + &mut nread, + &mut flags, + ptr::null_mut(), + None, + ), + PartialEq::eq, + sock::SOCKET_ERROR + ); + match res { + Ok(_) => Ok((nread as usize, RecvFlags(0))), + Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => { + Ok((0, RecvFlags(0))) + } + Err(ref err) if err.raw_os_error() == Some(sock::WSAEMSGSIZE as i32) => { + Ok((nread as usize, RecvFlags(MSG_TRUNC))) + } + Err(err) => Err(err), + } +} + +pub(crate) fn recv_from( + socket: SysSocket, + buf: &mut [u8], + flags: c_int, +) -> io::Result<(usize, SockAddr)> { + let mut storage: MaybeUninit = MaybeUninit::zeroed(); + let mut addrlen = size_of_val(&storage) as socklen_t; + let res = syscall!( + recvfrom( + socket, + buf.as_mut_ptr().cast(), + min(buf.len(), MAX_BUF_LEN) as c_int, + flags, + storage.as_mut_ptr().cast(), + &mut addrlen, + ), + PartialEq::eq, + sock::SOCKET_ERROR + ); + match res { + Ok(n) => { + // Safety: `recvfrom` wrote an address of `addrlen` bytes for us. The + // remaining bytes are initialised to zero (which is valid for + // `sockaddr_storage`). + let addr = SockAddr::from_raw(unsafe { storage.assume_init() }, addrlen); + Ok((n as usize, addr)) + } + Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => { + // Safety: see above. + let addr = SockAddr::from_raw(unsafe { storage.assume_init() }, addrlen); + Ok((0, addr)) + } + Err(err) => Err(err), + } +} + +pub(crate) fn recv_from_vectored( + socket: SysSocket, + bufs: &mut [IoSliceMut<'_>], + flags: c_int, +) -> io::Result<(usize, RecvFlags, SockAddr)> { + let mut storage: MaybeUninit = MaybeUninit::zeroed(); + let mut addrlen = size_of_val(&storage) as socklen_t; + let mut nread = 0; + let mut flags = flags as DWORD; + let res = syscall!( + WSARecvFrom( + socket, + bufs.as_mut_ptr().cast(), + min(bufs.len(), DWORD::max_value() as usize) as DWORD, + &mut nread, + &mut flags, + storage.as_mut_ptr().cast(), + &mut addrlen, + ptr::null_mut(), + None, + ), + PartialEq::eq, + sock::SOCKET_ERROR + ); + match res { + Ok(_) => { + // Safety: `WSARecvFrom` wrote an address of `addrlen` bytes for us. + // The remaining bytes are initialised to zero (which is valid for + // `sockaddr_storage`). + let addr = SockAddr::from_raw(unsafe { storage.assume_init() }, addrlen); + Ok((nread as usize, RecvFlags(0), addr)) + } + Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => { + // Safety: see above. + let addr = SockAddr::from_raw(unsafe { storage.assume_init() }, addrlen); + Ok((nread as usize, RecvFlags(0), addr)) + } + Err(ref err) if err.raw_os_error() == Some(sock::WSAEMSGSIZE as i32) => { + // Safety: see above. + let addr = SockAddr::from_raw(unsafe { storage.assume_init() }, addrlen); + Ok((nread as usize, RecvFlags(MSG_TRUNC), addr)) + } + Err(err) => Err(err), + } +} + +pub(crate) fn send(socket: SysSocket, buf: &[u8], flags: c_int) -> io::Result { + syscall!( + send( + socket, + buf.as_ptr().cast(), + min(buf.len(), MAX_BUF_LEN) as c_int, + flags, + ), + PartialEq::eq, + sock::SOCKET_ERROR + ) + .map(|n| n as usize) +} + +pub(crate) fn send_vectored( + socket: SysSocket, + bufs: &[IoSlice<'_>], + flags: c_int, +) -> io::Result { + let mut nsent = 0; + syscall!( + WSASend( + socket, + // FIXME: From the `WSASend` docs [1]: + // > For a Winsock application, once the WSASend function is called, + // > the system owns these buffers and the application may not + // > access them. + // + // So what we're doing is actually UB as `bufs` needs to be `&mut + // [IoSlice<'_>]`. + // + // Tracking issue: https://github.com/rust-lang/socket2-rs/issues/129. + // + // NOTE: `send_to_vectored` has the same problem. + // + // [1] https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasend + bufs.as_ptr() as *mut _, + min(bufs.len(), DWORD::max_value() as usize) as DWORD, + &mut nsent, + flags as DWORD, + std::ptr::null_mut(), + None, + ), + PartialEq::eq, + sock::SOCKET_ERROR + ) + .map(|_| nsent as usize) +} + +pub(crate) fn send_to( + socket: SysSocket, + buf: &[u8], + addr: &SockAddr, + flags: c_int, +) -> io::Result { + syscall!( + sendto( + socket, + buf.as_ptr().cast(), + min(buf.len(), MAX_BUF_LEN) as c_int, + flags, + addr.as_ptr(), + addr.len(), + ), + PartialEq::eq, + sock::SOCKET_ERROR + ) + .map(|n| n as usize) +} + +pub(crate) fn send_to_vectored( + socket: SysSocket, + bufs: &[IoSlice<'_>], + addr: &SockAddr, + flags: c_int, +) -> io::Result { + let mut nsent = 0; + syscall!( + WSASendTo( + socket, + // FIXME: Same problem as in `send_vectored`. + bufs.as_ptr() as *mut _, + bufs.len().min(DWORD::MAX as usize) as DWORD, + &mut nsent, + flags as DWORD, + addr.as_ptr(), + addr.len(), + ptr::null_mut(), + None, + ), + PartialEq::eq, + sock::SOCKET_ERROR + ) + .map(|_| nsent as usize) +} + /// Caller must ensure `T` is the correct type for `opt` and `val`. unsafe fn getsockopt(socket: SysSocket, opt: c_int, val: c_int) -> io::Result { let mut payload: MaybeUninit = MaybeUninit::uninit(); @@ -297,7 +532,12 @@ fn ioctlsocket(socket: SysSocket, cmd: c_long, payload: &mut u_long) -> io::Resu /// Windows only API. impl crate::Socket { /// Sets `HANDLE_FLAG_INHERIT` to zero using `SetHandleInformation`. + #[cfg(feature = "all")] pub fn set_no_inherit(&self, no_inherit: bool) -> io::Result<()> { + self._set_no_inherit(no_inherit) + } + + pub(crate) fn _set_no_inherit(&self, no_inherit: bool) -> io::Result<()> { // NOTE: can't use `syscall!` because it expects the function in the // `sock::` path. let res = unsafe { @@ -322,227 +562,6 @@ pub struct Socket { } impl Socket { - pub fn recv(&self, buf: &mut [u8], flags: c_int) -> io::Result { - unsafe { - let n = { - sock::recv( - self.socket, - buf.as_mut_ptr() as *mut c_char, - clamp(buf.len()), - flags, - ) - }; - match n { - sock::SOCKET_ERROR if sock::WSAGetLastError() == sock::WSAESHUTDOWN as i32 => Ok(0), - sock::SOCKET_ERROR => Err(last_error()), - n => Ok(n as usize), - } - } - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - unsafe { - let n = { - sock::recv( - self.socket, - buf.as_mut_ptr() as *mut c_char, - clamp(buf.len()), - MSG_PEEK, - ) - }; - match n { - sock::SOCKET_ERROR if sock::WSAGetLastError() == sock::WSAESHUTDOWN as i32 => Ok(0), - sock::SOCKET_ERROR => Err(last_error()), - n => Ok(n as usize), - } - } - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SockAddr)> { - self.recv_from(buf, MSG_PEEK) - } - - pub fn recv_from(&self, buf: &mut [u8], flags: c_int) -> io::Result<(usize, SockAddr)> { - unsafe { - let mut storage: SOCKADDR_STORAGE = mem::zeroed(); - let mut addrlen = mem::size_of_val(&storage) as c_int; - - let n = { - sock::recvfrom( - self.socket, - buf.as_mut_ptr() as *mut c_char, - clamp(buf.len()), - flags, - &mut storage as *mut _ as *mut _, - &mut addrlen, - ) - }; - let n = match n { - sock::SOCKET_ERROR if sock::WSAGetLastError() == sock::WSAESHUTDOWN as i32 => 0, - sock::SOCKET_ERROR => return Err(last_error()), - n => n as usize, - }; - let addr = SockAddr::from_raw_parts(&storage as *const _ as *const _, addrlen); - Ok((n, addr)) - } - } - - pub fn recv_vectored( - &self, - bufs: &mut [IoSliceMut<'_>], - flags: c_int, - ) -> io::Result<(usize, RecvFlags)> { - let mut nread = 0; - let mut flags = flags as DWORD; - let ret = unsafe { - sock::WSARecv( - self.socket, - bufs.as_mut_ptr() as *mut WSABUF, - bufs.len().min(DWORD::MAX as usize) as DWORD, - &mut nread, - &mut flags, - ptr::null_mut(), - None, - ) - }; - - let nread = nread as usize; - if ret == 0 { - Ok((nread, RecvFlags(0))) - } else { - let error = last_error(); - match error.raw_os_error() { - Some(sock::WSAESHUTDOWN) => Ok((0, RecvFlags(0))), - Some(sock::WSAEMSGSIZE) => Ok((nread, RecvFlags(MSG_TRUNC))), - _ => Err(error), - } - } - } - - pub fn recv_from_vectored( - &self, - bufs: &mut [IoSliceMut<'_>], - flags: c_int, - ) -> io::Result<(usize, RecvFlags, SockAddr)> { - let mut nread = 0; - let mut flags = flags as DWORD; - let mut storage: SOCKADDR_STORAGE = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as c_int; - let ret = unsafe { - sock::WSARecvFrom( - self.socket, - bufs.as_mut_ptr() as *mut WSABUF, - bufs.len().min(DWORD::MAX as usize) as DWORD, - &mut nread, - &mut flags, - &mut storage as *mut SOCKADDR_STORAGE as *mut SOCKADDR, - &mut addrlen, - ptr::null_mut(), - None, - ) - }; - - let flags; - if ret == 0 { - flags = RecvFlags(0); - } else { - let error = last_error(); - if error.raw_os_error() == Some(sock::WSAEMSGSIZE) { - flags = RecvFlags(MSG_TRUNC) - } else { - return Err(error); - } - } - - let addr = unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, addrlen) }; - Ok((nread as usize, flags, addr)) - } - - pub fn send(&self, buf: &[u8], flags: c_int) -> io::Result { - unsafe { - let n = { - sock::send( - self.socket, - buf.as_ptr() as *const c_char, - clamp(buf.len()), - flags, - ) - }; - if n == sock::SOCKET_ERROR { - Err(last_error()) - } else { - Ok(n as usize) - } - } - } - - pub fn send_to(&self, buf: &[u8], flags: c_int, addr: &SockAddr) -> io::Result { - unsafe { - let n = { - sock::sendto( - self.socket, - buf.as_ptr() as *const c_char, - clamp(buf.len()), - flags, - addr.as_ptr(), - addr.len(), - ) - }; - if n == sock::SOCKET_ERROR { - Err(last_error()) - } else { - Ok(n as usize) - } - } - } - - pub fn send_vectored(&self, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result { - let mut nsent = 0; - let ret = unsafe { - sock::WSASend( - self.socket, - bufs.as_ptr() as *mut WSABUF, - bufs.len().min(DWORD::MAX as usize) as DWORD, - &mut nsent, - flags as DWORD, - std::ptr::null_mut(), - None, - ) - }; - match ret { - 0 => Ok(nsent as usize), - _ => Err(last_error()), - } - } - - pub fn send_to_vectored( - &self, - bufs: &[IoSlice<'_>], - flags: c_int, - addr: &SockAddr, - ) -> io::Result { - let mut nsent = 0; - let ret = unsafe { - sock::WSASendTo( - self.socket, - bufs.as_ptr() as *mut WSABUF, - bufs.len().min(DWORD::MAX as usize) as DWORD, - &mut nsent, - flags as DWORD, - addr.as_ptr(), - addr.len(), - std::ptr::null_mut(), - None, - ) - }; - match ret { - 0 => Ok(nsent as usize), - _ => Err(last_error()), - } - } - - // ================================================ - pub fn ttl(&self) -> io::Result { unsafe { let raw: c_int = self.getsockopt(IPPROTO_IP, IP_TTL)?; @@ -832,6 +851,7 @@ impl Socket { } } + #[cfg(feature = "all")] pub fn out_of_band_inline(&self) -> io::Result { unsafe { let raw: c_int = self.getsockopt(SOL_SOCKET, SO_OOBINLINE)?; @@ -839,6 +859,7 @@ impl Socket { } } + #[cfg(feature = "all")] pub fn set_out_of_band_inline(&self, oob_inline: bool) -> io::Result<()> { unsafe { self.setsockopt(SOL_SOCKET, SO_OOBINLINE, oob_inline as c_int) } } @@ -872,46 +893,6 @@ impl Socket { Err(last_error()) } } - - pub fn inner(self) -> SysSocket { - self.socket - } - - pub fn from_inner(socket: SysSocket) -> Socket { - Socket { socket } - } -} - -impl Read for Socket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - <&Socket>::read(&mut &*self, buf) - } -} - -impl<'a> Read for &'a Socket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.recv(buf, 0) - } -} - -impl Write for Socket { - fn write(&mut self, buf: &[u8]) -> io::Result { - <&Socket>::write(&mut &*self, buf) - } - - fn flush(&mut self) -> io::Result<()> { - <&Socket>::flush(&mut &*self) - } -} - -impl<'a> Write for &'a Socket { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.send(buf, 0) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } } impl fmt::Debug for Socket { @@ -967,57 +948,17 @@ impl IntoRawSocket for crate::Socket { impl FromRawSocket for crate::Socket { unsafe fn from_raw_socket(socket: RawSocket) -> crate::Socket { crate::Socket { - inner: Socket::from_raw_socket(socket).inner(), + inner: socket as SysSocket, } } } -impl From for net::TcpStream { - fn from(socket: Socket) -> net::TcpStream { - unsafe { net::TcpStream::from_raw_socket(socket.into_raw_socket()) } - } -} - -impl From for net::TcpListener { - fn from(socket: Socket) -> net::TcpListener { - unsafe { net::TcpListener::from_raw_socket(socket.into_raw_socket()) } - } -} - -impl From for net::UdpSocket { - fn from(socket: Socket) -> net::UdpSocket { - unsafe { net::UdpSocket::from_raw_socket(socket.into_raw_socket()) } - } -} - -impl From for Socket { - fn from(socket: net::TcpStream) -> Socket { - unsafe { Socket::from_raw_socket(socket.into_raw_socket()) } - } -} - -impl From for Socket { - fn from(socket: net::TcpListener) -> Socket { - unsafe { Socket::from_raw_socket(socket.into_raw_socket()) } - } -} - -impl From for Socket { - fn from(socket: net::UdpSocket) -> Socket { - unsafe { Socket::from_raw_socket(socket.into_raw_socket()) } - } -} - pub(crate) fn close(socket: SysSocket) { unsafe { let _ = sock::closesocket(socket); } } -fn clamp(input: usize) -> c_int { - cmp::min(input, ::max_value() as usize) as c_int -} - fn dur2ms(dur: Option) -> io::Result { match dur { Some(dur) => { @@ -1150,6 +1091,7 @@ fn test_ipv6() { } #[test] +#[cfg(feature = "all")] fn test_out_of_band_inline() { let tcp = Socket { socket: socket(AF_INET, SOCK_STREAM, 0).unwrap(), diff --git a/src/tests.rs b/src/tests.rs index 3bda857e..4b51568e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -157,15 +157,12 @@ fn send_recv_vectored() { let (socket_a, socket_b) = udp_pair_connected(); let sent = socket_a - .send_vectored( - &[ - IoSlice::new(b"the"), - IoSlice::new(b"weeknight"), - IoSlice::new(b"would"), - IoSlice::new(b"yellow"), - ], - 0, - ) + .send_vectored(&[ + IoSlice::new(b"the"), + IoSlice::new(b"weeknight"), + IoSlice::new(b"would"), + IoSlice::new(b"yellow"), + ]) .unwrap(); assert_eq!(sent, 23); @@ -177,17 +174,14 @@ fn send_recv_vectored() { let mut ow = [0u8; 2]; let (received, flags) = socket_b - .recv_vectored( - &mut [ - IoSliceMut::new(&mut the), - IoSliceMut::new(&mut wee), - IoSliceMut::new(&mut knight), - IoSliceMut::new(&mut would), - IoSliceMut::new(&mut yell), - IoSliceMut::new(&mut ow), - ], - 0, - ) + .recv_vectored(&mut [ + IoSliceMut::new(&mut the), + IoSliceMut::new(&mut wee), + IoSliceMut::new(&mut knight), + IoSliceMut::new(&mut would), + IoSliceMut::new(&mut yell), + IoSliceMut::new(&mut ow), + ]) .unwrap(); assert_eq!(received, 23); #[cfg(all(unix, not(target_os = "redox")))] @@ -221,7 +215,6 @@ fn send_from_recv_to_vectored() { IoSlice::new(b"menswear"), ], &addr_b, - 0, ) .unwrap(); assert_eq!(sent, 18); @@ -231,15 +224,12 @@ fn send_from_recv_to_vectored() { let mut men = [0u8; 3]; let mut swear = [0u8; 5]; let (received, flags, addr) = socket_b - .recv_from_vectored( - &mut [ - IoSliceMut::new(&mut surgeon), - IoSliceMut::new(&mut has), - IoSliceMut::new(&mut men), - IoSliceMut::new(&mut swear), - ], - 0, - ) + .recv_from_vectored(&mut [ + IoSliceMut::new(&mut surgeon), + IoSliceMut::new(&mut has), + IoSliceMut::new(&mut men), + IoSliceMut::new(&mut swear), + ]) .unwrap(); assert_eq!(received, 18); @@ -270,7 +260,7 @@ fn recv_vectored_truncated() { let mut buffer = [0u8; 24]; let (received, flags) = socket_b - .recv_vectored(&mut [IoSliceMut::new(&mut buffer)], 0) + .recv_vectored(&mut [IoSliceMut::new(&mut buffer)]) .unwrap(); assert_eq!(received, 24); assert_eq!(flags.is_truncated(), true); @@ -294,7 +284,7 @@ fn recv_from_vectored_truncated() { let mut buffer = [0u8; 24]; let (received, flags, addr) = socket_b - .recv_from_vectored(&mut [IoSliceMut::new(&mut buffer)], 0) + .recv_from_vectored(&mut [IoSliceMut::new(&mut buffer)]) .unwrap(); assert_eq!(received, 24); assert_eq!(flags.is_truncated(), true); diff --git a/tests/socket.rs b/tests/socket.rs index 5b10a8ea..973ff10b 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1,4 +1,4 @@ -#[cfg(any(windows, all(feature = "all", target_vendor = "apple")))] +#[cfg(any(windows, target_vendor = "apple"))] use std::io; #[cfg(unix)] use std::os::unix::io::AsRawFd; @@ -26,6 +26,28 @@ fn set_nonblocking() { assert_nonblocking(&socket, false); } +#[test] +fn default_flags() { + let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); + #[cfg(unix)] + assert_close_on_exec(&socket, true); + #[cfg(target_vendor = "apple")] + assert_flag_no_sigpipe(&socket, true); + #[cfg(windows)] + assert_flag_no_inherit(&socket, true); +} + +#[test] +fn no_default_flags() { + let socket = Socket::new_raw(Domain::IPV4, Type::STREAM, None).unwrap(); + #[cfg(unix)] + assert_close_on_exec(&socket, false); + #[cfg(target_vendor = "apple")] + assert_flag_no_sigpipe(&socket, false); + #[cfg(windows)] + assert_flag_no_inherit(&socket, false); +} + #[cfg(all( feature = "all", any( @@ -61,17 +83,17 @@ pub fn assert_nonblocking(_: &S, _: bool) { // No way to get this information... } -#[cfg(unix)] +#[cfg(all(unix, feature = "all"))] #[test] fn set_cloexec() { let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); - assert_close_on_exec(&socket, false); - - socket.set_cloexec(true).unwrap(); assert_close_on_exec(&socket, true); socket.set_cloexec(false).unwrap(); assert_close_on_exec(&socket, false); + + socket.set_cloexec(true).unwrap(); + assert_close_on_exec(&socket, true); } #[cfg(all( @@ -103,17 +125,17 @@ where assert_eq!(flags & libc::FD_CLOEXEC != 0, want, "CLOEXEC option"); } -#[cfg(windows)] +#[cfg(all(windows, feature = "all"))] #[test] fn set_no_inherit() { let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); - assert_flag_no_inherit(&socket, false); - - socket.set_no_inherit(true).unwrap(); assert_flag_no_inherit(&socket, true); socket.set_no_inherit(false).unwrap(); assert_flag_no_inherit(&socket, false); + + socket.set_no_inherit(true).unwrap(); + assert_flag_no_inherit(&socket, true); } #[cfg(all(feature = "all", windows))] @@ -147,17 +169,17 @@ where #[test] fn set_nosigpipe() { let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); - assert_flag_no_sigpipe(&socket, false); - - socket.set_nosigpipe(true).unwrap(); assert_flag_no_sigpipe(&socket, true); socket.set_nosigpipe(false).unwrap(); assert_flag_no_sigpipe(&socket, false); + + socket.set_nosigpipe(true).unwrap(); + assert_flag_no_sigpipe(&socket, true); } /// Assert that `SO_NOSIGPIPE` is set on `socket`. -#[cfg(all(feature = "all", target_vendor = "apple"))] +#[cfg(target_vendor = "apple")] #[track_caller] pub fn assert_flag_no_sigpipe(socket: &S, want: bool) where