| // Copyright 2015 The Rust Project Developers. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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; |
| #[cfg(not(target_os = "nto"))] |
| use std::net::Ipv6Addr; |
| use std::net::{self, Ipv4Addr, 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<Protocol>) -> io::Result<Socket> { |
| 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<Protocol>) -> io::Result<Socket> { |
| 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<Protocol>, |
| ) -> 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<Protocol>, |
| ) -> 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<SockAddr> { |
| 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<SockAddr> { |
| 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<Type> { |
| unsafe { getsockopt::<c_int>(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 |
| /// <https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaduplicatesocketw>. |
| pub fn try_clone(&self) -> io::Result<Socket> { |
| 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<u8>]` 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<u8>]) -> io::Result<usize> { |
| 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<u8>]) -> io::Result<usize> { |
| 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<u8>], |
| flags: sys::c_int, |
| ) -> io::Result<usize> { |
| 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<u8>]) -> io::Result<usize> { |
| 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<u8>]) -> 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<u8>], |
| 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`]. |
| /// |
| /// # Note: Datagram Sockets |
| /// For datagram sockets, the behavior of this method when `buf` is smaller than |
| /// the datagram at the head of the receive queue differs between Windows and |
| /// Unix-like platforms (Linux, macOS, BSDs, etc: colloquially termed "*nix"). |
| /// |
| /// On *nix platforms, the datagram is truncated to the length of `buf`. |
| /// |
| /// On Windows, an error corresponding to `WSAEMSGSIZE` will be returned. |
| /// |
| /// For consistency between platforms, be sure to provide a sufficiently large buffer to avoid |
| /// truncation; the exact size required depends on the underlying protocol. |
| /// |
| /// If you just want to know the sender of the data, try [`peek_sender`]. |
| /// |
| /// [`recv`]: Socket::recv |
| /// [`peek_sender`]: Socket::peek_sender |
| pub fn peek_from(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<(usize, SockAddr)> { |
| self.recv_from_with_flags(buf, sys::MSG_PEEK) |
| } |
| |
| /// Retrieve the sender for the data at the head of the receive queue. |
| /// |
| /// This is equivalent to calling [`peek_from`] with a zero-sized buffer, |
| /// but suppresses the `WSAEMSGSIZE` error on Windows. |
| /// |
| /// [`peek_from`]: Socket::peek_from |
| pub fn peek_sender(&self) -> io::Result<SockAddr> { |
| sys::peek_sender(self.as_raw()) |
| } |
| |
| /// 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.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<usize> { |
| 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<usize> { |
| 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<usize> { |
| 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<usize> { |
| 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<usize> { |
| 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<usize> { |
| 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<usize> { |
| 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<usize> { |
| 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<Socket> { |
| // 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: <https://man7.org/linux/man-pages/man7/socket.7.html> |
| /// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options> |
| 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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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<Option<io::Error>> { |
| match unsafe { getsockopt::<c_int>(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<bool> { |
| unsafe { |
| getsockopt::<Bool>(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<Option<Duration>> { |
| unsafe { |
| getsockopt::<sys::linger>(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<Duration>) -> 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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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<usize> { |
| unsafe { |
| getsockopt::<c_int>(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<Option<Duration>> { |
| 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<Duration>) -> 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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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<usize> { |
| unsafe { |
| getsockopt::<c_int>(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<Option<Duration>> { |
| 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<Duration>) -> io::Result<()> { |
| sys::set_timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDTIMEO, duration) |
| } |
| } |
| |
| fn from_linger(linger: sys::linger) -> Option<Duration> { |
| if linger.l_onoff == 0 { |
| None |
| } else { |
| Some(Duration::from_secs(linger.l_linger as u64)) |
| } |
| } |
| |
| fn into_linger(duration: Option<Duration>) -> 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: <https://man7.org/linux/man-pages/man7/ip.7.html> |
| /// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options> |
| 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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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", |
| target_os = "nto", |
| )))] |
| 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", |
| target_os = "nto", |
| )))] |
| 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", |
| target_os = "nto", |
| )))] |
| 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", |
| target_os = "nto", |
| )))] |
| 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<Ipv4Addr> { |
| 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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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<u32> { |
| unsafe { |
| getsockopt::<c_int>(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<u32> { |
| unsafe { |
| getsockopt::<c_int>(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: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options> |
| /// 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: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options> |
| /// 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<u32> { |
| unsafe { |
| getsockopt::<c_int>(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", |
| target_os = "nto", |
| )))] |
| 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", |
| target_os = "nto", |
| )))] |
| pub fn recv_tos(&self) -> io::Result<bool> { |
| unsafe { |
| getsockopt::<c_int>(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: <https://man7.org/linux/man-pages/man7/ipv6.7.html> |
| /// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ipv6-socket-options> |
| 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). |
| #[cfg(not(target_os = "nto"))] |
| 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 |
| #[cfg(not(target_os = "nto"))] |
| 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<u32> { |
| unsafe { |
| getsockopt::<c_int>(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<u32> { |
| unsafe { |
| getsockopt::<c_int>(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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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<u32> { |
| unsafe { |
| getsockopt::<c_int>(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<bool> { |
| unsafe { |
| getsockopt::<c_int>(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: <https://man7.org/linux/man-pages/man7/tcp.7.html> |
| /// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options> |
| 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<Duration> { |
| 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<Duration> { |
| unsafe { |
| getsockopt::<c_int>(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<u32> { |
| unsafe { |
| getsockopt::<c_int>(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<bool> { |
| unsafe { |
| getsockopt::<Bool>(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<usize> { |
| // 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<u8>]) }; |
| self.recv(buf) |
| } |
| |
| #[cfg(not(target_os = "redox"))] |
| fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
| // 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<usize> { |
| // Safety: see other `Read::read` impl. |
| let buf = unsafe { &mut *(buf as *mut [u8] as *mut [MaybeUninit<u8>]) }; |
| self.recv(buf) |
| } |
| |
| #[cfg(not(target_os = "redox"))] |
| fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
| // 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<usize> { |
| self.send(buf) |
| } |
| |
| #[cfg(not(target_os = "redox"))] |
| fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
| 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<usize> { |
| self.send(buf) |
| } |
| |
| #[cfg(not(target_os = "redox"))] |
| fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
| 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); |