// 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. //! Utilities for creating and using sockets. //! //! The goal of this crate is to create and use a socket using advanced //! configuration options (those that are not available in the types in the //! standard library) without using any unsafe code. //! //! This crate provides as direct as possible access to the system's //! functionality for sockets, this means little effort to provide //! cross-platform utilities. It is up to the user to know how to use sockets //! when using this crate. *If you don't know how to create a socket using //! libc/system calls then this crate is not for you*. Most, if not all, //! functions directly relate to the equivalent system call with no error //! handling applied, so no handling errors such as [`EINTR`]. As a result using //! this crate can be a little wordy, but it should give you maximal flexibility //! over configuration of sockets. //! //! [`EINTR`]: std::io::ErrorKind::Interrupted //! //! # 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::IPV6, Type::STREAM, None)?; //! //! socket.set_only_v6(false)?; //! let address: SocketAddr = "[::1]:12345".parse().unwrap(); //! socket.bind(&address.into())?; //! socket.listen(128)?; //! //! let listener: TcpListener = socket.into(); //! // ... //! # drop(listener); //! # Ok(()) } //! ``` //! //! ## Features //! //! This crate has a single feature `all`, which enables all functions even ones //! that are not available on all OSs. #![doc(html_root_url = "https://docs.rs/socket2/0.4")] #![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)] // Show required OS/features on docs.rs. #![cfg_attr(docsrs, feature(doc_cfg))] // Disallow warnings when running tests. #![cfg_attr(test, deny(warnings))] // Disallow warnings in examples. #![doc(test(attr(deny(warnings))))] use std::fmt; use std::mem::MaybeUninit; use std::net::SocketAddr; use std::ops::{Deref, DerefMut}; use std::time::Duration; /// Macro to implement `fmt::Debug` for a type, printing the constant names /// rather than a number. /// /// Note this is used in the `sys` module and thus must be defined before /// defining the modules. macro_rules! impl_debug { ( // Type name for which to implement `fmt::Debug`. $type: path, $( $(#[$target: meta])* // The flag(s) to check. // Need to specific the libc crate because Windows doesn't use // `libc` but `winapi`. $libc: ident :: $flag: ident ),+ $(,)* ) => { impl std::fmt::Debug for $type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let string = match self.0 { $( $(#[$target])* $libc :: $flag => stringify!($flag), )+ n => return write!(f, "{}", n), }; f.write_str(string) } } }; } /// Macro to convert from one network type to another. macro_rules! from { ($from: ty, $for: ty) => { impl From<$from> for $for { fn from(socket: $from) -> $for { #[cfg(unix)] unsafe { <$for>::from_raw_fd(socket.into_raw_fd()) } #[cfg(windows)] unsafe { <$for>::from_raw_socket(socket.into_raw_socket()) } } } }; } mod sockaddr; mod socket; mod sockref; #[cfg_attr(unix, path = "sys/unix.rs")] #[cfg_attr(windows, path = "sys/windows.rs")] mod sys; #[cfg(not(any(windows, unix)))] compile_error!("Socket2 doesn't support the compile target"); use sys::c_int; pub use sockaddr::SockAddr; pub use socket::Socket; pub use sockref::SockRef; #[cfg(not(any( target_os = "haiku", target_os = "illumos", target_os = "netbsd", target_os = "redox", target_os = "solaris", )))] pub use socket::InterfaceIndexOrAddress; /// Specification of the communication domain for a socket. /// /// This is a newtype wrapper around an integer which provides a nicer API in /// addition to an injection point for documentation. Convenience constants such /// as [`Domain::IPV4`], [`Domain::IPV6`], etc, are provided to avoid reaching /// into libc for various constants. /// /// This type is freely interconvertible with C's `int` type, however, if a raw /// value needs to be provided. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Domain(c_int); impl Domain { /// Domain for IPv4 communication, corresponding to `AF_INET`. pub const IPV4: Domain = Domain(sys::AF_INET); /// Domain for IPv6 communication, corresponding to `AF_INET6`. pub const IPV6: Domain = Domain(sys::AF_INET6); /// Returns the correct domain for `address`. pub const fn for_address(address: SocketAddr) -> Domain { match address { SocketAddr::V4(_) => Domain::IPV4, SocketAddr::V6(_) => Domain::IPV6, } } } impl From for Domain { fn from(d: c_int) -> Domain { Domain(d) } } impl From for c_int { fn from(d: Domain) -> c_int { d.0 } } /// Specification of communication semantics on a socket. /// /// This is a newtype wrapper around an integer which provides a nicer API in /// addition to an injection point for documentation. Convenience constants such /// as [`Type::STREAM`], [`Type::DGRAM`], etc, are provided to avoid reaching /// into libc for various constants. /// /// This type is freely interconvertible with C's `int` type, however, if a raw /// value needs to be provided. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Type(c_int); impl Type { /// Type corresponding to `SOCK_STREAM`. /// /// Used for protocols such as TCP. pub const STREAM: Type = Type(sys::SOCK_STREAM); /// Type corresponding to `SOCK_DGRAM`. /// /// Used for protocols such as UDP. pub const DGRAM: Type = Type(sys::SOCK_DGRAM); /// Type corresponding to `SOCK_SEQPACKET`. #[cfg(feature = "all")] #[cfg_attr(docsrs, doc(cfg(feature = "all")))] pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET); /// Type corresponding to `SOCK_RAW`. #[cfg(all(feature = "all", not(target_os = "redox")))] #[cfg_attr(docsrs, doc(cfg(all(feature = "all", not(target_os = "redox")))))] pub const RAW: Type = Type(sys::SOCK_RAW); } impl From for Type { fn from(t: c_int) -> Type { Type(t) } } impl From for c_int { fn from(t: Type) -> c_int { t.0 } } /// Protocol specification used for creating sockets via `Socket::new`. /// /// This is a newtype wrapper around an integer which provides a nicer API in /// addition to an injection point for documentation. /// /// This type is freely interconvertible with C's `int` type, however, if a raw /// value needs to be provided. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Protocol(c_int); impl Protocol { /// Protocol corresponding to `ICMPv4`. pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP); /// Protocol corresponding to `ICMPv6`. pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6); /// Protocol corresponding to `TCP`. pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP); /// Protocol corresponding to `UDP`. pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP); } impl From for Protocol { fn from(p: c_int) -> Protocol { Protocol(p) } } impl From for c_int { fn from(p: Protocol) -> c_int { p.0 } } /// Flags for incoming messages. /// /// Flags provide additional information about incoming messages. #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] #[derive(Copy, Clone, Eq, PartialEq)] pub struct RecvFlags(c_int); #[cfg(not(target_os = "redox"))] impl RecvFlags { /// Check if the message contains a truncated datagram. /// /// This flag is only used for datagram-based sockets, /// not for stream sockets. /// /// On Unix this corresponds to the `MSG_TRUNC` flag. /// On Windows this corresponds to the `WSAEMSGSIZE` error code. pub const fn is_truncated(self) -> bool { self.0 & sys::MSG_TRUNC != 0 } } /// A version of [`IoSliceMut`] that allows the buffer to be uninitialised. /// /// [`IoSliceMut`]: std::io::IoSliceMut #[repr(transparent)] pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>); impl<'a> fmt::Debug for MaybeUninitSlice<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.0.as_slice(), fmt) } } impl<'a> MaybeUninitSlice<'a> { /// Creates a new `MaybeUninitSlice` wrapping a byte slice. /// /// # Panics /// /// Panics on Windows if the slice is larger than 4GB. pub fn new(buf: &'a mut [MaybeUninit]) -> MaybeUninitSlice<'a> { MaybeUninitSlice(sys::MaybeUninitSlice::new(buf)) } } impl<'a> Deref for MaybeUninitSlice<'a> { type Target = [MaybeUninit]; fn deref(&self) -> &[MaybeUninit] { self.0.as_slice() } } impl<'a> DerefMut for MaybeUninitSlice<'a> { fn deref_mut(&mut self) -> &mut [MaybeUninit] { self.0.as_mut_slice() } } /// Configures a socket's TCP keepalive parameters. /// /// See [`Socket::set_tcp_keepalive`]. #[derive(Debug, Clone)] pub struct TcpKeepalive { #[cfg_attr(target_os = "openbsd", allow(dead_code))] time: Option, #[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "solaris")))] interval: Option, #[cfg(not(any( target_os = "openbsd", target_os = "redox", target_os = "solaris", target_os = "windows" )))] retries: Option, } impl TcpKeepalive { /// Returns a new, empty set of TCP keepalive parameters. pub const fn new() -> TcpKeepalive { TcpKeepalive { time: None, #[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "solaris")))] interval: None, #[cfg(not(any( target_os = "openbsd", target_os = "redox", target_os = "solaris", target_os = "windows" )))] retries: None, } } /// Set the amount of time after which TCP keepalive probes will be sent on /// idle connections. /// /// This will set `TCP_KEEPALIVE` on macOS and iOS, and /// `TCP_KEEPIDLE` on all other Unix operating systems, except /// OpenBSD and Haiku which don't support any way to set this /// option. On Windows, this sets the value of the `tcp_keepalive` /// struct's `keepalivetime` field. /// /// Some platforms specify this value in seconds, so sub-second /// specifications may be omitted. pub const fn with_time(self, time: Duration) -> Self { Self { time: Some(time), ..self } } /// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the /// value of the `tcp_keepalive` struct's `keepaliveinterval` field. /// /// Sets the time interval between TCP keepalive probes. /// /// Some platforms specify this value in seconds, so sub-second /// specifications may be omitted. #[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", windows, ) ))] #[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", windows, ) ))) )] pub const fn with_interval(self, interval: Duration) -> Self { Self { interval: Some(interval), ..self } } /// Set the value of the `TCP_KEEPCNT` option. /// /// Set the maximum number of TCP keepalive probes that will be sent before /// dropping a connection, if TCP keepalive is enabled on this socket. #[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 const fn with_retries(self, retries: u32) -> Self { Self { retries: Some(retries), ..self } } }