| 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 |
| +} |