use std::io; use std::mem; use std::net::SocketAddr; #[cfg(unix)] use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; use std::time::Duration; use crate::net::{TcpListener, TcpStream}; use crate::sys; /// A non-blocking TCP socket used to configure a stream or listener. /// /// The `TcpSocket` type wraps the operating-system's socket handle. This type /// is used to configure the socket before establishing a connection or start /// listening for inbound connections. /// /// The socket will be closed when the value is dropped. #[derive(Debug)] pub struct TcpSocket { sys: sys::tcp::TcpSocket, } /// Configures a socket's TCP keepalive parameters. #[derive(Debug, Default, Clone)] pub struct TcpKeepalive { pub(crate) time: Option, #[cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", target_os = "windows", ))] pub(crate) interval: Option, #[cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", ))] pub(crate) retries: Option, } impl TcpSocket { /// Create a new IPv4 TCP socket. /// /// This calls `socket(2)`. pub fn new_v4() -> io::Result { sys::tcp::new_v4_socket().map(|sys| TcpSocket { sys }) } /// Create a new IPv6 TCP socket. /// /// This calls `socket(2)`. pub fn new_v6() -> io::Result { sys::tcp::new_v6_socket().map(|sys| TcpSocket { sys }) } pub(crate) fn new_for_addr(addr: SocketAddr) -> io::Result { if addr.is_ipv4() { TcpSocket::new_v4() } else { TcpSocket::new_v6() } } /// Bind `addr` to the TCP socket. pub fn bind(&self, addr: SocketAddr) -> io::Result<()> { sys::tcp::bind(self.sys, addr) } /// Connect the socket to `addr`. /// /// This consumes the socket and performs the connect operation. Once the /// connection completes, the socket is now a non-blocking `TcpStream` and /// can be used as such. pub fn connect(self, addr: SocketAddr) -> io::Result { let stream = sys::tcp::connect(self.sys, addr)?; // Don't close the socket mem::forget(self); Ok(TcpStream::from_std(stream)) } /// Listen for inbound connections, converting the socket to a /// `TcpListener`. pub fn listen(self, backlog: u32) -> io::Result { let listener = sys::tcp::listen(self.sys, backlog)?; // Don't close the socket mem::forget(self); Ok(TcpListener::from_std(listener)) } /// Sets the value of `SO_REUSEADDR` on this socket. pub fn set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()> { sys::tcp::set_reuseaddr(self.sys, reuseaddr) } /// Get the value of `SO_REUSEADDR` set on this socket. pub fn get_reuseaddr(&self) -> io::Result { sys::tcp::get_reuseaddr(self.sys) } /// Sets the value of `SO_REUSEPORT` on this socket. /// Only supported available in unix #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> { sys::tcp::set_reuseport(self.sys, reuseport) } /// Get the value of `SO_REUSEPORT` set on this socket. /// Only supported available in unix #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] pub fn get_reuseport(&self) -> io::Result { sys::tcp::get_reuseport(self.sys) } /// Sets the value of `SO_LINGER` on this socket. pub fn set_linger(&self, dur: Option) -> io::Result<()> { sys::tcp::set_linger(self.sys, dur) } /// Gets the value of `SO_LINGER` on this socket pub fn get_linger(&self) -> io::Result> { sys::tcp::get_linger(self.sys) } /// Sets the value of `SO_RCVBUF` on this socket. pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> { sys::tcp::set_recv_buffer_size(self.sys, size) } /// Get the value of `SO_RCVBUF` set on this socket. /// /// Note that if [`set_recv_buffer_size`] has been called on this socket /// previously, the value returned by this function may not be the same as /// the argument provided to `set_recv_buffer_size`. This is for the /// following reasons: /// /// * Most operating systems have minimum and maximum allowed sizes for the /// receive buffer, and will clamp the provided value if it is below the /// minimum or above the maximum. The minimum and maximum buffer sizes are /// OS-dependent. /// * Linux will double the buffer size to account for internal bookkeeping /// data, and returns the doubled value from `getsockopt(2)`. As per `man /// 7 socket`: /// > Sets or gets the maximum socket receive buffer in bytes. The /// > kernel doubles this value (to allow space for bookkeeping /// > overhead) when it is set using `setsockopt(2)`, and this doubled /// > value is returned by `getsockopt(2)`. /// /// [`set_recv_buffer_size`]: #method.set_recv_buffer_size pub fn get_recv_buffer_size(&self) -> io::Result { sys::tcp::get_recv_buffer_size(self.sys) } /// Sets the value of `SO_SNDBUF` on this socket. pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> { sys::tcp::set_send_buffer_size(self.sys, size) } /// Get the value of `SO_SNDBUF` set on this socket. /// /// Note that if [`set_send_buffer_size`] has been called on this socket /// previously, the value returned by this function may not be the same as /// the argument provided to `set_send_buffer_size`. This is for the /// following reasons: /// /// * Most operating systems have minimum and maximum allowed sizes for the /// receive buffer, and will clamp the provided value if it is below the /// minimum or above the maximum. The minimum and maximum buffer sizes are /// OS-dependent. /// * Linux will double the buffer size to account for internal bookkeeping /// data, and returns the doubled value from `getsockopt(2)`. As per `man /// 7 socket`: /// > Sets or gets the maximum socket send buffer in bytes. The /// > kernel doubles this value (to allow space for bookkeeping /// > overhead) when it is set using `setsockopt(2)`, and this doubled /// > value is returned by `getsockopt(2)`. /// /// [`set_send_buffer_size`]: #method.set_send_buffer_size pub fn get_send_buffer_size(&self) -> io::Result { sys::tcp::get_send_buffer_size(self.sys) } /// Sets whether keepalive messages are enabled to be sent on this socket. /// /// This will set the `SO_KEEPALIVE` option on this socket. pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { sys::tcp::set_keepalive(self.sys, keepalive) } /// Returns whether or not TCP keepalive probes will be sent by this socket. pub fn get_keepalive(&self) -> io::Result { sys::tcp::get_keepalive(self.sys) } /// Sets parameters configuring TCP keepalive probes for this socket. /// /// The supported parameters depend on the operating system, and are /// configured using the [`TcpKeepalive`] struct. At a minimum, all systems /// support configuring the [keepalive time]: the time after which the OS /// will start sending keepalive messages on an idle connection. /// /// # Notes /// /// * This will enable TCP keepalive on this socket, if it is not already /// enabled. /// * On some platforms, such as Windows, any keepalive parameters *not* /// configured by the `TcpKeepalive` struct passed to this function may be /// overwritten with their default values. Therefore, this function should /// either only be called once per socket, or the same parameters should /// be passed every time it is called. /// /// # Examples #[cfg_attr(feature = "os-poll", doc = "```")] #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] /// use mio::net::{TcpSocket, TcpKeepalive}; /// use std::time::Duration; /// /// # fn main() -> Result<(), std::io::Error> { /// let socket = TcpSocket::new_v6()?; /// let keepalive = TcpKeepalive::default() /// .with_time(Duration::from_secs(4)); /// // Depending on the target operating system, we may also be able to /// // configure the keepalive probe interval and/or the number of retries /// // here as well. /// /// socket.set_keepalive_params(keepalive)?; /// # Ok(()) } /// ``` /// /// [`TcpKeepalive`]: ../struct.TcpKeepalive.html /// [keepalive time]: ../struct.TcpKeepalive.html#method.with_time pub fn set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()> { self.set_keepalive(true)?; sys::tcp::set_keepalive_params(self.sys, keepalive) } /// Returns the amount of time after which TCP keepalive probes will be sent /// on idle connections. /// /// If `None`, then keepalive messages are disabled. /// /// This returns the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD, /// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE` /// on all other Unix operating systems. On Windows, it is not possible to /// access the value of TCP keepalive parameters after they have been set. /// /// Some platforms specify this value in seconds, so sub-second /// specifications may be omitted. #[cfg_attr(docsrs, doc(cfg(not(target_os = "windows"))))] #[cfg(not(target_os = "windows"))] pub fn get_keepalive_time(&self) -> io::Result> { sys::tcp::get_keepalive_time(self.sys) } /// Returns the time interval between TCP keepalive probes, if TCP keepalive is /// enabled on this socket. /// /// If `None`, then keepalive messages are disabled. /// /// This returns the value of `TCP_KEEPINTVL` on supported Unix operating /// systems. On Windows, it is not possible to access the value of TCP /// keepalive parameters after they have been set.. /// /// Some platforms specify this value in seconds, so sub-second /// specifications may be omitted. #[cfg_attr( docsrs, doc(cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", ))) )] #[cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", ))] pub fn get_keepalive_interval(&self) -> io::Result> { sys::tcp::get_keepalive_interval(self.sys) } /// Returns the maximum number of TCP keepalive probes that will be sent before /// dropping a connection, if TCP keepalive is enabled on this socket. /// /// If `None`, then keepalive messages are disabled. /// /// This returns the value of `TCP_KEEPCNT` on Unix operating systems that /// support this option. On Windows, it is not possible to access the value /// of TCP keepalive parameters after they have been set. #[cfg_attr( docsrs, doc(cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", ))) )] #[cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", ))] pub fn get_keepalive_retries(&self) -> io::Result> { sys::tcp::get_keepalive_retries(self.sys) } /// Returns the local address of this socket /// /// Will return `Err` result in windows if called before calling `bind` pub fn get_localaddr(&self) -> io::Result { sys::tcp::get_localaddr(self.sys) } } impl Drop for TcpSocket { fn drop(&mut self) { sys::tcp::close(self.sys); } } #[cfg(unix)] impl IntoRawFd for TcpSocket { fn into_raw_fd(self) -> RawFd { let ret = self.sys; // Avoid closing the socket mem::forget(self); ret } } #[cfg(unix)] impl AsRawFd for TcpSocket { fn as_raw_fd(&self) -> RawFd { self.sys } } #[cfg(unix)] impl FromRawFd for TcpSocket { /// Converts a `RawFd` to a `TcpSocket`. /// /// # Notes /// /// The caller is responsible for ensuring that the socket is in /// non-blocking mode. unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket { TcpSocket { sys: fd } } } #[cfg(windows)] impl IntoRawSocket for TcpSocket { fn into_raw_socket(self) -> RawSocket { // The winapi crate defines `SOCKET` as `usize`. The Rust std // conditionally defines `RawSocket` as a fixed size unsigned integer // matching the pointer width. These end up being the same type but we // must cast between them. let ret = self.sys as RawSocket; // Avoid closing the socket mem::forget(self); ret } } #[cfg(windows)] impl AsRawSocket for TcpSocket { fn as_raw_socket(&self) -> RawSocket { self.sys as RawSocket } } #[cfg(windows)] impl FromRawSocket for TcpSocket { /// Converts a `RawSocket` to a `TcpSocket`. /// /// # Notes /// /// The caller is responsible for ensuring that the socket is in /// non-blocking mode. unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket { TcpSocket { sys: socket as sys::tcp::TcpSocket, } } } impl TcpKeepalive { // Sets the amount of time after which TCP keepalive probes will be sent /// on idle connections. /// /// This will set the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD, /// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE` /// on all other Unix operating systems. On Windows, this sets the value of /// the `tcp_keepalive` struct's `keepalivetime` field. /// /// Some platforms specify this value in seconds, so sub-second /// specifications may be omitted. pub fn with_time(self, time: Duration) -> Self { Self { time: Some(time), ..self } } /// Sets the time interval between TCP keepalive probes. /// This sets the value of `TCP_KEEPINTVL` on supported Unix operating /// systems. On Windows, this sets the value of the `tcp_keepalive` struct's /// `keepaliveinterval` field. /// /// Some platforms specify this value in seconds, so sub-second /// specifications may be omitted. #[cfg_attr( docsrs, doc(cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", target_os = "windows" ))) )] #[cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", target_os = "windows" ))] pub fn with_interval(self, interval: Duration) -> Self { Self { interval: Some(interval), ..self } } /// Sets the maximum number of TCP keepalive probes that will be sent before /// dropping a connection, if TCP keepalive is enabled on this socket. /// /// This will set the value of `TCP_KEEPCNT` on Unix operating systems that /// support this option. #[cfg_attr( docsrs, doc(cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", ))) )] #[cfg(any( target_os = "linux", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", ))] pub fn with_retries(self, retries: u32) -> Self { Self { retries: Some(retries), ..self } } /// Returns a new, empty set of TCP keepalive parameters. pub fn new() -> Self { Self::default() } }