mirror of
https://git.proxmox.com/git/rustc
synced 2026-03-28 03:41:08 +00:00
2624 lines
87 KiB
Diff
2624 lines
87 KiB
Diff
src/socket.rs
|
|
src/sys/unix.rs
|
|
src/sys/windows.rs
|
|
|
|
|
|
|
|
|
|
commit 9f0fbf2ed487668e4e2b5357a8e4a167e7ec903a
|
|
Author: Thomas de Zeeuw <thomasdezeeuw@gmail.com>
|
|
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
|
|
+}
|