blob: 4fa268847641162ae05b2d5faf7a96665a9b1426 [file] [log] [blame]
src/socket.rs
src/sys/unix.rs
src/sys/windows.rs
commit 9f0fbf2ed487668e4e2b5357a8e4a167e7ec903a
Author: Thomas de Zeeuw <[email protected]>
Date: Sat Jan 18 16:43:21 2020 +0100
Part 1 of the rewrite
Currently not functional on Windows and a lot of methods are removed,
but its a start.
diff --git a/src/socket.rs b/src/socket.rs
index 354c5cf..424e094 100644
--- a/src/socket.rs
+++ b/src/socket.rs
@@ -8,201 +8,107 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::fmt;
-use std::io::{self, Read, Write};
-use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown};
-#[cfg(all(unix, feature = "unix"))]
-use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
-use std::time::Duration;
+use std::net::{Shutdown, TcpListener, TcpStream, UdpSocket};
+#[cfg(unix)]
+use std::os::unix::io::{FromRawFd, IntoRawFd};
+use std::{fmt, io};
-use crate::sys;
+use crate::sys::{self, c_int};
use crate::{Domain, Protocol, SockAddr, Type};
-/// Newtype, owned, wrapper around a system socket.
+/// An owned system socket.
///
-/// This type simply wraps an instance of a file descriptor (`c_int`) on Unix
-/// and an instance of `SOCKET` on Windows. This is the main type exported by
-/// this crate and is intended to mirror the raw semantics of sockets on
-/// platforms as closely as possible. Almost all methods correspond to
-/// precisely one libc or OS API call which is essentially just a "Rustic
-/// translation" of what's below.
+/// This type simply wraps an instance of a file descriptor (`int`) on Unix and
+/// an instance of `SOCKET` on Windows. This is the main type exported by this
+/// crate and is intended to mirror the raw semantics of sockets on platforms as
+/// closely as possible. All methods correspond to precisely one libc or OS API
+/// call which is essentially just a "Rustic translation" of what's below.
+///
+/// # Notes
+///
+/// This type can be converted to and from all network types provided by the
+/// standard library using the [`From`] and [`Into`] traits. Is up to the user
+/// to ensure the socket is setup correctly for a given type!
///
/// # Examples
///
-/// ```no_run
-/// use std::net::SocketAddr;
-/// use socket2::{Socket, Domain, Type, SockAddr};
+/// ```
+/// # fn main() -> std::io::Result<()> {
+/// use std::net::{SocketAddr, TcpListener};
+/// use socket2::{Socket, Domain, Type};
///
-/// // create a TCP listener bound to two addresses
-/// let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
+/// // Create a new `Socket`.
+/// let socket = Socket::new(Domain::ipv4(), Type::stream(), None)?;
///
-/// socket.bind(&"127.0.0.1:12345".parse::<SocketAddr>().unwrap().into()).unwrap();
-/// socket.bind(&"127.0.0.1:12346".parse::<SocketAddr>().unwrap().into()).unwrap();
-/// socket.listen(128).unwrap();
+/// // Bind the socket to an addresses.
+/// let addr1: SocketAddr = "127.0.0.1:15123".parse().unwrap();
+/// socket.bind(&addr1.into())?;
///
-/// let listener = socket.into_tcp_listener();
-/// // ...
+/// // Start listening on the socket.
+/// socket.listen(128)?;
+///
+/// // Finally convert it to `TcpListener` from the standard library. Now it can
+/// // be used like any other `TcpListener`.
+/// let listener: TcpListener = socket.into();
+/// # drop(listener);
+/// # Ok(())
+/// # }
/// ```
pub struct Socket {
- // The `sys` module most have access to the socket.
- pub(crate) inner: sys::Socket,
+ // The `sys` module must have access to the raw socket to implement OS
+ // specific additional methods, e.g. Unix Domain sockets (UDS).
+ pub(crate) inner: sys::RawSocket,
}
impl Socket {
/// Creates a new socket ready to be configured.
///
- /// This function corresponds to `socket(2)` and simply creates a new
- /// socket, no other configuration is done and further functions must be
- /// invoked to configure this socket.
+ /// This function corresponds to `socket(2)`.
pub fn new(domain: Domain, type_: Type, protocol: Option<Protocol>) -> io::Result<Socket> {
- let protocol = protocol.map(|p| p.0).unwrap_or(0);
- Ok(Socket {
- inner: sys::Socket::new(domain.0, type_.0, protocol)?,
- })
- }
-
- /// Creates a pair of sockets which are connected to each other.
- ///
- /// This function corresponds to `socketpair(2)`.
- ///
- /// This function is only available on Unix when the `pair` feature is
- /// enabled.
- #[cfg(all(unix, feature = "pair"))]
- pub fn pair(
- domain: Domain,
- type_: Type,
- protocol: Option<Protocol>,
- ) -> io::Result<(Socket, Socket)> {
- let protocol = protocol.map(|p| p.0).unwrap_or(0);
- let sockets = sys::Socket::pair(domain.0, type_.0, protocol)?;
- Ok((Socket { inner: sockets.0 }, Socket { inner: sockets.1 }))
- }
-
- /// Consumes this `Socket`, converting it to a `TcpStream`.
- pub fn into_tcp_stream(self) -> net::TcpStream {
- self.into()
- }
-
- /// Consumes this `Socket`, converting it to a `TcpListener`.
- pub fn into_tcp_listener(self) -> net::TcpListener {
- self.into()
- }
-
- /// Consumes this `Socket`, converting it to a `UdpSocket`.
- pub fn into_udp_socket(self) -> net::UdpSocket {
- self.into()
- }
-
- /// Consumes this `Socket`, converting it into a `UnixStream`.
- ///
- /// This function is only available on Unix when the `unix` feature is
- /// enabled.
- #[cfg(all(unix, feature = "unix"))]
- pub fn into_unix_stream(self) -> UnixStream {
- self.into()
- }
-
- /// Consumes this `Socket`, converting it into a `UnixListener`.
- ///
- /// This function is only available on Unix when the `unix` feature is
- /// enabled.
- #[cfg(all(unix, feature = "unix"))]
- pub fn into_unix_listener(self) -> UnixListener {
- self.into()
- }
-
- /// Consumes this `Socket`, converting it into a `UnixDatagram`.
- ///
- /// This function is only available on Unix when the `unix` feature is
- /// enabled.
- #[cfg(all(unix, feature = "unix"))]
- pub fn into_unix_datagram(self) -> UnixDatagram {
- self.into()
+ sys::socket(domain.0, type_.0, protocol.map(|p| p.0).unwrap_or(0))
}
/// Initiate a connection on this socket to the specified address.
///
- /// This function directly corresponds to the connect(2) function on Windows
- /// and Unix.
- ///
- /// An error will be returned if `listen` or `connect` has already been
- /// called on this builder.
+ /// This function directly corresponds to the `connect(2)` function.
pub fn connect(&self, addr: &SockAddr) -> io::Result<()> {
- self.inner.connect(addr)
- }
-
- /// Initiate a connection on this socket to the specified address, only
- /// only waiting for a certain period of time for the connection to be
- /// established.
- ///
- /// Unlike many other methods on `Socket`, this does *not* correspond to a
- /// single C function. It sets the socket to nonblocking mode, connects via
- /// connect(2), and then waits for the connection to complete with poll(2)
- /// on Unix and select on Windows. When the connection is complete, the
- /// socket is set back to blocking mode. On Unix, this will loop over
- /// `EINTR` errors.
- ///
- /// # Warnings
- ///
- /// The nonblocking state of the socket is overridden by this function -
- /// it will be returned in blocking mode on success, and in an indeterminate
- /// state on failure.
- ///
- /// If the connection request times out, it may still be processing in the
- /// background - a second call to `connect` or `connect_timeout` may fail.
- pub fn connect_timeout(&self, addr: &SockAddr, timeout: Duration) -> io::Result<()> {
- self.inner.connect_timeout(addr, timeout)
+ sys::connect(self.inner, addr.as_ptr(), addr.len())
}
/// Binds this socket to the specified address.
///
- /// This function directly corresponds to the bind(2) function on Windows
- /// and Unix.
+ /// This function directly corresponds to the `bind(2)` function.
pub fn bind(&self, addr: &SockAddr) -> io::Result<()> {
- self.inner.bind(addr)
+ sys::bind(self.inner, addr.as_ptr(), addr.len())
}
- /// Mark a socket as ready to accept incoming connection requests using
- /// accept()
+ /// Returns the socket address of the local half of this connection.
///
- /// This function directly corresponds to the listen(2) function on Windows
- /// and Unix.
- ///
- /// An error will be returned if `listen` or `connect` has already been
- /// called on this builder.
- pub fn listen(&self, backlog: i32) -> io::Result<()> {
- self.inner.listen(backlog)
+ /// This function directly corresponds to the `getsockname(2)` function.
+ pub fn local_addr(&self) -> io::Result<SockAddr> {
+ sys::getsockname(self.inner)
}
- /// Accept a new incoming connection from this listener.
+ /// Returns the socket address of the remote peer of this connection.
///
- /// 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.
- pub fn accept(&self) -> io::Result<(Socket, SockAddr)> {
- self.inner
- .accept()
- .map(|(socket, addr)| (Socket { inner: socket }, addr))
- }
-
- /// Returns the socket address of the local half of this TCP connection.
- pub fn local_addr(&self) -> io::Result<SockAddr> {
- self.inner.local_addr()
+ /// This function directly corresponds to the `getpeername(2)` function.
+ pub fn peer_addr(&self) -> io::Result<SockAddr> {
+ sys::getpeername(self.inner)
}
- /// Returns the socket address of the remote peer of this TCP connection.
- pub fn peer_addr(&self) -> io::Result<SockAddr> {
- self.inner.peer_addr()
+ /// Mark a socket as ready to accept incoming connection requests using
+ /// `accept(2)`.
+ ///
+ /// This function directly corresponds to the `listen(2)` function.
+ pub fn listen(&self, backlog: c_int) -> io::Result<()> {
+ sys::listen(self.inner, backlog)
}
- /// Creates a new independently owned handle to the underlying socket.
+ /// Accept a new incoming connection from this listener.
///
- /// The returned `TcpStream` is a reference to the same stream that this
- /// object references. Both handles will read and write the same stream of
- /// data, and options set on one stream will be propagated to the other
- /// stream.
- pub fn try_clone(&self) -> io::Result<Socket> {
- self.inner.try_clone().map(|s| Socket { inner: s })
+ /// This function directly corresponds to the `accept(2)` function.
+ pub fn accept(&self) -> io::Result<(Socket, SockAddr)> {
+ sys::accept(self.inner)
}
/// Get the value of the `SO_ERROR` option on this socket.
@@ -211,15 +117,14 @@ impl Socket {
/// the field in the process. This can be useful for checking errors between
/// calls.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- self.inner.take_error()
- }
-
- /// Moves this TCP stream into or out of nonblocking mode.
- ///
- /// On Unix this corresponds to calling fcntl, and on Windows this
- /// corresponds to calling ioctlsocket.
- pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
- self.inner.set_nonblocking(nonblocking)
+ self.getsockopt::<c_int>(libc::SOL_SOCKET, libc::SO_ERROR)
+ .map(|errno| {
+ if errno == 0 {
+ None
+ } else {
+ Some(io::Error::from_raw_os_error(errno))
+ }
+ })
}
/// Shuts down the read, write, or both halves of this connection.
@@ -227,689 +132,110 @@ impl Socket {
/// This function will cause all pending and future I/O on the specified
/// portions to return immediately with an appropriate value.
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
- self.inner.shutdown(how)
- }
-
- /// 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.
- ///
- /// [`connect`]: #method.connect
- pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.inner.recv(buf)
- }
-
- /// Receives data on the socket from the remote adress to which it is
- /// connected, without removing that data from the queue. On success,
- /// returns the number of bytes peeked.
- ///
- /// 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<usize> {
- self.inner.peek(buf)
- }
-
- /// 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)
- }
-
- /// Receives data from the socket, without removing it from the queue.
- ///
- /// Successive calls return the same data. This is accomplished by passing
- /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call.
- ///
- /// 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)
- }
-
- /// Sends data on the socket to a connected peer.
- ///
- /// This is typically used on TCP sockets or datagram sockets which have
- /// been connected.
- ///
- /// On success returns the number of bytes that were sent.
- pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
- self.inner.send(buf)
- }
-
- /// 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.
- pub fn send_to(&self, buf: &[u8], addr: &SockAddr) -> io::Result<usize> {
- self.inner.send_to(buf, addr)
- }
-
- // ================================================
-
- /// Gets the value of the `IP_TTL` option for this socket.
- ///
- /// For more information about this option, see [`set_ttl`][link].
- ///
- /// [link]: #method.set_ttl
- pub fn ttl(&self) -> io::Result<u32> {
- self.inner.ttl()
- }
-
- /// Sets the value for the `IP_TTL` option on this socket.
- ///
- /// This value sets the time-to-live field that is used in every packet sent
- /// from this socket.
- pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
- self.inner.set_ttl(ttl)
- }
-
- /// Gets the value of the `IPV6_UNICAST_HOPS` option for this socket.
- ///
- /// Specifies the hop limit for ipv6 unicast packets
- pub fn unicast_hops_v6(&self) -> io::Result<u32> {
- self.inner.unicast_hops_v6()
- }
-
- /// Sets the value for the `IPV6_UNICAST_HOPS` option on this socket.
- ///
- /// Specifies the hop limit for ipv6 unicast packets
- pub fn set_unicast_hops_v6(&self, ttl: u32) -> io::Result<()> {
- self.inner.set_unicast_hops_v6(ttl)
- }
-
- /// Gets the value of the `IPV6_V6ONLY` option for this socket.
- ///
- /// For more information about this option, see [`set_only_v6`][link].
- ///
- /// [link]: #method.set_only_v6
- pub fn only_v6(&self) -> io::Result<bool> {
- self.inner.only_v6()
- }
-
- /// Sets the value for the `IPV6_V6ONLY` option on this socket.
- ///
- /// If this is set to `true` then the socket is restricted to sending and
- /// receiving IPv6 packets only. In this case two IPv4 and IPv6 applications
- /// can bind the same port at the same time.
- ///
- /// If this is set to `false` then the socket can be used to send and
- /// receive packets from an IPv4-mapped IPv6 address.
- pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
- self.inner.set_only_v6(only_v6)
- }
-
- /// Returns the read timeout of this socket.
- ///
- /// If the timeout is `None`, then `read` calls will block indefinitely.
- pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
- self.inner.read_timeout()
- }
-
- /// Sets the read timeout to the timeout specified.
- ///
- /// If the value specified is `None`, then `read` calls will block
- /// indefinitely. It is an error to pass the zero `Duration` to this
- /// method.
- pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
- self.inner.set_read_timeout(dur)
- }
-
- /// Returns the write timeout of this socket.
- ///
- /// If the timeout is `None`, then `write` calls will block indefinitely.
- pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
- self.inner.write_timeout()
- }
-
- /// Sets the write timeout to the timeout specified.
- ///
- /// If the value specified is `None`, then `write` calls will block
- /// indefinitely. It is an error to pass the zero `Duration` to this
- /// method.
- pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
- self.inner.set_write_timeout(dur)
- }
-
- /// Gets the value of the `TCP_NODELAY` option on this socket.
- ///
- /// For more information about this option, see [`set_nodelay`][link].
- ///
- /// [link]: #method.set_nodelay
- pub fn nodelay(&self) -> io::Result<bool> {
- self.inner.nodelay()
- }
-
- /// Sets the value of the `TCP_NODELAY` option on this socket.
- ///
- /// If set, this option disables the Nagle algorithm. This means that
- /// segments are always sent as soon as possible, even if there is only a
- /// small amount of data. When not set, data is buffered until there is a
- /// sufficient amount to send out, thereby avoiding the frequent sending of
- /// small packets.
- pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
- self.inner.set_nodelay(nodelay)
- }
-
- /// Sets the value of the `SO_BROADCAST` option for this socket.
- ///
- /// When enabled, this socket is allowed to send packets to a broadcast
- /// address.
- pub fn broadcast(&self) -> io::Result<bool> {
- self.inner.broadcast()
- }
-
- /// Gets the value of the `SO_BROADCAST` option for this socket.
- ///
- /// For more information about this option, see
- /// [`set_broadcast`][link].
- ///
- /// [link]: #method.set_broadcast
- pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> {
- self.inner.set_broadcast(broadcast)
- }
-
- /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket.
- ///
- /// For more information about this option, see
- /// [`set_multicast_loop_v4`][link].
- ///
- /// [link]: #method.set_multicast_loop_v4
- pub fn multicast_loop_v4(&self) -> io::Result<bool> {
- self.inner.multicast_loop_v4()
- }
-
- /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket.
- ///
- /// If enabled, multicast packets will be looped back to the local socket.
- /// Note that this may not have any affect on IPv6 sockets.
- pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> {
- self.inner.set_multicast_loop_v4(multicast_loop_v4)
- }
-
- /// Gets the value of the `IP_MULTICAST_TTL` option for this socket.
- ///
- /// For more information about this option, see
- /// [`set_multicast_ttl_v4`][link].
- ///
- /// [link]: #method.set_multicast_ttl_v4
- pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
- self.inner.multicast_ttl_v4()
- }
-
- /// Sets the value of the `IP_MULTICAST_TTL` option for this socket.
- ///
- /// Indicates the time-to-live value of outgoing multicast packets for
- /// this socket. The default value is 1 which means that multicast packets
- /// don't leave the local network unless explicitly requested.
- ///
- /// Note that this may not have any affect on IPv6 sockets.
- pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> {
- self.inner.set_multicast_ttl_v4(multicast_ttl_v4)
- }
-
- /// Gets the value of the `IPV6_MULTICAST_HOPS` option for this socket
- ///
- /// For more information about this option, see
- /// [`set_multicast_hops_v6`][link].
- ///
- /// [link]: #method.set_multicast_hops_v6
- pub fn multicast_hops_v6(&self) -> io::Result<u32> {
- self.inner.multicast_hops_v6()
- }
-
- /// Sets the value of the `IPV6_MULTICAST_HOPS` option for this socket
- ///
- /// Indicates the number of "routers" multicast packets will transit for
- /// this socket. The default value is 1 which means that multicast packets
- /// don't leave the local network unless explicitly requested.
- pub fn set_multicast_hops_v6(&self, hops: u32) -> io::Result<()> {
- self.inner.set_multicast_hops_v6(hops)
- }
-
- /// Gets the value of the `IP_MULTICAST_IF` option for this socket.
- ///
- /// For more information about this option, see
- /// [`set_multicast_if_v4`][link].
- ///
- /// [link]: #method.set_multicast_if_v4
- ///
- /// Returns the interface to use for routing multicast packets.
- pub fn multicast_if_v4(&self) -> io::Result<Ipv4Addr> {
- self.inner.multicast_if_v4()
- }
-
- /// Sets the value of the `IP_MULTICAST_IF` option for this socket.
- ///
- /// Specifies the interface to use for routing multicast packets.
- pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> {
- self.inner.set_multicast_if_v4(interface)
- }
-
- /// Gets the value of the `IPV6_MULTICAST_IF` option for this socket.
- ///
- /// For more information about this option, see
- /// [`set_multicast_if_v6`][link].
- ///
- /// [link]: #method.set_multicast_if_v6
- ///
- /// Returns the interface to use for routing multicast packets.
- pub fn multicast_if_v6(&self) -> io::Result<u32> {
- self.inner.multicast_if_v6()
- }
-
- /// Sets the value of the `IPV6_MULTICAST_IF` option for this socket.
- ///
- /// Specifies the interface to use for routing multicast packets. Unlike ipv4, this
- /// is generally required in ipv6 contexts where network routing prefixes may
- /// overlap.
- pub fn set_multicast_if_v6(&self, interface: u32) -> io::Result<()> {
- self.inner.set_multicast_if_v6(interface)
- }
-
- /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket.
- ///
- /// For more information about this option, see
- /// [`set_multicast_loop_v6`][link].
- ///
- /// [link]: #method.set_multicast_loop_v6
- pub fn multicast_loop_v6(&self) -> io::Result<bool> {
- self.inner.multicast_loop_v6()
- }
-
- /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket.
- ///
- /// Controls whether this socket sees the multicast packets it sends itself.
- /// Note that this may not have any affect on IPv4 sockets.
- pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> {
- self.inner.set_multicast_loop_v6(multicast_loop_v6)
- }
-
- /// Executes an operation of the `IP_ADD_MEMBERSHIP` type.
- ///
- /// This function specifies a new multicast group for this socket to join.
- /// The address must be a valid multicast address, and `interface` is the
- /// address of the local interface with which the system should join the
- /// multicast group. If it's equal to `INADDR_ANY` then an appropriate
- /// interface is chosen by the system.
- pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
- self.inner.join_multicast_v4(multiaddr, interface)
- }
-
- /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type.
- ///
- /// This function specifies a new multicast group for this socket to join.
- /// The address must be a valid multicast address, and `interface` is the
- /// index of the interface to join/leave (or 0 to indicate any interface).
- pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
- self.inner.join_multicast_v6(multiaddr, interface)
- }
-
- /// Executes an operation of the `IP_DROP_MEMBERSHIP` type.
- ///
- /// For more information about this option, see
- /// [`join_multicast_v4`][link].
- ///
- /// [link]: #method.join_multicast_v4
- pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
- self.inner.leave_multicast_v4(multiaddr, interface)
- }
-
- /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type.
- ///
- /// For more information about this option, see
- /// [`join_multicast_v6`][link].
- ///
- /// [link]: #method.join_multicast_v6
- pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
- self.inner.leave_multicast_v6(multiaddr, interface)
- }
-
- /// Reads the linger duration for this socket by getting the SO_LINGER
- /// option
- pub fn linger(&self) -> io::Result<Option<Duration>> {
- self.inner.linger()
- }
-
- /// Sets the linger duration of this socket by setting the SO_LINGER option
- pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
- self.inner.set_linger(dur)
- }
-
- /// Check the `SO_REUSEADDR` option on this socket.
- pub fn reuse_address(&self) -> io::Result<bool> {
- self.inner.reuse_address()
+ sys::shutdown(self.inner, how)
}
+}
- /// Set value for the `SO_REUSEADDR` option on this socket.
+impl Socket {
+ /// Set a socket option.
///
- /// This indicates that futher calls to `bind` may allow reuse of local
- /// addresses. For IPv4 sockets this means that a socket may bind even when
- /// there's a socket already listening on this port.
- pub fn set_reuse_address(&self, reuse: bool) -> io::Result<()> {
- self.inner.set_reuse_address(reuse)
+ /// This function directly corresponds to the `setsockopt(2)` function. As
+ /// different options use different option types the user must define the
+ /// correct type `T`!
+ pub fn setsockopt<T>(&self, level: c_int, optname: c_int, opt: &T) -> io::Result<()> {
+ sys::setsockopt(self.inner, level, optname, opt)
}
- /// Gets the value of the `SO_RCVBUF` option on this socket.
- ///
- /// For more information about this option, see
- /// [`set_recv_buffer_size`][link].
+ /// Get a socket option.
///
- /// [link]: #method.set_recv_buffer_size
- pub fn recv_buffer_size(&self) -> io::Result<usize> {
- self.inner.recv_buffer_size()
- }
-
- /// Sets the value of the `SO_RCVBUF` option on this socket.
+ /// This function directly corresponds to the `getsockopt(2)` function. As
+ /// different options have different return types the user must define the
+ /// return type `T` correctly!
///
- /// Changes the size of the operating system's receive buffer associated
- /// with the socket.
- pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> {
- self.inner.set_recv_buffer_size(size)
- }
-
- /// Gets the value of the `SO_SNDBUF` option on this socket.
+ /// For an example usage see [`Socket::take_error`].
///
- /// For more information about this option, see [`set_send_buffer`][link].
+ /// # Notes
///
- /// [link]: #method.set_send_buffer
- pub fn send_buffer_size(&self) -> io::Result<usize> {
- self.inner.send_buffer_size()
+ /// Currently this will panic (in debug mode) if `T` isn't completely
+ /// written to, it doesn't support options which partly write to `T`.
+ pub fn getsockopt<T>(&self, level: c_int, optname: c_int) -> io::Result<T> {
+ sys::getsockopt(self.inner, level, optname)
}
- /// Sets the value of the `SO_SNDBUF` option on this socket.
+ /// Manipulate the file descriptor options of the socket.
///
- /// Changes the size of the operating system's send buffer associated with
- /// the socket.
- pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> {
- self.inner.set_send_buffer_size(size)
- }
-
- /// Returns whether keepalive messages are enabled on this socket, and if so
- /// the duration of time between them.
+ /// This function directly corresponds to the `fcntl(2)` function. As
+ /// different command have different options the user must defined the
+ /// correct type `T`!
///
- /// For more information about this option, see [`set_keepalive`][link].
+ /// # Examples
///
- /// [link]: #method.set_keepalive
- pub fn keepalive(&self) -> io::Result<Option<Duration>> {
- self.inner.keepalive()
- }
-
- /// Sets whether keepalive messages are enabled to be sent on this socket.
+ /// The following example retrieves and sets the file descriptor flags.
///
- /// On Unix, this option will set the `SO_KEEPALIVE` as well as the
- /// `TCP_KEEPALIVE` or `TCP_KEEPIDLE` option (depending on your platform).
- /// On Windows, this will set the `SIO_KEEPALIVE_VALS` option.
+ /// ```
+ /// use std::io;
+ /// use socket2::{Domain, Socket, Type};
///
- /// If `None` is specified then keepalive messages are disabled, otherwise
- /// the duration specified will be the time to remain idle before sending a
- /// TCP keepalive probe.
+ /// # fn main() -> io::Result<()> {
+ /// let socket = Socket::new(Domain::ipv4(), Type::stream(), None)?;
///
- /// Some platforms specify this value in seconds, so sub-second
- /// specifications may be omitted.
- pub fn set_keepalive(&self, keepalive: Option<Duration>) -> io::Result<()> {
- self.inner.set_keepalive(keepalive)
- }
-
- /// Check the value of the `SO_REUSEPORT` option on this socket.
+ /// // Retrieve the flags, using nothing `()` as argument.
+ /// let flags = socket.fcntl(libc::F_GETFD, ())?;
+ /// assert!((flags & libc::FD_CLOEXEC) == 0);
///
- /// This function is only available on Unix.
- #[cfg(all(unix, not(target_os = "solaris")))]
- pub fn reuse_port(&self) -> io::Result<bool> {
- self.inner.reuse_port()
- }
-
- /// Set value for the `SO_REUSEPORT` option on this socket.
+ /// // Now we set the `FD_CLOEXEC` flag.
+ /// socket.fcntl(libc::F_SETFD, flags | libc::FD_CLOEXEC)?;
///
- /// This indicates that further calls to `bind` may allow reuse of local
- /// addresses. For IPv4 sockets this means that a socket may bind even when
- /// there's a socket already listening on this port.
+ /// // Now the flag should be set.
+ /// let flags = socket.fcntl(libc::F_GETFD, ())?;
+ /// assert!((flags & libc::FD_CLOEXEC) != 0);
///
- /// This function is only available on Unix.
- #[cfg(all(unix, not(target_os = "solaris")))]
- pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> {
- self.inner.set_reuse_port(reuse)
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn fcntl<T>(&self, cmd: c_int, arg: T) -> io::Result<c_int> {
+ sys::fcntl(self.inner, cmd, arg)
}
}
-impl Read for Socket {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- self.inner.read(buf)
+impl From<TcpStream> for Socket {
+ fn from(socket: TcpStream) -> Socket {
+ unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
}
}
-impl<'a> Read for &'a Socket {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- (&self.inner).read(buf)
+impl Into<TcpStream> for Socket {
+ fn into(self) -> TcpStream {
+ unsafe { TcpStream::from_raw_fd(self.into_raw_fd()) }
}
}
-impl Write for Socket {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- self.inner.write(buf)
- }
-
- fn flush(&mut self) -> io::Result<()> {
- self.inner.flush()
+impl From<TcpListener> for Socket {
+ fn from(socket: TcpListener) -> Socket {
+ unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
}
}
-impl<'a> Write for &'a Socket {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- (&self.inner).write(buf)
- }
-
- fn flush(&mut self) -> io::Result<()> {
- (&self.inner).flush()
+impl Into<TcpListener> for Socket {
+ fn into(self) -> TcpListener {
+ unsafe { TcpListener::from_raw_fd(self.into_raw_fd()) }
}
}
-impl fmt::Debug for Socket {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.inner.fmt(f)
+impl From<UdpSocket> for Socket {
+ fn from(socket: UdpSocket) -> Socket {
+ unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
}
}
-impl From<net::TcpStream> for Socket {
- fn from(socket: net::TcpStream) -> Socket {
- Socket {
- inner: socket.into(),
- }
+impl Into<UdpSocket> for Socket {
+ fn into(self) -> UdpSocket {
+ unsafe { UdpSocket::from_raw_fd(self.into_raw_fd()) }
}
}
-impl From<net::TcpListener> for Socket {
- fn from(socket: net::TcpListener) -> Socket {
- Socket {
- inner: socket.into(),
- }
- }
-}
-
-impl From<net::UdpSocket> for Socket {
- fn from(socket: net::UdpSocket) -> Socket {
- Socket {
- inner: socket.into(),
- }
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<UnixStream> for Socket {
- fn from(socket: UnixStream) -> Socket {
- Socket {
- inner: socket.into(),
- }
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<UnixListener> for Socket {
- fn from(socket: UnixListener) -> Socket {
- Socket {
- inner: socket.into(),
- }
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<UnixDatagram> for Socket {
- fn from(socket: UnixDatagram) -> Socket {
- Socket {
- inner: socket.into(),
- }
- }
-}
-
-impl From<Socket> for net::TcpStream {
- fn from(socket: Socket) -> net::TcpStream {
- socket.inner.into()
- }
-}
-
-impl From<Socket> for net::TcpListener {
- fn from(socket: Socket) -> net::TcpListener {
- socket.inner.into()
- }
-}
-
-impl From<Socket> for net::UdpSocket {
- fn from(socket: Socket) -> net::UdpSocket {
- socket.inner.into()
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<Socket> for UnixStream {
- fn from(socket: Socket) -> UnixStream {
- socket.inner.into()
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<Socket> for UnixListener {
- fn from(socket: Socket) -> UnixListener {
- socket.inner.into()
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<Socket> for UnixDatagram {
- fn from(socket: Socket) -> UnixDatagram {
- socket.inner.into()
- }
-}
-
-#[cfg(test)]
-mod test {
- use std::net::SocketAddr;
-
- use super::*;
-
- #[test]
- fn connect_timeout_unrouteable() {
- // this IP is unroutable, so connections should always time out
- let addr = "10.255.255.1:80".parse::<SocketAddr>().unwrap().into();
-
- let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
- match socket.connect_timeout(&addr, Duration::from_millis(250)) {
- Ok(_) => panic!("unexpected success"),
- Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {}
- Err(e) => panic!("unexpected error {}", e),
- }
- }
-
- #[test]
- fn connect_timeout_unbound() {
- // bind and drop a socket to track down a "probably unassigned" port
- let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
- let addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap().into();
- socket.bind(&addr).unwrap();
- let addr = socket.local_addr().unwrap();
- drop(socket);
-
- let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
- match socket.connect_timeout(&addr, Duration::from_millis(250)) {
- Ok(_) => panic!("unexpected success"),
- Err(ref e)
- if e.kind() == io::ErrorKind::ConnectionRefused
- || e.kind() == io::ErrorKind::TimedOut => {}
- Err(e) => panic!("unexpected error {}", e),
- }
- }
-
- #[test]
- fn connect_timeout_valid() {
- let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
- socket
- .bind(&"127.0.0.1:0".parse::<SocketAddr>().unwrap().into())
- .unwrap();
- socket.listen(128).unwrap();
-
- let addr = socket.local_addr().unwrap();
-
- let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
- socket
- .connect_timeout(&addr, Duration::from_millis(250))
- .unwrap();
- }
-
- #[test]
- #[cfg(all(unix, feature = "pair", feature = "unix"))]
- fn pair() {
- let (mut a, mut b) = Socket::pair(Domain::unix(), Type::stream(), None).unwrap();
- a.write_all(b"hello world").unwrap();
- let mut buf = [0; 11];
- b.read_exact(&mut buf).unwrap();
- assert_eq!(buf, &b"hello world"[..]);
- }
-
- #[test]
- #[cfg(all(unix, feature = "unix"))]
- fn unix() {
- use tempdir::TempDir;
-
- let dir = TempDir::new("unix").unwrap();
- let addr = SockAddr::unix(dir.path().join("sock")).unwrap();
-
- let listener = Socket::new(Domain::unix(), Type::stream(), None).unwrap();
- listener.bind(&addr).unwrap();
- listener.listen(10).unwrap();
-
- let mut a = Socket::new(Domain::unix(), Type::stream(), None).unwrap();
- a.connect(&addr).unwrap();
-
- let mut b = listener.accept().unwrap().0;
-
- a.write_all(b"hello world").unwrap();
- let mut buf = [0; 11];
- b.read_exact(&mut buf).unwrap();
- assert_eq!(buf, &b"hello world"[..]);
- }
-
- #[test]
- fn keepalive() {
- let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
- socket.set_keepalive(Some(Duration::from_secs(7))).unwrap();
- // socket.keepalive() doesn't work on Windows #24
- #[cfg(unix)]
- assert_eq!(socket.keepalive().unwrap(), Some(Duration::from_secs(7)));
- socket.set_keepalive(None).unwrap();
- #[cfg(unix)]
- assert_eq!(socket.keepalive().unwrap(), None);
- }
-
- #[test]
- fn nodelay() {
- let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
-
- assert!(socket.set_nodelay(true).is_ok());
-
- let result = socket.nodelay();
-
- assert!(result.is_ok());
- assert!(result.unwrap());
+impl fmt::Debug for Socket {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.inner.fmt(f)
}
}
diff --git a/src/sys/unix.rs b/src/sys/unix.rs
index 3612f77..3cc08f6 100644
--- a/src/sys/unix.rs
+++ b/src/sys/unix.rs
@@ -8,23 +8,13 @@
// 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::{ErrorKind, Read, Write};
-use std::mem;
+use std::mem::{self, size_of, MaybeUninit};
use std::net::Shutdown;
-use std::net::{self, Ipv4Addr, Ipv6Addr};
-use std::ops::Neg;
-#[cfg(feature = "unix")]
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
-use std::os::unix::prelude::*;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::time::{Duration, Instant};
-use libc::{self, c_void, socklen_t, ssize_t};
-
-use crate::{Domain, Type};
+use crate::{Domain, Protocol, SockAddr, Socket, Type};
// Used in conversions for `Domain`, `Type` and `Protocol`.
#[allow(non_camel_case_types)]
@@ -36,43 +26,8 @@ pub(crate) use libc::{AF_INET, AF_INET6};
pub(crate) use libc::{SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET, SOCK_STREAM};
// Used in `Protocol`.
pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
-
-cfg_if::cfg_if! {
- if #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
- target_os = "ios", target_os = "macos",
- target_os = "openbsd", target_os = "netbsd",
- target_os = "solaris", target_os = "haiku"))] {
- use libc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
- use libc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
- } else {
- use libc::IPV6_ADD_MEMBERSHIP;
- use libc::IPV6_DROP_MEMBERSHIP;
- }
-}
-
-cfg_if::cfg_if! {
- if #[cfg(any(target_os = "linux", target_os = "android",
- target_os = "dragonfly", target_os = "freebsd",
- target_os = "openbsd", target_os = "netbsd",
- target_os = "haiku", target_os = "bitrig"))] {
- use libc::MSG_NOSIGNAL;
- } else {
- const MSG_NOSIGNAL: c_int = 0x0;
- }
-}
-
-cfg_if::cfg_if! {
- if #[cfg(any(target_os = "macos", target_os = "ios"))] {
- use libc::TCP_KEEPALIVE as KEEPALIVE_OPTION;
- } else if #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))] {
- use libc::SO_KEEPALIVE as KEEPALIVE_OPTION;
- } else {
- use libc::TCP_KEEPIDLE as KEEPALIVE_OPTION;
- }
-}
-
-use crate::utils::One;
-use crate::SockAddr;
+// Used in `Socket`.
+pub(crate) use std::os::unix::io::RawFd as RawSocket;
/// Unix only API.
impl Domain {
@@ -131,1036 +86,241 @@ impl Type {
}
}
-pub struct Socket {
- fd: c_int,
+/// Helper macro to execute a system call that returns an `io::Result`.
+macro_rules! syscall {
+ ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
+ let res = unsafe { libc::$fn($($arg, )*) };
+ if res == -1 {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(res)
+ }
+ }};
+}
+
+pub(crate) fn socket(domain: c_int, type_: c_int, protocol: c_int) -> io::Result<Socket> {
+ syscall!(socket(domain, type_, protocol)).map(|fd| Socket { inner: fd })
+}
+
+pub(crate) fn connect(
+ sockfd: RawSocket,
+ addr: *const libc::sockaddr_storage,
+ addrlen: libc::socklen_t,
+) -> io::Result<()> {
+ // Most OSes don't actually use `sockaddr_storage` in the `connect(2)` call,
+ // but `sockaddr_storage` can be converted safely into the correct type.
+ syscall!(connect(sockfd, addr as *const _, addrlen)).map(|_| ())
+}
+
+pub(crate) fn bind(
+ sockfd: RawSocket,
+ addr: *const libc::sockaddr_storage,
+ addrlen: libc::socklen_t,
+) -> io::Result<()> {
+ // Most OSes don't actually use `sockaddr_storage` in the `bind(2)` call,
+ // but `sockaddr_storage` can be converted safely into the correct type.
+ syscall!(bind(sockfd, addr as *const _, addrlen)).map(|_| ())
+}
+
+pub(crate) fn listen(sockfd: RawSocket, backlog: c_int) -> io::Result<()> {
+ syscall!(listen(sockfd, backlog)).map(|_| ())
+}
+
+pub(crate) fn accept(sockfd: RawSocket) -> io::Result<(Socket, SockAddr)> {
+ let mut addr: MaybeUninit<libc::sockaddr_storage> = MaybeUninit::uninit();
+ let mut addrlen = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
+ syscall!(accept(sockfd, addr.as_mut_ptr() as *mut _, &mut addrlen)).map(|stream_fd| {
+ // This is safe because `accept(2)` filled in the address for us.
+ let addr = unsafe { SockAddr::from_raw_parts(addr.assume_init(), addrlen) };
+ (Socket { inner: stream_fd }, addr)
+ })
+}
+
+pub(crate) fn getsockname(sockfd: RawSocket) -> io::Result<SockAddr> {
+ let mut addr: MaybeUninit<libc::sockaddr_storage> = MaybeUninit::uninit();
+ let mut addrlen = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
+ syscall!(getsockname(
+ sockfd,
+ addr.as_mut_ptr() as *mut _,
+ &mut addrlen
+ ))
+ .map(|_| {
+ // This is safe because `getsockname(2)` filled in the address for us.
+ unsafe { SockAddr::from_raw_parts(addr.assume_init(), addrlen) }
+ })
+}
+
+pub(crate) fn getpeername(sockfd: RawSocket) -> io::Result<SockAddr> {
+ let mut addr: MaybeUninit<libc::sockaddr_storage> = MaybeUninit::uninit();
+ let mut addrlen = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
+ syscall!(getpeername(
+ sockfd,
+ addr.as_mut_ptr() as *mut _,
+ &mut addrlen
+ ))
+ .map(|_| {
+ // This is safe because `getpeername(2)` filled in the address for us.
+ unsafe { SockAddr::from_raw_parts(addr.assume_init(), addrlen) }
+ })
+}
+
+pub(crate) fn shutdown(sockfd: RawSocket, how: Shutdown) -> io::Result<()> {
+ let how = match how {
+ Shutdown::Write => libc::SHUT_WR,
+ Shutdown::Read => libc::SHUT_RD,
+ Shutdown::Both => libc::SHUT_RDWR,
+ };
+ syscall!(shutdown(sockfd, how)).map(|_| ())
+}
+
+pub(crate) fn setsockopt<T>(
+ sockfd: RawSocket,
+ level: c_int,
+ optname: c_int,
+ opt: &T,
+) -> io::Result<()> {
+ syscall!(setsockopt(
+ sockfd,
+ level,
+ optname,
+ opt as *const _ as *const _,
+ size_of::<T>() as libc::socklen_t,
+ ))
+ .map(|_| ())
+}
+
+pub(crate) fn getsockopt<T>(sockfd: RawSocket, level: c_int, optname: c_int) -> io::Result<T> {
+ let mut optval: MaybeUninit<T> = MaybeUninit::uninit();
+ let mut optlen = size_of::<T>() as libc::socklen_t;
+ syscall!(getsockopt(
+ sockfd,
+ level,
+ optname,
+ optval.as_mut_ptr() as *mut _,
+ &mut optlen
+ ))
+ .map(|_| unsafe {
+ // Safe because `getsockopt(2)` initialised the value for us.
+ debug_assert_eq!(optlen as usize, size_of::<T>());
+ optval.assume_init()
+ })
+}
+
+pub(crate) fn fcntl<T>(sockfd: RawSocket, cmd: c_int, arg: T) -> io::Result<c_int> {
+ syscall!(fcntl(sockfd, cmd, arg))
}
+/// Unix only API.
impl Socket {
- pub fn new(family: c_int, ty: c_int, protocol: c_int) -> io::Result<Socket> {
- unsafe {
- // On linux we first attempt to pass the SOCK_CLOEXEC flag to
- // atomically create the socket and set it as CLOEXEC. Support for
- // this option, however, was added in 2.6.27, and we still support
- // 2.6.18 as a kernel, so if the returned error is EINVAL we
- // fallthrough to the fallback.
- #[cfg(target_os = "linux")]
- {
- match cvt(libc::socket(family, ty | libc::SOCK_CLOEXEC, protocol)) {
- Ok(fd) => return Ok(Socket::from_raw_fd(fd)),
- Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
- Err(e) => return Err(e),
- }
- }
-
- let fd = cvt(libc::socket(family, ty, protocol))?;
- let fd = Socket::from_raw_fd(fd);
- set_cloexec(fd.as_raw_fd())?;
- #[cfg(target_os = "macos")]
- {
- fd.setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?;
- }
- Ok(fd)
- }
- }
-
- pub fn pair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<(Socket, Socket)> {
- unsafe {
- let mut fds = [0, 0];
- cvt(libc::socketpair(family, ty, protocol, fds.as_mut_ptr()))?;
- let fds = (Socket::from_raw_fd(fds[0]), Socket::from_raw_fd(fds[1]));
- set_cloexec(fds.0.as_raw_fd())?;
- set_cloexec(fds.1.as_raw_fd())?;
- #[cfg(target_os = "macos")]
- {
- fds.0
- .setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?;
- fds.1
- .setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?;
- }
- Ok(fds)
- }
- }
-
- pub fn bind(&self, addr: &SockAddr) -> io::Result<()> {
- unsafe { cvt(libc::bind(self.fd, addr.as_ptr(), addr.len() as _)).map(|_| ()) }
- }
-
- pub fn listen(&self, backlog: i32) -> io::Result<()> {
- unsafe { cvt(libc::listen(self.fd, backlog)).map(|_| ()) }
- }
-
- pub fn connect(&self, addr: &SockAddr) -> io::Result<()> {
- unsafe { cvt(libc::connect(self.fd, addr.as_ptr(), addr.len())).map(|_| ()) }
- }
-
- pub fn connect_timeout(&self, addr: &SockAddr, timeout: Duration) -> io::Result<()> {
- self.set_nonblocking(true)?;
- let r = self.connect(addr);
- self.set_nonblocking(false)?;
-
- match r {
- Ok(()) => return Ok(()),
- // there's no io::ErrorKind conversion registered for EINPROGRESS :(
- Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
- Err(e) => return Err(e),
- }
-
- let mut pollfd = libc::pollfd {
- fd: self.fd,
- events: libc::POLLOUT,
- revents: 0,
- };
-
- if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- "cannot set a 0 duration timeout",
- ));
- }
-
- let start = Instant::now();
-
- loop {
- let elapsed = start.elapsed();
- if elapsed >= timeout {
- return Err(io::Error::new(
- io::ErrorKind::TimedOut,
- "connection timed out",
- ));
- }
-
- let timeout = timeout - elapsed;
- let mut timeout = timeout
- .as_secs()
- .saturating_mul(1_000)
- .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
- if timeout == 0 {
- timeout = 1;
- }
-
- let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int;
-
- match unsafe { libc::poll(&mut pollfd, 1, timeout) } {
- -1 => {
- let err = io::Error::last_os_error();
- if err.kind() != io::ErrorKind::Interrupted {
- return Err(err);
- }
- }
- 0 => {
- return Err(io::Error::new(
- io::ErrorKind::TimedOut,
- "connection timed out",
- ))
- }
- _ => {
- // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
- // for POLLHUP rather than read readiness
- if pollfd.revents & libc::POLLHUP != 0 {
- let e = self.take_error()?.unwrap_or_else(|| {
- io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP")
- });
- return Err(e);
- }
- return Ok(());
- }
- }
- }
- }
-
- pub fn local_addr(&self) -> io::Result<SockAddr> {
- unsafe {
- let mut storage: libc::sockaddr_storage = mem::zeroed();
- let mut len = mem::size_of_val(&storage) as libc::socklen_t;
- cvt(libc::getsockname(
- self.fd,
- &mut storage as *mut _ as *mut _,
- &mut len,
- ))?;
- Ok(SockAddr::from_raw_parts(
- &storage as *const _ as *const _,
- len,
- ))
- }
- }
-
- pub fn peer_addr(&self) -> io::Result<SockAddr> {
- unsafe {
- let mut storage: libc::sockaddr_storage = mem::zeroed();
- let mut len = mem::size_of_val(&storage) as libc::socklen_t;
- cvt(libc::getpeername(
- self.fd,
- &mut storage as *mut _ as *mut _,
- &mut len,
- ))?;
- Ok(SockAddr::from_raw_parts(
- &storage as *const _ as *const _,
- len,
- ))
- }
- }
-
- pub fn try_clone(&self) -> io::Result<Socket> {
- // implementation lifted from libstd
- #[cfg(any(target_os = "android", target_os = "haiku"))]
- use libc::F_DUPFD as F_DUPFD_CLOEXEC;
- #[cfg(not(any(target_os = "android", target_os = "haiku")))]
- use libc::F_DUPFD_CLOEXEC;
-
- static CLOEXEC_FAILED: AtomicBool = AtomicBool::new(false);
- unsafe {
- if !CLOEXEC_FAILED.load(Ordering::Relaxed) {
- match cvt(libc::fcntl(self.fd, F_DUPFD_CLOEXEC, 0)) {
- Ok(fd) => {
- let fd = Socket::from_raw_fd(fd);
- if cfg!(target_os = "linux") {
- set_cloexec(fd.as_raw_fd())?;
- }
- return Ok(fd);
- }
- Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
- CLOEXEC_FAILED.store(true, Ordering::Relaxed);
- }
- Err(e) => return Err(e),
- }
- }
- let fd = cvt(libc::fcntl(self.fd, libc::F_DUPFD, 0))?;
- let fd = Socket::from_raw_fd(fd);
- set_cloexec(fd.as_raw_fd())?;
- Ok(fd)
- }
- }
-
- #[allow(unused_mut)]
- pub fn accept(&self) -> io::Result<(Socket, SockAddr)> {
- let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
- let mut len = mem::size_of_val(&storage) as socklen_t;
-
- let mut socket = None;
- #[cfg(target_os = "linux")]
- {
- let res = cvt_r(|| unsafe {
- libc::syscall(
- libc::SYS_accept4,
- self.fd as libc::c_long,
- &mut storage as *mut _ as libc::c_long,
- &mut len,
- libc::SOCK_CLOEXEC as libc::c_long,
- ) as libc::c_int
- });
- match res {
- Ok(fd) => socket = Some(Socket { fd: fd }),
- Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
- Err(e) => return Err(e),
- }
- }
-
- let socket = match socket {
- Some(socket) => socket,
- None => unsafe {
- let fd =
- cvt_r(|| libc::accept(self.fd, &mut storage as *mut _ as *mut _, &mut len))?;
- let fd = Socket::from_raw_fd(fd);
- set_cloexec(fd.as_raw_fd())?;
- fd
- },
- };
- let addr = unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, len) };
- Ok((socket, addr))
- }
-
- pub fn take_error(&self) -> io::Result<Option<io::Error>> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_ERROR)?;
- if raw == 0 {
- Ok(None)
- } else {
- Ok(Some(io::Error::from_raw_os_error(raw as i32)))
- }
- }
- }
-
- pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
- unsafe {
- let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
- let new = if nonblocking {
- previous | libc::O_NONBLOCK
- } else {
- previous & !libc::O_NONBLOCK
- };
- if new != previous {
- cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
- }
- Ok(())
- }
- }
-
- pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
- let how = match how {
- Shutdown::Write => libc::SHUT_WR,
- Shutdown::Read => libc::SHUT_RD,
- Shutdown::Both => libc::SHUT_RDWR,
- };
- cvt(unsafe { libc::shutdown(self.fd, how) })?;
- Ok(())
- }
-
- pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
- unsafe {
- let n = cvt({
- libc::recv(
- self.fd,
- buf.as_mut_ptr() as *mut c_void,
- cmp::min(buf.len(), max_len()),
- 0,
- )
- })?;
- Ok(n as usize)
- }
- }
-
- pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
- unsafe {
- let n = cvt({
- libc::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 recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SockAddr)> {
- self.recvfrom(buf, 0)
- }
-
- pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SockAddr)> {
- self.recvfrom(buf, libc::MSG_PEEK)
- }
-
- fn recvfrom(&self, buf: &mut [u8], flags: c_int) -> io::Result<(usize, SockAddr)> {
- unsafe {
- let mut storage: libc::sockaddr_storage = mem::zeroed();
- let mut addrlen = mem::size_of_val(&storage) as socklen_t;
-
- let n = cvt({
- libc::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 = SockAddr::from_raw_parts(&storage as *const _ as *const _, addrlen);
- Ok((n as usize, addr))
- }
- }
-
- pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
- unsafe {
- let n = cvt({
- libc::send(
- self.fd,
- buf.as_ptr() as *const c_void,
- cmp::min(buf.len(), max_len()),
- MSG_NOSIGNAL,
- )
- })?;
- Ok(n as usize)
- }
- }
-
- pub fn send_to(&self, buf: &[u8], addr: &SockAddr) -> io::Result<usize> {
- unsafe {
- let n = cvt({
- libc::sendto(
- self.fd,
- buf.as_ptr() as *const c_void,
- cmp::min(buf.len(), max_len()),
- MSG_NOSIGNAL,
- addr.as_ptr(),
- addr.len(),
- )
- })?;
- Ok(n as usize)
- }
- }
-
- // ================================================
-
- pub fn ttl(&self) -> io::Result<u32> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IP, libc::IP_TTL)?;
- Ok(raw as u32)
- }
- }
-
- pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
- unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_TTL, ttl as c_int) }
- }
-
- pub fn unicast_hops_v6(&self) -> io::Result<u32> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS)?;
- Ok(raw as u32)
- }
- }
-
- pub fn set_unicast_hops_v6(&self, hops: u32) -> io::Result<()> {
- unsafe {
- self.setsockopt(
- libc::IPPROTO_IPV6 as c_int,
- libc::IPV6_UNICAST_HOPS,
- hops as c_int,
- )
- }
- }
-
- pub fn only_v6(&self) -> io::Result<bool> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_V6ONLY)?;
- Ok(raw != 0)
- }
- }
-
- pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
- unsafe { self.setsockopt(libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, only_v6 as c_int) }
- }
-
- pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
- unsafe {
- Ok(timeval2dur(
- self.getsockopt(libc::SOL_SOCKET, libc::SO_RCVTIMEO)?,
- ))
- }
- }
-
- pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
- unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_RCVTIMEO, dur2timeval(dur)?) }
- }
-
- pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
- unsafe {
- Ok(timeval2dur(
- self.getsockopt(libc::SOL_SOCKET, libc::SO_SNDTIMEO)?,
- ))
- }
- }
-
- pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
- unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_SNDTIMEO, dur2timeval(dur)?) }
- }
-
- pub fn nodelay(&self) -> io::Result<bool> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_TCP, libc::TCP_NODELAY)?;
- Ok(raw != 0)
- }
- }
-
- pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
- unsafe { self.setsockopt(libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) }
- }
-
- pub fn broadcast(&self) -> io::Result<bool> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_BROADCAST)?;
- Ok(raw != 0)
- }
- }
-
- pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> {
- unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_BROADCAST, broadcast as c_int) }
- }
-
- pub fn multicast_loop_v4(&self) -> io::Result<bool> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP)?;
- Ok(raw != 0)
- }
- }
-
- pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> {
- unsafe {
- self.setsockopt(
- libc::IPPROTO_IP,
- libc::IP_MULTICAST_LOOP,
- multicast_loop_v4 as c_int,
- )
- }
- }
-
- pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_TTL)?;
- Ok(raw as u32)
- }
- }
-
- pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> {
- unsafe {
- self.setsockopt(
- libc::IPPROTO_IP,
- libc::IP_MULTICAST_TTL,
- multicast_ttl_v4 as c_int,
- )
- }
- }
-
- pub fn multicast_hops_v6(&self) -> io::Result<u32> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_HOPS)?;
- Ok(raw as u32)
- }
- }
-
- pub fn set_multicast_hops_v6(&self, hops: u32) -> io::Result<()> {
- unsafe { self.setsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_HOPS, hops as c_int) }
- }
-
- pub fn multicast_if_v4(&self) -> io::Result<Ipv4Addr> {
- unsafe {
- let imr_interface: libc::in_addr =
- self.getsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_IF)?;
- Ok(from_s_addr(imr_interface.s_addr))
- }
- }
-
- pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> {
- let interface = to_s_addr(interface);
- let imr_interface = libc::in_addr { s_addr: interface };
-
- unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_IF, imr_interface) }
- }
-
- pub fn multicast_if_v6(&self) -> io::Result<u32> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_IF)?;
- Ok(raw as u32)
- }
- }
-
- pub fn set_multicast_if_v6(&self, interface: u32) -> io::Result<()> {
- unsafe {
- self.setsockopt(
- libc::IPPROTO_IPV6,
- libc::IPV6_MULTICAST_IF,
- interface as c_int,
- )
- }
- }
-
- pub fn multicast_loop_v6(&self) -> io::Result<bool> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_MULTICAST_LOOP)?;
- Ok(raw != 0)
- }
- }
-
- pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> {
- unsafe {
- self.setsockopt(
- libc::IPPROTO_IPV6,
- libc::IPV6_MULTICAST_LOOP,
- multicast_loop_v6 as c_int,
- )
- }
- }
-
- pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
- let multiaddr = to_s_addr(multiaddr);
- let interface = to_s_addr(interface);
- let mreq = libc::ip_mreq {
- imr_multiaddr: libc::in_addr { s_addr: multiaddr },
- imr_interface: libc::in_addr { s_addr: interface },
- };
- unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, mreq) }
- }
-
- pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
- let multiaddr = to_in6_addr(multiaddr);
- let mreq = libc::ipv6_mreq {
- ipv6mr_multiaddr: multiaddr,
- ipv6mr_interface: to_ipv6mr_interface(interface),
- };
- unsafe { self.setsockopt(libc::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) }
- }
-
- pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
- let multiaddr = to_s_addr(multiaddr);
- let interface = to_s_addr(interface);
- let mreq = libc::ip_mreq {
- imr_multiaddr: libc::in_addr { s_addr: multiaddr },
- imr_interface: libc::in_addr { s_addr: interface },
- };
- unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, mreq) }
- }
-
- pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
- let multiaddr = to_in6_addr(multiaddr);
- let mreq = libc::ipv6_mreq {
- ipv6mr_multiaddr: multiaddr,
- ipv6mr_interface: to_ipv6mr_interface(interface),
- };
- unsafe { self.setsockopt(libc::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) }
- }
-
- pub fn linger(&self) -> io::Result<Option<Duration>> {
- unsafe {
- Ok(linger2dur(
- self.getsockopt(libc::SOL_SOCKET, libc::SO_LINGER)?,
- ))
- }
- }
-
- pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
- unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_LINGER, dur2linger(dur)) }
- }
-
- pub fn set_reuse_address(&self, reuse: bool) -> io::Result<()> {
- unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_REUSEADDR, reuse as c_int) }
- }
-
- pub fn reuse_address(&self) -> io::Result<bool> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_REUSEADDR)?;
- Ok(raw != 0)
- }
- }
-
- pub fn recv_buffer_size(&self) -> io::Result<usize> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_RCVBUF)?;
- Ok(raw as usize)
- }
- }
-
- pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> {
- unsafe {
- // TODO: casting usize to a c_int should be a checked cast
- self.setsockopt(libc::SOL_SOCKET, libc::SO_RCVBUF, size as c_int)
- }
- }
-
- pub fn send_buffer_size(&self) -> io::Result<usize> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_SNDBUF)?;
- Ok(raw as usize)
- }
- }
-
- pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> {
- unsafe {
- // TODO: casting usize to a c_int should be a checked cast
- self.setsockopt(libc::SOL_SOCKET, libc::SO_SNDBUF, size as c_int)
- }
- }
-
- pub fn keepalive(&self) -> io::Result<Option<Duration>> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_KEEPALIVE)?;
- if raw == 0 {
- return Ok(None);
- }
- let secs: c_int = self.getsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION)?;
- Ok(Some(Duration::new(secs as u64, 0)))
- }
- }
-
- pub fn set_keepalive(&self, keepalive: Option<Duration>) -> io::Result<()> {
- unsafe {
- self.setsockopt(
- libc::SOL_SOCKET,
- libc::SO_KEEPALIVE,
- keepalive.is_some() as c_int,
- )?;
- if let Some(dur) = keepalive {
- // TODO: checked cast here
- self.setsockopt(libc::IPPROTO_TCP, KEEPALIVE_OPTION, dur.as_secs() as c_int)?;
- }
- Ok(())
- }
- }
-
- #[cfg(all(unix, not(target_os = "solaris")))]
- pub fn reuse_port(&self) -> io::Result<bool> {
- unsafe {
- let raw: c_int = self.getsockopt(libc::SOL_SOCKET, libc::SO_REUSEPORT)?;
- Ok(raw != 0)
- }
- }
-
- #[cfg(all(unix, not(target_os = "solaris")))]
- pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> {
- unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_REUSEPORT, reuse as c_int) }
- }
-
- unsafe fn setsockopt<T>(&self, opt: c_int, val: c_int, payload: T) -> io::Result<()>
- where
- T: Copy,
- {
- let payload = &payload as *const T as *const c_void;
- cvt(libc::setsockopt(
- self.fd,
- opt,
- val,
- payload,
- mem::size_of::<T>() as libc::socklen_t,
- ))?;
- Ok(())
- }
-
- unsafe fn getsockopt<T: Copy>(&self, opt: c_int, val: c_int) -> io::Result<T> {
- let mut slot: T = mem::zeroed();
- let mut len = mem::size_of::<T>() as libc::socklen_t;
- cvt(libc::getsockopt(
- self.fd,
- opt,
- val,
- &mut slot as *mut _ as *mut _,
- &mut len,
- ))?;
- assert_eq!(len as usize, mem::size_of::<T>());
- Ok(slot)
- }
-}
-
-impl Read for Socket {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- <&Socket>::read(&mut &*self, buf)
- }
-}
-
-impl<'a> Read for &'a Socket {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- unsafe {
- let n = cvt({
- libc::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<usize> {
- <&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<usize> {
- self.send(buf)
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl fmt::Debug for Socket {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut f = f.debug_struct("Socket");
- f.field("fd", &self.fd);
- if let Ok(addr) = self.local_addr() {
- f.field("local_addr", &addr);
- }
- if let Ok(addr) = self.peer_addr() {
- f.field("peer_addr", &addr);
- }
- f.finish()
- }
-}
-
-impl AsRawFd for Socket {
- fn as_raw_fd(&self) -> c_int {
- self.fd
- }
-}
-
-impl IntoRawFd for Socket {
- fn into_raw_fd(self) -> c_int {
- let fd = self.fd;
- mem::forget(self);
- return fd;
- }
-}
-
-impl FromRawFd for Socket {
- unsafe fn from_raw_fd(fd: c_int) -> Socket {
- Socket { fd: fd }
- }
-}
-
-impl AsRawFd for crate::Socket {
- fn as_raw_fd(&self) -> c_int {
- self.inner.as_raw_fd()
- }
-}
-
-impl IntoRawFd for crate::Socket {
- fn into_raw_fd(self) -> c_int {
- self.inner.into_raw_fd()
- }
-}
-
-impl FromRawFd for crate::Socket {
- unsafe fn from_raw_fd(fd: c_int) -> crate::Socket {
- crate::Socket {
- inner: Socket::from_raw_fd(fd),
- }
- }
-}
-
-impl Drop for Socket {
- fn drop(&mut self) {
- unsafe {
- let _ = libc::close(self.fd);
- }
- }
-}
-
-impl From<Socket> for net::TcpStream {
- fn from(socket: Socket) -> net::TcpStream {
- unsafe { net::TcpStream::from_raw_fd(socket.into_raw_fd()) }
- }
-}
-
-impl From<Socket> for net::TcpListener {
- fn from(socket: Socket) -> net::TcpListener {
- unsafe { net::TcpListener::from_raw_fd(socket.into_raw_fd()) }
- }
-}
-
-impl From<Socket> for net::UdpSocket {
- fn from(socket: Socket) -> net::UdpSocket {
- unsafe { net::UdpSocket::from_raw_fd(socket.into_raw_fd()) }
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<Socket> for UnixStream {
- fn from(socket: Socket) -> UnixStream {
- unsafe { UnixStream::from_raw_fd(socket.into_raw_fd()) }
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<Socket> for UnixListener {
- fn from(socket: Socket) -> UnixListener {
- unsafe { UnixListener::from_raw_fd(socket.into_raw_fd()) }
- }
-}
-
-#[cfg(all(unix, feature = "unix"))]
-impl From<Socket> for UnixDatagram {
- fn from(socket: Socket) -> UnixDatagram {
- unsafe { UnixDatagram::from_raw_fd(socket.into_raw_fd()) }
- }
-}
-
-impl From<net::TcpStream> for Socket {
- fn from(socket: net::TcpStream) -> Socket {
- unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
+ /// Creates a pair of sockets which are connected to each other.
+ ///
+ /// This function corresponds to `socketpair(2)`.
+ pub fn pair(
+ domain: Domain,
+ type_: Type,
+ protocol: Option<Protocol>,
+ ) -> io::Result<(Socket, Socket)> {
+ let mut fds = [0, 0];
+ let protocol = protocol.map(|p| p.0).unwrap_or(0);
+ syscall!(socketpair(domain.0, type_.0, protocol, fds.as_mut_ptr()))
+ .map(|_| (Socket { inner: fds[0] }, Socket { inner: fds[1] }))
+ }
+
+ /// Accept a new incoming connection from this listener.
+ ///
+ /// This function directly corresponds to the `accept4(2)` function.
+ ///
+ /// # Notes
+ ///
+ /// This only available on Android, DragonFlyBSD, FreeBSD, Linux and
+ /// OpenBSD. Once https://github.com/rust-lang/libc/issues/1636 is fixed
+ /// NetBSD will also support it.
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ // NetBSD 8.0 actually has `accept4(2)`, but libc doesn't expose it
+ // (yet). See https://github.com/rust-lang/libc/issues/1636.
+ //target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ pub fn accept4(&self, flags: c_int) -> io::Result<(Socket, SockAddr)> {
+ let mut addr: MaybeUninit<libc::sockaddr_storage> = MaybeUninit::uninit();
+ let mut addrlen = size_of::<libc::sockaddr_storage>() as libc::socklen_t;
+ syscall!(accept4(
+ self.inner,
+ addr.as_mut_ptr() as *mut _,
+ &mut addrlen,
+ flags
+ ))
+ .map(|stream_fd| {
+ // This is safe because `accept(2)` filled in the address for us.
+ let addr = unsafe { SockAddr::from_raw_parts(addr.assume_init(), addrlen) };
+ (Socket { inner: stream_fd }, addr)
+ })
}
}
-impl From<net::TcpListener> for Socket {
- fn from(socket: net::TcpListener) -> Socket {
+impl From<UnixStream> for Socket {
+ fn from(socket: UnixStream) -> Socket {
unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
}
}
-impl From<net::UdpSocket> for Socket {
- fn from(socket: net::UdpSocket) -> Socket {
- unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
+impl Into<UnixStream> for Socket {
+ fn into(self) -> UnixStream {
+ unsafe { UnixStream::from_raw_fd(self.into_raw_fd()) }
}
}
-#[cfg(all(unix, feature = "unix"))]
-impl From<UnixStream> for Socket {
- fn from(socket: UnixStream) -> Socket {
+impl From<UnixListener> for Socket {
+ fn from(socket: UnixListener) -> Socket {
unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
}
}
-#[cfg(all(unix, feature = "unix"))]
-impl From<UnixListener> for Socket {
- fn from(socket: UnixListener) -> Socket {
- unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
+impl Into<UnixListener> for Socket {
+ fn into(self) -> UnixListener {
+ unsafe { UnixListener::from_raw_fd(self.into_raw_fd()) }
}
}
-#[cfg(all(unix, feature = "unix"))]
impl From<UnixDatagram> for Socket {
fn from(socket: UnixDatagram) -> Socket {
unsafe { Socket::from_raw_fd(socket.into_raw_fd()) }
}
}
-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") {
- <c_int>::max_value() as usize - 1
- } else {
- <ssize_t>::max_value() as usize
- }
-}
-
-fn cvt<T: One + PartialEq + Neg<Output = T>>(t: T) -> io::Result<T> {
- let one: T = T::one();
- if t == -one {
- Err(io::Error::last_os_error())
- } else {
- Ok(t)
+impl Into<UnixDatagram> for Socket {
+ fn into(self) -> UnixDatagram {
+ unsafe { UnixDatagram::from_raw_fd(self.into_raw_fd()) }
}
}
-fn cvt_r<F, T>(mut f: F) -> io::Result<T>
-where
- F: FnMut() -> T,
- T: One + PartialEq + Neg<Output = T>,
-{
- loop {
- match cvt(f()) {
- Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
- other => return other,
- }
- }
-}
-
-fn set_cloexec(fd: c_int) -> io::Result<()> {
- unsafe {
- let previous = cvt(libc::fcntl(fd, libc::F_GETFD))?;
- let new = previous | libc::FD_CLOEXEC;
- if new != previous {
- cvt(libc::fcntl(fd, libc::F_SETFD, new))?;
- }
- Ok(())
- }
-}
-
-fn dur2timeval(dur: Option<Duration>) -> io::Result<libc::timeval> {
- match dur {
- Some(dur) => {
- if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- "cannot set a 0 duration timeout",
- ));
- }
-
- let secs = if dur.as_secs() > libc::time_t::max_value() as u64 {
- libc::time_t::max_value()
- } else {
- dur.as_secs() as libc::time_t
- };
- let mut timeout = libc::timeval {
- tv_sec: secs,
- tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t,
- };
- if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
- timeout.tv_usec = 1;
- }
- Ok(timeout)
- }
- None => Ok(libc::timeval {
- tv_sec: 0,
- tv_usec: 0,
- }),
+impl FromRawFd for Socket {
+ unsafe fn from_raw_fd(fd: RawFd) -> Socket {
+ Socket { inner: fd }
}
}
-fn timeval2dur(raw: libc::timeval) -> Option<Duration> {
- if raw.tv_sec == 0 && raw.tv_usec == 0 {
- None
- } else {
- let sec = raw.tv_sec as u64;
- let nsec = (raw.tv_usec as u32) * 1000;
- Some(Duration::new(sec, nsec))
+impl AsRawFd for Socket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.inner
}
}
-fn to_s_addr(addr: &Ipv4Addr) -> libc::in_addr_t {
- let octets = addr.octets();
- crate::hton(
- ((octets[0] as libc::in_addr_t) << 24)
- | ((octets[1] as libc::in_addr_t) << 16)
- | ((octets[2] as libc::in_addr_t) << 8)
- | ((octets[3] as libc::in_addr_t) << 0),
- )
-}
-
-fn from_s_addr(in_addr: libc::in_addr_t) -> Ipv4Addr {
- let h_addr = crate::ntoh(in_addr);
-
- let a: u8 = (h_addr >> 24) as u8;
- let b: u8 = (h_addr >> 16) as u8;
- let c: u8 = (h_addr >> 8) as u8;
- let d: u8 = (h_addr >> 0) as u8;
-
- Ipv4Addr::new(a, b, c, d)
-}
-
-fn to_in6_addr(addr: &Ipv6Addr) -> libc::in6_addr {
- let mut ret: libc::in6_addr = unsafe { mem::zeroed() };
- ret.s6_addr = addr.octets();
- return ret;
-}
-
-#[cfg(target_os = "android")]
-fn to_ipv6mr_interface(value: u32) -> c_int {
- value as c_int
-}
-
-#[cfg(not(target_os = "android"))]
-fn to_ipv6mr_interface(value: u32) -> libc::c_uint {
- value as libc::c_uint
-}
-
-fn linger2dur(linger_opt: libc::linger) -> Option<Duration> {
- if linger_opt.l_onoff == 0 {
- None
- } else {
- Some(Duration::from_secs(linger_opt.l_linger as u64))
+impl IntoRawFd for Socket {
+ fn into_raw_fd(self) -> RawFd {
+ let fd = self.inner;
+ mem::forget(self);
+ fd
}
}
-fn dur2linger(dur: Option<Duration>) -> libc::linger {
- match dur {
- Some(d) => libc::linger {
- l_onoff: 1,
- l_linger: d.as_secs() as c_int,
- },
- None => libc::linger {
- l_onoff: 0,
- l_linger: 0,
- },
+impl Drop for Socket {
+ fn drop(&mut self) {
+ // Can't handle the error here, nor can we do much with it.
+ let _ = unsafe { libc::close(self.inner) };
}
}
-
-#[test]
-fn test_ip() {
- let ip = Ipv4Addr::new(127, 0, 0, 1);
- assert_eq!(ip, from_s_addr(to_s_addr(&ip)));
-}
diff --git a/tests/socket.rs b/tests/socket.rs
new file mode 100644
index 0000000..66058e1
--- /dev/null
+++ b/tests/socket.rs
@@ -0,0 +1,122 @@
+use std::net::{TcpListener, TcpStream, UdpSocket};
+
+use socket2::{Domain, Socket, Type};
+
+mod util;
+use util::any_local_ipv4_addr;
+
+#[test]
+fn from_std_tcp_stream() {
+ let listener = TcpListener::bind(any_local_ipv4_addr()).unwrap();
+ let tcp_socket = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
+ let socket: Socket = tcp_socket.into();
+ drop(socket);
+}
+
+#[test]
+fn from_std_tcp_listener() {
+ let tcp_socket = TcpListener::bind(any_local_ipv4_addr()).unwrap();
+ let socket: Socket = tcp_socket.into();
+ drop(socket);
+}
+
+#[test]
+fn from_std_udp_socket() {
+ let udp_socket = UdpSocket::bind(any_local_ipv4_addr()).unwrap();
+ let socket: Socket = udp_socket.into();
+ drop(socket);
+}
+
+#[test]
+fn into_std_tcp_stream() {
+ let socket: Socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
+ let tcp_socket: TcpStream = socket.into();
+ drop(tcp_socket);
+}
+
+#[test]
+fn into_std_tcp_listener() {
+ let socket: Socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
+ let tcp_socket: TcpListener = socket.into();
+ drop(tcp_socket);
+}
+
+#[test]
+fn into_std_udp_socket() {
+ let socket: Socket = Socket::new(Domain::ipv4(), Type::dgram(), None).unwrap();
+ let udp_socket: UdpSocket = socket.into();
+ drop(udp_socket);
+}
+
+#[test]
+fn socket_connect_tcp() {
+ let listener = TcpListener::bind(any_local_ipv4_addr()).unwrap();
+ let addr = listener.local_addr().unwrap();
+
+ let socket: TcpStream = Socket::new(Domain::ipv4(), Type::stream(), None)
+ .and_then(|socket| socket.connect(&addr.into()).map(|()| socket.into()))
+ .unwrap();
+ assert_eq!(socket.peer_addr().unwrap(), addr);
+
+ let (stream, peer_addr) = listener.accept().unwrap();
+ let socket_local_addr = socket.local_addr().unwrap();
+ assert_eq!(peer_addr, socket_local_addr);
+ assert_eq!(stream.peer_addr().unwrap(), socket_local_addr);
+}
+
+#[test]
+fn socket_bind_tcp() {
+ let socket: TcpListener = Socket::new(Domain::ipv4(), Type::stream(), None)
+ .and_then(|socket| {
+ socket
+ .bind(&any_local_ipv4_addr().into())
+ .map(|()| socket.into())
+ })
+ .unwrap();
+
+ assert!(socket.local_addr().unwrap().ip().is_loopback())
+}
+
+#[test]
+fn socket_listen_tcp() {
+ let socket: TcpListener = Socket::new(Domain::ipv4(), Type::stream(), None)
+ .and_then(|socket| {
+ socket.bind(&any_local_ipv4_addr().into())?;
+ socket.listen(1024)?;
+ Ok(socket.into())
+ })
+ .unwrap();
+ let addr = socket.local_addr().unwrap();
+
+ let stream = TcpStream::connect(addr).unwrap();
+ let stream_addr = stream.local_addr().unwrap();
+
+ let (accepted_stream, peer_addr) = socket.accept().unwrap();
+ assert_eq!(peer_addr, stream_addr);
+ assert_eq!(accepted_stream.peer_addr().unwrap(), stream_addr);
+}
+
+// Also tests `local_addr` and `peer_addr`.
+#[test]
+fn socket_accept_tcp() {
+ let socket: Socket = Socket::new(Domain::ipv4(), Type::stream(), None)
+ .and_then(|socket| {
+ socket.bind(&any_local_ipv4_addr().into())?;
+ socket.listen(1024)?;
+ Ok(socket.into())
+ })
+ .unwrap();
+ let addr = socket.local_addr().unwrap();
+ let addr = addr.as_std().unwrap();
+
+ let stream = TcpStream::connect(addr).unwrap();
+ let stream_addr = stream.local_addr().unwrap();
+
+ let (accepted_socket, peer_addr) = socket.accept().unwrap();
+ let peer_addr = peer_addr.as_std().unwrap();
+ assert_eq!(peer_addr, stream_addr);
+ assert_eq!(
+ accepted_socket.peer_addr().unwrap().as_std().unwrap(),
+ stream_addr
+ );
+}
diff --git a/tests/unix.rs b/tests/unix.rs
new file mode 100644
index 0000000..3af40f6
--- /dev/null
+++ b/tests/unix.rs
@@ -0,0 +1,60 @@
+//! Tests for Unix only API.
+
+#![cfg(unix)]
+
+use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
+
+use socket2::{Domain, Socket, Type};
+
+mod util;
+use util::temp_file;
+
+#[test]
+fn from_std_unix_stream() {
+ let path = temp_file("from_std_unix_stream");
+ let listener = UnixListener::bind(&path).unwrap();
+ let stream = UnixStream::connect(&path).unwrap();
+ let socket: Socket = stream.into();
+ drop(socket);
+ drop(listener);
+}
+
+#[test]
+fn from_std_unix_listener() {
+ let path = temp_file("from_std_unix_listener");
+ let listener = UnixListener::bind(&path).unwrap();
+ let socket: Socket = listener.into();
+ drop(socket);
+}
+
+#[test]
+fn from_std_unix_socket() {
+ let path = temp_file("from_std_unix_socket");
+ let datagram = UnixDatagram::bind(&path).unwrap();
+ let socket: Socket = datagram.into();
+ drop(socket);
+}
+
+#[test]
+fn into_std_unix_stream() {
+ let socket: Socket = Socket::new(Domain::unix(), Type::stream(), None).unwrap();
+ let unix_socket: UnixStream = socket.into();
+ drop(unix_socket);
+}
+
+#[test]
+fn into_std_tcp_listener() {
+ let socket: Socket = Socket::new(Domain::unix(), Type::stream(), None).unwrap();
+ let unix_socket: UnixListener = socket.into();
+ drop(unix_socket);
+}
+
+#[test]
+fn into_std_udp_socket() {
+ let socket: Socket = Socket::new(Domain::unix(), Type::dgram(), None).unwrap();
+ let unix_socket: UnixDatagram = socket.into();
+ drop(unix_socket);
+}
+
+// TODO: test accept4.
+// TODO: test pair.
diff --git a/tests/util/mod.rs b/tests/util/mod.rs
new file mode 100644
index 0000000..1744e40
--- /dev/null
+++ b/tests/util/mod.rs
@@ -0,0 +1,49 @@
+// Not all tests use all functions.
+#![allow(dead_code)]
+
+use std::net::SocketAddr;
+use std::path::PathBuf;
+use std::sync::Once;
+use std::{env, fs};
+
+/// Bind to any port on localhost.
+pub fn any_local_ipv4_addr() -> SocketAddr {
+ "127.0.0.1:0".parse().unwrap()
+}
+
+/* TODO: needed?
+/// Bind to any port on localhost, using a IPv6 address.
+pub fn any_local_ipv6_addr() -> SocketAddr {
+ "[::1]:0".parse().unwrap()
+}
+*/
+
+/// Returns a path to a temporary file using `name` as filename.
+pub fn temp_file(name: &'static str) -> PathBuf {
+ init();
+ let mut path = temp_dir();
+ path.push(name);
+ path
+}
+
+pub fn init() {
+ static INIT: Once = Once::new();
+
+ INIT.call_once(|| {
+ // Remove all temporary files from previous test runs.
+ let dir = temp_dir();
+ let _ = fs::remove_dir_all(&dir);
+ fs::create_dir_all(&dir).expect("unable to create temporary directory");
+ })
+}
+
+/// Returns the temporary directory for test files.
+///
+/// # Notes
+///
+/// `init` must be called before this.
+fn temp_dir() -> PathBuf {
+ let mut path = env::temp_dir();
+ path.push("socket_tests");
+ path
+}