// Copyright 2015 The Rust Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::fmt; use std::io::{self, Read, Write}; #[cfg(not(target_os = "redox"))] use std::io::{IoSlice, IoSliceMut}; use std::mem::MaybeUninit; use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown}; #[cfg(unix)] use std::os::unix::io::{FromRawFd, IntoRawFd}; #[cfg(windows)] use std::os::windows::io::{FromRawSocket, IntoRawSocket}; use std::time::Duration; use crate::sys::{self, c_int, getsockopt, setsockopt, Bool}; use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type}; #[cfg(not(target_os = "redox"))] use crate::{MaybeUninitSlice, RecvFlags}; /// Owned wrapper around a 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. /// /// ## Converting to and from other types /// /// This type can be freely converted into the network primitives provided by /// the standard library, such as [`TcpStream`] or [`UdpSocket`], using the /// [`From`] trait, see the example below. /// /// [`TcpStream`]: std::net::TcpStream /// [`UdpSocket`]: std::net::UdpSocket /// /// # Notes /// /// Some methods that set options on `Socket` require two system calls to set /// there options without overwriting previously set options. We do this by /// first getting the current settings, applying the desired changes and than /// updating the settings. This means that the operation is **not** atomic. This /// can lead to a data race when two threads are changing options in parallel. /// /// # Examples /// ```no_run /// # 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)?; /// /// let address: SocketAddr = "[::1]:12345".parse().unwrap(); /// let address = address.into(); /// socket.bind(&address)?; /// socket.bind(&address)?; /// socket.listen(128)?; /// /// let listener: TcpListener = socket.into(); /// // ... /// # drop(listener); /// # Ok(()) } /// ``` pub struct Socket { inner: Inner, } /// Store a `TcpStream` internally to take advantage of its niche optimizations on Unix platforms. pub(crate) type Inner = std::net::TcpStream; impl Socket { /// # Safety /// /// The caller must ensure `raw` is a valid file descriptor/socket. NOTE: /// this should really be marked `unsafe`, but this being an internal /// function, often passed as mapping function, it's makes it very /// inconvenient to mark it as `unsafe`. pub(crate) fn from_raw(raw: sys::Socket) -> Socket { Socket { inner: unsafe { // SAFETY: the caller must ensure that `raw` is a valid file // descriptor, but when it isn't it could return I/O errors, or // potentially close a fd it doesn't own. All of that isn't // memory unsafe, so it's not desired but never memory unsafe or // causes UB. // // However there is one exception. We use `TcpStream` to // represent the `Socket` internally (see `Inner` type), // `TcpStream` has a layout optimisation that doesn't allow for // negative file descriptors (as those are always invalid). // Violating this assumption (fd never negative) causes UB, // something we don't want. So check for that we have this // `assert!`. #[cfg(unix)] assert!(raw >= 0, "tried to create a `Socket` with an invalid fd"); sys::socket_from_raw(raw) }, } } pub(crate) fn as_raw(&self) -> sys::Socket { sys::socket_as_raw(&self.inner) } pub(crate) fn into_raw(self) -> sys::Socket { sys::socket_into_raw(self.inner) } /// Creates a new socket and sets common flags. /// /// This function corresponds to `socket(2)` on Unix and `WSASocketW` on /// Windows. /// /// On Unix-like systems, the close-on-exec flag is set on the new socket. /// Additionally, on Apple platforms `SOCK_NOSIGPIPE` is set. On Windows, /// the socket is made non-inheritable. /// /// [`Socket::new_raw`] can be used if you don't want these flags to be set. pub fn new(domain: Domain, ty: Type, protocol: Option) -> io::Result { let ty = set_common_type(ty); Socket::new_raw(domain, ty, protocol).and_then(set_common_flags) } /// Creates a new socket ready to be configured. /// /// This function corresponds to `socket(2)` on Unix and `WSASocketW` on /// Windows and simply creates a new socket, no other configuration is done. pub fn new_raw(domain: Domain, ty: Type, protocol: Option) -> io::Result { let protocol = protocol.map(|p| p.0).unwrap_or(0); sys::socket(domain.0, ty.0, protocol).map(Socket::from_raw) } /// Creates a pair of sockets which are connected to each other. /// /// This function corresponds to `socketpair(2)`. /// /// This function sets the same flags as in done for [`Socket::new`], /// [`Socket::pair_raw`] can be used if you don't want to set those flags. #[cfg(any(doc, all(feature = "all", unix)))] #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))] pub fn pair( domain: Domain, ty: Type, protocol: Option, ) -> io::Result<(Socket, Socket)> { let ty = set_common_type(ty); let (a, b) = Socket::pair_raw(domain, ty, protocol)?; let a = set_common_flags(a)?; let b = set_common_flags(b)?; Ok((a, b)) } /// Creates a pair of sockets which are connected to each other. /// /// This function corresponds to `socketpair(2)`. #[cfg(any(doc, all(feature = "all", unix)))] #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))] pub fn pair_raw( domain: Domain, ty: Type, protocol: Option, ) -> io::Result<(Socket, Socket)> { let protocol = protocol.map(|p| p.0).unwrap_or(0); sys::socketpair(domain.0, ty.0, protocol) .map(|[a, b]| (Socket::from_raw(a), Socket::from_raw(b))) } /// Binds this socket to the specified address. /// /// This function directly corresponds to the `bind(2)` function on Windows /// and Unix. pub fn bind(&self, address: &SockAddr) -> io::Result<()> { sys::bind(self.as_raw(), address) } /// 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. /// /// # Notes /// /// When using a non-blocking connect (by setting the socket into /// non-blocking mode before calling this function), socket option can't be /// set *while connecting*. This will cause errors on Windows. Socket /// options can be safely set before and after connecting the socket. pub fn connect(&self, address: &SockAddr) -> io::Result<()> { sys::connect(self.as_raw(), address) } /// 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 non-blocking 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.set_nonblocking(true)?; let res = self.connect(addr); self.set_nonblocking(false)?; match res { Ok(()) => return Ok(()), Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} #[cfg(unix)] Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} Err(e) => return Err(e), } sys::poll_connect(self, timeout) } /// Mark a socket as ready to accept incoming connection requests using /// [`Socket::accept()`]. /// /// 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: c_int) -> io::Result<()> { sys::listen(self.as_raw(), backlog) } /// Accept a new incoming connection from this listener. /// /// This function uses `accept4(2)` on platforms that support it and /// `accept(2)` platforms that do not. /// /// This function sets the same flags as in done for [`Socket::new`], /// [`Socket::accept_raw`] can be used if you don't want to set those flags. pub fn accept(&self) -> io::Result<(Socket, SockAddr)> { // Use `accept4` on platforms that support it. #[cfg(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd", ))] return self._accept4(libc::SOCK_CLOEXEC); // Fall back to `accept` on platforms that do not support `accept4`. #[cfg(not(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd", )))] { let (socket, addr) = self.accept_raw()?; let socket = set_common_flags(socket)?; // `set_common_flags` does not disable inheritance on Windows because `Socket::new` // unlike `accept` is able to create the socket with inheritance disabled. #[cfg(windows)] socket._set_no_inherit(true)?; Ok((socket, addr)) } } /// Accept a new incoming connection from this listener. /// /// This function directly corresponds to the `accept(2)` function on /// Windows and Unix. pub fn accept_raw(&self) -> io::Result<(Socket, SockAddr)> { sys::accept(self.as_raw()).map(|(inner, addr)| (Socket::from_raw(inner), addr)) } /// Returns the socket address of the local half of this socket. /// /// # Notes /// /// Depending on the OS this may return an error if the socket is not /// [bound]. /// /// [bound]: Socket::bind pub fn local_addr(&self) -> io::Result { sys::getsockname(self.as_raw()) } /// Returns the socket address of the remote peer of this socket. /// /// # Notes /// /// This returns an error if the socket is not [`connect`ed]. /// /// [`connect`ed]: Socket::connect pub fn peer_addr(&self) -> io::Result { sys::getpeername(self.as_raw()) } /// Returns the [`Type`] of this socket by checking the `SO_TYPE` option on /// this socket. pub fn r#type(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_TYPE).map(Type) } } /// Creates a new independently owned handle to the underlying socket. /// /// # Notes /// /// On Unix this uses `F_DUPFD_CLOEXEC` and thus sets the `FD_CLOEXEC` on /// the returned socket. /// /// On Windows this uses `WSA_FLAG_NO_HANDLE_INHERIT` setting inheriting to /// false. /// /// On Windows this can **not** be used function cannot be used on a /// QOS-enabled socket, see /// . pub fn try_clone(&self) -> io::Result { sys::try_clone(self.as_raw()).map(Socket::from_raw) } /// Moves this TCP stream into or out of nonblocking mode. /// /// # Notes /// /// On Unix this corresponds to calling `fcntl` (un)setting `O_NONBLOCK`. /// /// On Windows this corresponds to calling `ioctlsocket` (un)setting /// `FIONBIO`. pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { sys::set_nonblocking(self.as_raw(), nonblocking) } /// Shuts down the read, write, or both halves of this connection. /// /// 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<()> { sys::shutdown(self.as_raw(), 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 might fail if the socket is not connected. /// /// [`connect`]: Socket::connect /// /// # Safety /// /// Normally casting a `&mut [u8]` to `&mut [MaybeUninit]` would be /// unsound, as that allows us to write uninitialised bytes to the buffer. /// However this implementation promises to not write uninitialised bytes to /// the `buf`fer and passes it directly to `recv(2)` system call. This /// promise ensures that this function can be called using a `buf`fer of /// type `&mut [u8]`. /// /// Note that the [`io::Read::read`] implementation calls this function with /// a `buf`fer of type `&mut [u8]`, allowing initialised buffers to be used /// without using `unsafe`. pub fn recv(&self, buf: &mut [MaybeUninit]) -> io::Result { self.recv_with_flags(buf, 0) } /// Receives out-of-band (OOB) data on the socket from the remote address to /// which it is connected by setting the `MSG_OOB` flag for this call. /// /// For more information, see [`recv`], [`out_of_band_inline`]. /// /// [`recv`]: Socket::recv /// [`out_of_band_inline`]: Socket::out_of_band_inline pub fn recv_out_of_band(&self, buf: &mut [MaybeUninit]) -> io::Result { self.recv_with_flags(buf, sys::MSG_OOB) } /// Identical to [`recv`] but allows for specification of arbitrary flags to /// the underlying `recv` call. /// /// [`recv`]: Socket::recv pub fn recv_with_flags( &self, buf: &mut [MaybeUninit], flags: sys::c_int, ) -> io::Result { sys::recv(self.as_raw(), buf, flags) } /// Receives data on the socket from the remote address to which it is /// connected. Unlike [`recv`] this allows passing multiple buffers. /// /// The [`connect`] method will connect this socket to a remote address. /// This method might fail if the socket is not connected. /// /// In addition to the number of bytes read, this function returns the flags /// for the received message. See [`RecvFlags`] for more information about /// the returned flags. /// /// [`recv`]: Socket::recv /// [`connect`]: Socket::connect /// /// # Safety /// /// Normally casting a `IoSliceMut` to `MaybeUninitSlice` would be unsound, /// as that allows us to write uninitialised bytes to the buffer. However /// this implementation promises to not write uninitialised bytes to the /// `bufs` and passes it directly to `recvmsg(2)` system call. This promise /// ensures that this function can be called using `bufs` of type `&mut /// [IoSliceMut]`. /// /// Note that the [`io::Read::read_vectored`] implementation calls this /// function with `buf`s of type `&mut [IoSliceMut]`, allowing initialised /// buffers to be used without using `unsafe`. #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn recv_vectored( &self, bufs: &mut [MaybeUninitSlice<'_>], ) -> io::Result<(usize, RecvFlags)> { self.recv_vectored_with_flags(bufs, 0) } /// Identical to [`recv_vectored`] but allows for specification of arbitrary /// flags to the underlying `recvmsg`/`WSARecv` call. /// /// [`recv_vectored`]: Socket::recv_vectored /// /// # Safety /// /// `recv_from_vectored` makes the same safety guarantees regarding `bufs` /// as [`recv_vectored`]. /// /// [`recv_vectored`]: Socket::recv_vectored #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn recv_vectored_with_flags( &self, bufs: &mut [MaybeUninitSlice<'_>], flags: c_int, ) -> io::Result<(usize, RecvFlags)> { sys::recv_vectored(self.as_raw(), bufs, flags) } /// 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. /// /// # Safety /// /// `peek` makes the same safety guarantees regarding the `buf`fer as /// [`recv`]. /// /// [`recv`]: Socket::recv pub fn peek(&self, buf: &mut [MaybeUninit]) -> io::Result { self.recv_with_flags(buf, sys::MSG_PEEK) } /// Receives data from the socket. On success, returns the number of bytes /// read and the address from whence the data came. /// /// # Safety /// /// `recv_from` makes the same safety guarantees regarding the `buf`fer as /// [`recv`]. /// /// [`recv`]: Socket::recv pub fn recv_from(&self, buf: &mut [MaybeUninit]) -> io::Result<(usize, SockAddr)> { self.recv_from_with_flags(buf, 0) } /// Identical to [`recv_from`] but allows for specification of arbitrary /// flags to the underlying `recvfrom` call. /// /// [`recv_from`]: Socket::recv_from pub fn recv_from_with_flags( &self, buf: &mut [MaybeUninit], flags: c_int, ) -> io::Result<(usize, SockAddr)> { sys::recv_from(self.as_raw(), buf, flags) } /// Receives data from the socket. Returns the amount of bytes read, the /// [`RecvFlags`] and the remote address from the data is coming. Unlike /// [`recv_from`] this allows passing multiple buffers. /// /// [`recv_from`]: Socket::recv_from /// /// # Safety /// /// `recv_from_vectored` makes the same safety guarantees regarding `bufs` /// as [`recv_vectored`]. /// /// [`recv_vectored`]: Socket::recv_vectored #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn recv_from_vectored( &self, bufs: &mut [MaybeUninitSlice<'_>], ) -> io::Result<(usize, RecvFlags, SockAddr)> { self.recv_from_vectored_with_flags(bufs, 0) } /// Identical to [`recv_from_vectored`] but allows for specification of /// arbitrary flags to the underlying `recvmsg`/`WSARecvFrom` call. /// /// [`recv_from_vectored`]: Socket::recv_from_vectored /// /// # Safety /// /// `recv_from_vectored` makes the same safety guarantees regarding `bufs` /// as [`recv_vectored`]. /// /// [`recv_vectored`]: Socket::recv_vectored #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn recv_from_vectored_with_flags( &self, bufs: &mut [MaybeUninitSlice<'_>], flags: c_int, ) -> io::Result<(usize, RecvFlags, SockAddr)> { sys::recv_from_vectored(self.as_raw(), bufs, flags) } /// 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. /// /// # Safety /// /// `peek_from` makes the same safety guarantees regarding the `buf`fer as /// [`recv`]. /// /// [`recv`]: Socket::recv pub fn peek_from(&self, buf: &mut [MaybeUninit]) -> io::Result<(usize, SockAddr)> { self.recv_from_with_flags(buf, sys::MSG_PEEK) } /// 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 { self.send_with_flags(buf, 0) } /// Identical to [`send`] but allows for specification of arbitrary flags to the underlying /// `send` call. /// /// [`send`]: #method.send pub fn send_with_flags(&self, buf: &[u8], flags: c_int) -> io::Result { sys::send(self.as_raw(), buf, flags) } /// Send data to the connected peer. Returns the amount of bytes written. #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_vectored_with_flags(bufs, 0) } /// Identical to [`send_vectored`] but allows for specification of arbitrary /// flags to the underlying `sendmsg`/`WSASend` call. /// /// [`send_vectored`]: Socket::send_vectored #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn send_vectored_with_flags( &self, bufs: &[IoSlice<'_>], flags: c_int, ) -> io::Result { sys::send_vectored(self.as_raw(), bufs, flags) } /// Sends out-of-band (OOB) data on the socket to connected peer /// by setting the `MSG_OOB` flag for this call. /// /// For more information, see [`send`], [`out_of_band_inline`]. /// /// [`send`]: #method.send /// [`out_of_band_inline`]: #method.out_of_band_inline pub fn send_out_of_band(&self, buf: &[u8]) -> io::Result { self.send_with_flags(buf, sys::MSG_OOB) } /// Sends data on the socket to the given address. On success, returns the /// number of bytes written. /// /// This is typically used on UDP or datagram-oriented sockets. pub fn send_to(&self, buf: &[u8], addr: &SockAddr) -> io::Result { self.send_to_with_flags(buf, addr, 0) } /// Identical to [`send_to`] but allows for specification of arbitrary flags /// to the underlying `sendto` call. /// /// [`send_to`]: Socket::send_to pub fn send_to_with_flags( &self, buf: &[u8], addr: &SockAddr, flags: c_int, ) -> io::Result { sys::send_to(self.as_raw(), buf, addr, flags) } /// Send data to a peer listening on `addr`. Returns the amount of bytes /// written. #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn send_to_vectored(&self, bufs: &[IoSlice<'_>], addr: &SockAddr) -> io::Result { self.send_to_vectored_with_flags(bufs, addr, 0) } /// Identical to [`send_to_vectored`] but allows for specification of /// arbitrary flags to the underlying `sendmsg`/`WSASendTo` call. /// /// [`send_to_vectored`]: Socket::send_to_vectored #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn send_to_vectored_with_flags( &self, bufs: &[IoSlice<'_>], addr: &SockAddr, flags: c_int, ) -> io::Result { sys::send_to_vectored(self.as_raw(), bufs, addr, flags) } } /// Set `SOCK_CLOEXEC` and `NO_HANDLE_INHERIT` on the `ty`pe on platforms that /// support it. #[inline(always)] fn set_common_type(ty: Type) -> Type { // On platforms that support it set `SOCK_CLOEXEC`. #[cfg(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd", ))] let ty = ty._cloexec(); // On windows set `NO_HANDLE_INHERIT`. #[cfg(windows)] let ty = ty._no_inherit(); ty } /// Set `FD_CLOEXEC` and `NOSIGPIPE` on the `socket` for platforms that need it. #[inline(always)] #[allow(clippy::unnecessary_wraps)] fn set_common_flags(socket: Socket) -> io::Result { // On platforms that don't have `SOCK_CLOEXEC` use `FD_CLOEXEC`. #[cfg(all( unix, not(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd", )) ))] socket._set_cloexec(true)?; // On Apple platforms set `NOSIGPIPE`. #[cfg(target_vendor = "apple")] socket._set_nosigpipe(true)?; Ok(socket) } /// A local interface specified by its index or an address assigned to it. /// /// `Index(0)` and `Address(Ipv4Addr::UNSPECIFIED)` are equivalent and indicate /// that an appropriate interface should be selected by the system. #[cfg(not(any( target_os = "haiku", target_os = "illumos", target_os = "netbsd", target_os = "redox", target_os = "solaris", )))] #[derive(Debug)] pub enum InterfaceIndexOrAddress { /// An interface index. Index(u32), /// An address assigned to an interface. Address(Ipv4Addr), } /// Socket options get/set using `SOL_SOCKET`. /// /// Additional documentation can be found in documentation of the OS. /// * Linux: /// * Windows: impl Socket { /// Get the value of the `SO_BROADCAST` option for this socket. /// /// For more information about this option, see [`set_broadcast`]. /// /// [`set_broadcast`]: Socket::set_broadcast pub fn broadcast(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_BROADCAST) .map(|broadcast| broadcast != 0) } } /// Set 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 set_broadcast(&self, broadcast: bool) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::SOL_SOCKET, sys::SO_BROADCAST, broadcast as c_int, ) } } /// Get the value of the `SO_ERROR` option on this socket. /// /// This will retrieve the stored error in the underlying socket, clearing /// the field in the process. This can be useful for checking errors between /// calls. pub fn take_error(&self) -> io::Result> { match unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_ERROR) } { Ok(0) => Ok(None), Ok(errno) => Ok(Some(io::Error::from_raw_os_error(errno))), Err(err) => Err(err), } } /// Get the value of the `SO_KEEPALIVE` option on this socket. /// /// For more information about this option, see [`set_keepalive`]. /// /// [`set_keepalive`]: Socket::set_keepalive pub fn keepalive(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_KEEPALIVE) .map(|keepalive| keepalive != 0) } } /// Set value for the `SO_KEEPALIVE` option on this socket. /// /// Enable sending of keep-alive messages on connection-oriented sockets. pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::SOL_SOCKET, sys::SO_KEEPALIVE, keepalive as c_int, ) } } /// Get the value of the `SO_LINGER` option on this socket. /// /// For more information about this option, see [`set_linger`]. /// /// [`set_linger`]: Socket::set_linger pub fn linger(&self) -> io::Result> { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_LINGER) .map(from_linger) } } /// Set value for the `SO_LINGER` option on this socket. /// /// If `linger` is not `None`, a close(2) or shutdown(2) will not return /// until all queued messages for the socket have been successfully sent or /// the linger timeout has been reached. Otherwise, the call returns /// immediately and the closing is done in the background. When the socket /// is closed as part of exit(2), it always lingers in the background. /// /// # Notes /// /// On most OSs the duration only has a precision of seconds and will be /// silently truncated. /// /// On Apple platforms (e.g. macOS, iOS, etc) this uses `SO_LINGER_SEC`. pub fn set_linger(&self, linger: Option) -> io::Result<()> { let linger = into_linger(linger); unsafe { setsockopt(self.as_raw(), sys::SOL_SOCKET, sys::SO_LINGER, linger) } } /// Get value for the `SO_OOBINLINE` option on this socket. /// /// For more information about this option, see [`set_out_of_band_inline`]. /// /// [`set_out_of_band_inline`]: Socket::set_out_of_band_inline #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn out_of_band_inline(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_OOBINLINE) .map(|oob_inline| oob_inline != 0) } } /// Set value for the `SO_OOBINLINE` option on this socket. /// /// If this option is enabled, out-of-band data is directly placed into the /// receive data stream. Otherwise, out-of-band data is passed only when the /// `MSG_OOB` flag is set during receiving. As per RFC6093, TCP sockets /// using the Urgent mechanism are encouraged to set this flag. #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] pub fn set_out_of_band_inline(&self, oob_inline: bool) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::SOL_SOCKET, sys::SO_OOBINLINE, oob_inline as c_int, ) } } /// Get value for the `SO_RCVBUF` option on this socket. /// /// For more information about this option, see [`set_recv_buffer_size`]. /// /// [`set_recv_buffer_size`]: Socket::set_recv_buffer_size pub fn recv_buffer_size(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVBUF) .map(|size| size as usize) } } /// Set value for the `SO_RCVBUF` option on this socket. /// /// 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<()> { unsafe { setsockopt( self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVBUF, size as c_int, ) } } /// Get value for the `SO_RCVTIMEO` option on this socket. /// /// If the returned timeout is `None`, then `read` and `recv` calls will /// block indefinitely. pub fn read_timeout(&self) -> io::Result> { sys::timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVTIMEO) } /// Set value for the `SO_RCVTIMEO` option on this socket. /// /// If `timeout` is `None`, then `read` and `recv` calls will block /// indefinitely. pub fn set_read_timeout(&self, duration: Option) -> io::Result<()> { sys::set_timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVTIMEO, duration) } /// Get the value of the `SO_REUSEADDR` option on this socket. /// /// For more information about this option, see [`set_reuse_address`]. /// /// [`set_reuse_address`]: Socket::set_reuse_address pub fn reuse_address(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_REUSEADDR) .map(|reuse| reuse != 0) } } /// Set value for the `SO_REUSEADDR` option on this socket. /// /// 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<()> { unsafe { setsockopt( self.as_raw(), sys::SOL_SOCKET, sys::SO_REUSEADDR, reuse as c_int, ) } } /// Get the value of the `SO_SNDBUF` option on this socket. /// /// For more information about this option, see [`set_send_buffer_size`]. /// /// [`set_send_buffer_size`]: Socket::set_send_buffer_size pub fn send_buffer_size(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDBUF) .map(|size| size as usize) } } /// Set value for the `SO_SNDBUF` option on this 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<()> { unsafe { setsockopt( self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDBUF, size as c_int, ) } } /// Get value for the `SO_SNDTIMEO` option on this socket. /// /// If the returned timeout is `None`, then `write` and `send` calls will /// block indefinitely. pub fn write_timeout(&self) -> io::Result> { sys::timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDTIMEO) } /// Set value for the `SO_SNDTIMEO` option on this socket. /// /// If `timeout` is `None`, then `write` and `send` calls will block /// indefinitely. pub fn set_write_timeout(&self, duration: Option) -> io::Result<()> { sys::set_timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDTIMEO, duration) } } fn from_linger(linger: sys::linger) -> Option { if linger.l_onoff == 0 { None } else { Some(Duration::from_secs(linger.l_linger as u64)) } } fn into_linger(duration: Option) -> sys::linger { match duration { Some(duration) => sys::linger { l_onoff: 1, l_linger: duration.as_secs() as _, }, None => sys::linger { l_onoff: 0, l_linger: 0, }, } } /// Socket options for IPv4 sockets, get/set using `IPPROTO_IP`. /// /// Additional documentation can be found in documentation of the OS. /// * Linux: /// * Windows: impl Socket { /// Get the value of the `IP_HDRINCL` option on this socket. /// /// For more information about this option, see [`set_header_included`]. /// /// [`set_header_included`]: Socket::set_header_included #[cfg(all(feature = "all", not(target_os = "redox")))] #[cfg_attr(docsrs, doc(all(feature = "all", not(target_os = "redox"))))] pub fn header_included(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_HDRINCL) .map(|included| included != 0) } } /// Set the value of the `IP_HDRINCL` option on this socket. /// /// If enabled, the user supplies an IP header in front of the user data. /// Valid only for [`SOCK_RAW`] sockets; see [raw(7)] for more information. /// When this flag is enabled, the values set by `IP_OPTIONS`, [`IP_TTL`], /// and [`IP_TOS`] are ignored. /// /// [`SOCK_RAW`]: Type::RAW /// [raw(7)]: https://man7.org/linux/man-pages/man7/raw.7.html /// [`IP_TTL`]: Socket::set_ttl /// [`IP_TOS`]: Socket::set_tos #[cfg(all(feature = "all", not(target_os = "redox")))] #[cfg_attr(docsrs, doc(all(feature = "all", not(target_os = "redox"))))] pub fn set_header_included(&self, included: bool) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_HDRINCL, included as c_int, ) } } /// Get the value of the `IP_TRANSPARENT` option on this socket. /// /// For more information about this option, see [`set_ip_transparent`]. /// /// [`set_ip_transparent`]: Socket::set_ip_transparent #[cfg(any(doc, all(feature = "all", target_os = "linux")))] #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] pub fn ip_transparent(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, libc::IP_TRANSPARENT) .map(|transparent| transparent != 0) } } /// Set the value of the `IP_TRANSPARENT` option on this socket. /// /// Setting this boolean option enables transparent proxying /// on this socket. This socket option allows the calling /// application to bind to a nonlocal IP address and operate /// both as a client and a server with the foreign address as /// the local endpoint. NOTE: this requires that routing be /// set up in a way that packets going to the foreign address /// are routed through the TProxy box (i.e., the system /// hosting the application that employs the IP_TRANSPARENT /// socket option). Enabling this socket option requires /// superuser privileges (the `CAP_NET_ADMIN` capability). /// /// TProxy redirection with the iptables TPROXY target also /// requires that this option be set on the redirected socket. #[cfg(any(doc, all(feature = "all", target_os = "linux")))] #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] pub fn set_ip_transparent(&self, transparent: bool) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, libc::IP_TRANSPARENT, transparent as c_int, ) } } /// Join a multicast group using `IP_ADD_MEMBERSHIP` option on this socket. /// /// 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 [`Ipv4Addr::UNSPECIFIED`] (`INADDR_ANY`) then /// an appropriate interface is chosen by the system. pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { let mreq = sys::IpMreq { imr_multiaddr: sys::to_in_addr(multiaddr), imr_interface: sys::to_in_addr(interface), }; unsafe { setsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_ADD_MEMBERSHIP, mreq) } } /// Leave a multicast group using `IP_DROP_MEMBERSHIP` option on this socket. /// /// For more information about this option, see [`join_multicast_v4`]. /// /// [`join_multicast_v4`]: Socket::join_multicast_v4 pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { let mreq = sys::IpMreq { imr_multiaddr: sys::to_in_addr(multiaddr), imr_interface: sys::to_in_addr(interface), }; unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_DROP_MEMBERSHIP, mreq, ) } } /// Join a multicast group using `IP_ADD_MEMBERSHIP` option on this socket. /// /// This function specifies a new multicast group for this socket to join. /// The address must be a valid multicast address, and `interface` specifies /// the local interface with which the system should join the multicast /// group. See [`InterfaceIndexOrAddress`]. #[cfg(not(any( target_os = "haiku", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "solaris", )))] pub fn join_multicast_v4_n( &self, multiaddr: &Ipv4Addr, interface: &InterfaceIndexOrAddress, ) -> io::Result<()> { let mreqn = sys::to_mreqn(multiaddr, interface); unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_ADD_MEMBERSHIP, mreqn, ) } } /// Leave a multicast group using `IP_DROP_MEMBERSHIP` option on this socket. /// /// For more information about this option, see [`join_multicast_v4_n`]. /// /// [`join_multicast_v4_n`]: Socket::join_multicast_v4_n #[cfg(not(any( target_os = "haiku", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "solaris", )))] pub fn leave_multicast_v4_n( &self, multiaddr: &Ipv4Addr, interface: &InterfaceIndexOrAddress, ) -> io::Result<()> { let mreqn = sys::to_mreqn(multiaddr, interface); unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_DROP_MEMBERSHIP, mreqn, ) } } /// Join a multicast SSM channel using `IP_ADD_SOURCE_MEMBERSHIP` option on this socket. /// /// This function specifies a new multicast channel for this socket to join. /// The group must be a valid SSM group address, the source must be the address of the sender /// and `interface` is the address of the local interface with which the system should join the /// multicast group. If it's [`Ipv4Addr::UNSPECIFIED`] (`INADDR_ANY`) then /// an appropriate interface is chosen by the system. #[cfg(not(any( target_os = "dragonfly", target_os = "haiku", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "fuchsia", )))] pub fn join_ssm_v4( &self, source: &Ipv4Addr, group: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()> { let mreqs = sys::IpMreqSource { imr_multiaddr: sys::to_in_addr(group), imr_interface: sys::to_in_addr(interface), imr_sourceaddr: sys::to_in_addr(source), }; unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_ADD_SOURCE_MEMBERSHIP, mreqs, ) } } /// Leave a multicast group using `IP_DROP_SOURCE_MEMBERSHIP` option on this socket. /// /// For more information about this option, see [`join_ssm_v4`]. /// /// [`join_ssm_v4`]: Socket::join_ssm_v4 #[cfg(not(any( target_os = "dragonfly", target_os = "haiku", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "fuchsia", )))] pub fn leave_ssm_v4( &self, source: &Ipv4Addr, group: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()> { let mreqs = sys::IpMreqSource { imr_multiaddr: sys::to_in_addr(group), imr_interface: sys::to_in_addr(interface), imr_sourceaddr: sys::to_in_addr(source), }; unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_DROP_SOURCE_MEMBERSHIP, mreqs, ) } } /// Get the value of the `IP_MULTICAST_IF` option for this socket. /// /// For more information about this option, see [`set_multicast_if_v4`]. /// /// [`set_multicast_if_v4`]: Socket::set_multicast_if_v4 pub fn multicast_if_v4(&self) -> io::Result { unsafe { getsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_IF).map(sys::from_in_addr) } } /// Set 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<()> { let interface = sys::to_in_addr(interface); unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_IF, interface, ) } } /// Get the value of the `IP_MULTICAST_LOOP` option for this socket. /// /// For more information about this option, see [`set_multicast_loop_v4`]. /// /// [`set_multicast_loop_v4`]: Socket::set_multicast_loop_v4 pub fn multicast_loop_v4(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_LOOP) .map(|loop_v4| loop_v4 != 0) } } /// Set 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, loop_v4: bool) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_LOOP, loop_v4 as c_int, ) } } /// Get the value of the `IP_MULTICAST_TTL` option for this socket. /// /// For more information about this option, see [`set_multicast_ttl_v4`]. /// /// [`set_multicast_ttl_v4`]: Socket::set_multicast_ttl_v4 pub fn multicast_ttl_v4(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_TTL) .map(|ttl| ttl as u32) } } /// Set 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, ttl: u32) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_TTL, ttl as c_int, ) } } /// Get the value of the `IP_TTL` option for this socket. /// /// For more information about this option, see [`set_ttl`]. /// /// [`set_ttl`]: Socket::set_ttl pub fn ttl(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_TTL).map(|ttl| ttl as u32) } } /// Set the value of the `IP_TTL` option for 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<()> { unsafe { setsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_TTL, ttl as c_int) } } /// Set the value of the `IP_TOS` option for this socket. /// /// This value sets the type-of-service field that is used in every packet /// sent from this socket. /// /// NOTE: /// documents that not all versions of windows support `IP_TOS`. #[cfg(not(any( target_os = "fuchsia", target_os = "redox", target_os = "solaris", target_os = "illumos", )))] pub fn set_tos(&self, tos: u32) -> io::Result<()> { unsafe { setsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_TOS, tos as c_int) } } /// Get the value of the `IP_TOS` option for this socket. /// /// For more information about this option, see [`set_tos`]. /// /// NOTE: /// documents that not all versions of windows support `IP_TOS`. /// /// [`set_tos`]: Socket::set_tos #[cfg(not(any( target_os = "fuchsia", target_os = "redox", target_os = "solaris", target_os = "illumos", )))] pub fn tos(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_TOS).map(|tos| tos as u32) } } /// Set the value of the `IP_RECVTOS` option for this socket. /// /// If enabled, the IP_TOS ancillary message is passed with /// incoming packets. It contains a byte which specifies the /// Type of Service/Precedence field of the packet header. #[cfg(not(any( target_os = "dragonfly", target_os = "fuchsia", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "solaris", target_os = "windows", )))] pub fn set_recv_tos(&self, recv_tos: bool) -> io::Result<()> { let recv_tos = if recv_tos { 1 } else { 0 }; unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IP, sys::IP_RECVTOS, recv_tos as c_int, ) } } /// Get the value of the `IP_RECVTOS` option for this socket. /// /// For more information about this option, see [`set_recv_tos`]. /// /// [`set_recv_tos`]: Socket::set_recv_tos #[cfg(not(any( target_os = "dragonfly", target_os = "fuchsia", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "solaris", target_os = "windows", )))] pub fn recv_tos(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_RECVTOS) .map(|recv_tos| recv_tos > 0) } } } /// Socket options for IPv6 sockets, get/set using `IPPROTO_IPV6`. /// /// Additional documentation can be found in documentation of the OS. /// * Linux: /// * Windows: impl Socket { /// Join a multicast group using `IPV6_ADD_MEMBERSHIP` option on this socket. /// /// Some OSs use `IPV6_JOIN_GROUP` for this option. /// /// 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<()> { let mreq = sys::Ipv6Mreq { ipv6mr_multiaddr: sys::to_in6_addr(multiaddr), // NOTE: some OSs use `c_int`, others use `c_uint`. ipv6mr_interface: interface as _, }; unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_ADD_MEMBERSHIP, mreq, ) } } /// Leave a multicast group using `IPV6_DROP_MEMBERSHIP` option on this socket. /// /// Some OSs use `IPV6_LEAVE_GROUP` for this option. /// /// For more information about this option, see [`join_multicast_v6`]. /// /// [`join_multicast_v6`]: Socket::join_multicast_v6 pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { let mreq = sys::Ipv6Mreq { ipv6mr_multiaddr: sys::to_in6_addr(multiaddr), // NOTE: some OSs use `c_int`, others use `c_uint`. ipv6mr_interface: interface as _, }; unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_DROP_MEMBERSHIP, mreq, ) } } /// Get the value of the `IPV6_MULTICAST_HOPS` option for this socket /// /// For more information about this option, see [`set_multicast_hops_v6`]. /// /// [`set_multicast_hops_v6`]: Socket::set_multicast_hops_v6 pub fn multicast_hops_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_HOPS) .map(|hops| hops as u32) } } /// Set 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<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_HOPS, hops as c_int, ) } } /// Get the value of the `IPV6_MULTICAST_IF` option for this socket. /// /// For more information about this option, see [`set_multicast_if_v6`]. /// /// [`set_multicast_if_v6`]: Socket::set_multicast_if_v6 pub fn multicast_if_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_IF) .map(|interface| interface as u32) } } /// Set 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<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_IF, interface as c_int, ) } } /// Get the value of the `IPV6_MULTICAST_LOOP` option for this socket. /// /// For more information about this option, see [`set_multicast_loop_v6`]. /// /// [`set_multicast_loop_v6`]: Socket::set_multicast_loop_v6 pub fn multicast_loop_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_LOOP) .map(|loop_v6| loop_v6 != 0) } } /// Set 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, loop_v6: bool) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_LOOP, loop_v6 as c_int, ) } } /// Get 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 { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_UNICAST_HOPS) .map(|hops| hops as u32) } } /// Set 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, hops: u32) -> io::Result<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_UNICAST_HOPS, hops as c_int, ) } } /// Get the value of the `IPV6_V6ONLY` option for this socket. /// /// For more information about this option, see [`set_only_v6`]. /// /// [`set_only_v6`]: Socket::set_only_v6 pub fn only_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_V6ONLY) .map(|only_v6| only_v6 != 0) } } /// Set 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<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_V6ONLY, only_v6 as c_int, ) } } } /// Socket options for TCP sockets, get/set using `IPPROTO_TCP`. /// /// Additional documentation can be found in documentation of the OS. /// * Linux: /// * Windows: impl Socket { /// Get the value of the `TCP_KEEPIDLE` option on this socket. /// /// This returns the value of `TCP_KEEPALIVE` on macOS and iOS and `TCP_KEEPIDLE` on all other /// supported Unix operating systems. #[cfg(any( doc, all( feature = "all", not(any(windows, target_os = "haiku", target_os = "openbsd")) ) ))] #[cfg_attr( docsrs, doc(cfg(all( feature = "all", not(any(windows, target_os = "haiku", target_os = "openbsd")) ))) )] pub fn keepalive_time(&self) -> io::Result { sys::keepalive_time(self.as_raw()) } /// Get the value of the `TCP_KEEPINTVL` option on this socket. /// /// For more information about this option, see [`set_tcp_keepalive`]. /// /// [`set_tcp_keepalive`]: Socket::set_tcp_keepalive #[cfg(all( feature = "all", any( doc, target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_vendor = "apple", ) ))] #[cfg_attr( docsrs, doc(cfg(all( feature = "all", any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_vendor = "apple", ) ))) )] pub fn keepalive_interval(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_KEEPINTVL) .map(|secs| Duration::from_secs(secs as u64)) } } /// Get the value of the `TCP_KEEPCNT` option on this socket. /// /// For more information about this option, see [`set_tcp_keepalive`]. /// /// [`set_tcp_keepalive`]: Socket::set_tcp_keepalive #[cfg(all( feature = "all", any( doc, target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_vendor = "apple", ) ))] #[cfg_attr( docsrs, doc(cfg(all( feature = "all", any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_vendor = "apple", ) ))) )] pub fn keepalive_retries(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_KEEPCNT) .map(|retries| retries as u32) } } /// Set 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. /// /// [keepalive time]: TcpKeepalive::with_time /// /// # Notes /// /// * This will enable `SO_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 /// /// ``` /// use std::time::Duration; /// /// use socket2::{Socket, TcpKeepalive, Domain, Type}; /// /// # fn main() -> std::io::Result<()> { /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; /// let keepalive = TcpKeepalive::new() /// .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_tcp_keepalive(&keepalive)?; /// # Ok(()) } /// ``` /// pub fn set_tcp_keepalive(&self, params: &TcpKeepalive) -> io::Result<()> { self.set_keepalive(true)?; sys::set_tcp_keepalive(self.as_raw(), params) } /// Get the value of the `TCP_NODELAY` option on this socket. /// /// For more information about this option, see [`set_nodelay`]. /// /// [`set_nodelay`]: Socket::set_nodelay pub fn nodelay(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_NODELAY) .map(|nodelay| nodelay != 0) } } /// Set 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<()> { unsafe { setsockopt( self.as_raw(), sys::IPPROTO_TCP, sys::TCP_NODELAY, nodelay as c_int, ) } } } impl Read for Socket { fn read(&mut self, buf: &mut [u8]) -> io::Result { // Safety: the `recv` implementation promises not to write uninitialised // bytes to the `buf`fer, so this casting is safe. let buf = unsafe { &mut *(buf as *mut [u8] as *mut [MaybeUninit]) }; self.recv(buf) } #[cfg(not(target_os = "redox"))] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { // Safety: both `IoSliceMut` and `MaybeUninitSlice` promise to have the // same layout, that of `iovec`/`WSABUF`. Furthermore `recv_vectored` // promises to not write unitialised bytes to the `bufs` and pass it // directly to the `recvmsg` system call, so this is safe. let bufs = unsafe { &mut *(bufs as *mut [IoSliceMut<'_>] as *mut [MaybeUninitSlice<'_>]) }; self.recv_vectored(bufs).map(|(n, _)| n) } } impl<'a> Read for &'a Socket { fn read(&mut self, buf: &mut [u8]) -> io::Result { // Safety: see other `Read::read` impl. let buf = unsafe { &mut *(buf as *mut [u8] as *mut [MaybeUninit]) }; self.recv(buf) } #[cfg(not(target_os = "redox"))] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { // Safety: see other `Read::read` impl. let bufs = unsafe { &mut *(bufs as *mut [IoSliceMut<'_>] as *mut [MaybeUninitSlice<'_>]) }; self.recv_vectored(bufs).map(|(n, _)| n) } } impl Write for Socket { fn write(&mut self, buf: &[u8]) -> io::Result { self.send(buf) } #[cfg(not(target_os = "redox"))] fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl<'a> Write for &'a Socket { fn write(&mut self, buf: &[u8]) -> io::Result { self.send(buf) } #[cfg(not(target_os = "redox"))] fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl fmt::Debug for Socket { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Socket") .field("raw", &self.as_raw()) .field("local_addr", &self.local_addr().ok()) .field("peer_addr", &self.peer_addr().ok()) .finish() } } from!(net::TcpStream, Socket); from!(net::TcpListener, Socket); from!(net::UdpSocket, Socket); from!(Socket, net::TcpStream); from!(Socket, net::TcpListener); from!(Socket, net::UdpSocket);