use std::hash::Hash; use std::mem::{self, size_of, MaybeUninit}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::path::Path; use std::{fmt, io, ptr}; #[cfg(windows)] use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN6_0; use crate::sys::{ c_int, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET, AF_INET6, AF_UNIX, }; use crate::Domain; /// The address of a socket. /// /// `SockAddr`s may be constructed directly to and from the standard library /// [`SocketAddr`], [`SocketAddrV4`], and [`SocketAddrV6`] types. #[derive(Clone)] pub struct SockAddr { storage: sockaddr_storage, len: socklen_t, } #[allow(clippy::len_without_is_empty)] impl SockAddr { /// Create a `SockAddr` from the underlying storage and its length. /// /// # Safety /// /// Caller must ensure that the address family and length match the type of /// storage address. For example if `storage.ss_family` is set to `AF_INET` /// the `storage` must be initialised as `sockaddr_in`, setting the content /// and length appropriately. /// /// # Examples /// /// ``` /// # fn main() -> std::io::Result<()> { /// # #[cfg(unix)] { /// use std::io; /// use std::mem; /// use std::os::unix::io::AsRawFd; /// /// use socket2::{SockAddr, Socket, Domain, Type}; /// /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; /// /// // Initialise a `SocketAddr` byte calling `getsockname(2)`. /// let mut addr_storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; /// let mut len = mem::size_of_val(&addr_storage) as libc::socklen_t; /// /// // The `getsockname(2)` system call will intiliase `storage` for /// // us, setting `len` to the correct length. /// let res = unsafe { /// libc::getsockname( /// socket.as_raw_fd(), /// (&mut addr_storage as *mut libc::sockaddr_storage).cast(), /// &mut len, /// ) /// }; /// if res == -1 { /// return Err(io::Error::last_os_error()); /// } /// /// let address = unsafe { SockAddr::new(addr_storage, len) }; /// # drop(address); /// # } /// # Ok(()) /// # } /// ``` pub const unsafe fn new(storage: sockaddr_storage, len: socklen_t) -> SockAddr { SockAddr { storage, len } } /// Initialise a `SockAddr` by calling the function `init`. /// /// The type of the address storage and length passed to the function `init` /// is OS/architecture specific. /// /// The address is zeroed before `init` is called and is thus valid to /// dereference and read from. The length initialised to the maximum length /// of the storage. /// /// # Safety /// /// Caller must ensure that the address family and length match the type of /// storage address. For example if `storage.ss_family` is set to `AF_INET` /// the `storage` must be initialised as `sockaddr_in`, setting the content /// and length appropriately. /// /// # Examples /// /// ``` /// # fn main() -> std::io::Result<()> { /// # #[cfg(unix)] { /// use std::io; /// use std::os::unix::io::AsRawFd; /// /// use socket2::{SockAddr, Socket, Domain, Type}; /// /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; /// /// // Initialise a `SocketAddr` byte calling `getsockname(2)`. /// let (_, address) = unsafe { /// SockAddr::try_init(|addr_storage, len| { /// // The `getsockname(2)` system call will intiliase `storage` for /// // us, setting `len` to the correct length. /// if libc::getsockname(socket.as_raw_fd(), addr_storage.cast(), len) == -1 { /// Err(io::Error::last_os_error()) /// } else { /// Ok(()) /// } /// }) /// }?; /// # drop(address); /// # } /// # Ok(()) /// # } /// ``` pub unsafe fn try_init(init: F) -> io::Result<(T, SockAddr)> where F: FnOnce(*mut sockaddr_storage, *mut socklen_t) -> io::Result, { const STORAGE_SIZE: socklen_t = size_of::() as socklen_t; // NOTE: `SockAddr::unix` depends on the storage being zeroed before // calling `init`. // NOTE: calling `recvfrom` with an empty buffer also depends on the // storage being zeroed before calling `init` as the OS might not // initialise it. let mut storage = MaybeUninit::::zeroed(); let mut len = STORAGE_SIZE; init(storage.as_mut_ptr(), &mut len).map(|res| { debug_assert!(len <= STORAGE_SIZE, "overflown address storage"); let addr = SockAddr { // Safety: zeroed-out `sockaddr_storage` is valid, caller must // ensure at least `len` bytes are valid. storage: storage.assume_init(), len, }; (res, addr) }) } /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. /// /// Returns an error if the path is longer than `SUN_LEN`. pub fn unix

(path: P) -> io::Result where P: AsRef, { crate::sys::unix_sockaddr(path.as_ref()) } /// Set the length of the address. /// /// # Safety /// /// Caller must ensure that the address up to `length` bytes are properly /// initialised. pub unsafe fn set_length(&mut self, length: socklen_t) { self.len = length; } /// Returns this address's family. pub const fn family(&self) -> sa_family_t { self.storage.ss_family } /// Returns this address's `Domain`. pub const fn domain(&self) -> Domain { Domain(self.storage.ss_family as c_int) } /// Returns the size of this address in bytes. pub const fn len(&self) -> socklen_t { self.len } /// Returns a raw pointer to the address. pub const fn as_ptr(&self) -> *const sockaddr { ptr::addr_of!(self.storage).cast() } /// Retuns the address as the storage. pub const fn as_storage(self) -> sockaddr_storage { self.storage } /// Returns true if this address is in the `AF_INET` (IPv4) family, false otherwise. pub const fn is_ipv4(&self) -> bool { self.storage.ss_family == AF_INET as sa_family_t } /// Returns true if this address is in the `AF_INET6` (IPv6) family, false /// otherwise. pub const fn is_ipv6(&self) -> bool { self.storage.ss_family == AF_INET6 as sa_family_t } /// Returns true if this address is of a unix socket (for local interprocess communication), /// i.e. it is from the `AF_UNIX` family, false otherwise. pub fn is_unix(&self) -> bool { self.storage.ss_family == AF_UNIX as sa_family_t } /// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IPv4) /// or `AF_INET6` (IPv6) family, otherwise returns `None`. pub fn as_socket(&self) -> Option { if self.storage.ss_family == AF_INET as sa_family_t { // SAFETY: if the `ss_family` field is `AF_INET` then storage must // be a `sockaddr_in`. let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::()) }; let ip = crate::sys::from_in_addr(addr.sin_addr); let port = u16::from_be(addr.sin_port); Some(SocketAddr::V4(SocketAddrV4::new(ip, port))) } else if self.storage.ss_family == AF_INET6 as sa_family_t { // SAFETY: if the `ss_family` field is `AF_INET6` then storage must // be a `sockaddr_in6`. let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::()) }; let ip = crate::sys::from_in6_addr(addr.sin6_addr); let port = u16::from_be(addr.sin6_port); Some(SocketAddr::V6(SocketAddrV6::new( ip, port, addr.sin6_flowinfo, #[cfg(unix)] addr.sin6_scope_id, #[cfg(windows)] unsafe { addr.Anonymous.sin6_scope_id }, ))) } else { None } } /// Returns this address as a [`SocketAddrV4`] if it is in the `AF_INET` /// family. pub fn as_socket_ipv4(&self) -> Option { match self.as_socket() { Some(SocketAddr::V4(addr)) => Some(addr), _ => None, } } /// Returns this address as a [`SocketAddrV6`] if it is in the `AF_INET6` /// family. pub fn as_socket_ipv6(&self) -> Option { match self.as_socket() { Some(SocketAddr::V6(addr)) => Some(addr), _ => None, } } /// Returns the initialised storage bytes. fn as_bytes(&self) -> &[u8] { // SAFETY: `self.storage` is a C struct which can always be treated a // slice of bytes. Futhermore we ensure we don't read any unitialised // bytes by using `self.len`. unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len as usize) } } } impl From for SockAddr { fn from(addr: SocketAddr) -> SockAddr { match addr { SocketAddr::V4(addr) => addr.into(), SocketAddr::V6(addr) => addr.into(), } } } impl From for SockAddr { fn from(addr: SocketAddrV4) -> SockAddr { // SAFETY: a `sockaddr_storage` of all zeros is valid. let mut storage = unsafe { mem::zeroed::() }; let len = { let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::() }; storage.sin_family = AF_INET as sa_family_t; storage.sin_port = addr.port().to_be(); storage.sin_addr = crate::sys::to_in_addr(addr.ip()); storage.sin_zero = Default::default(); mem::size_of::() as socklen_t }; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "haiku", target_os = "hermit", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "nto", target_os = "openbsd", target_os = "tvos", target_os = "vxworks", target_os = "watchos", ))] { storage.ss_len = len as u8; } SockAddr { storage, len } } } impl From for SockAddr { fn from(addr: SocketAddrV6) -> SockAddr { // SAFETY: a `sockaddr_storage` of all zeros is valid. let mut storage = unsafe { mem::zeroed::() }; let len = { let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::() }; storage.sin6_family = AF_INET6 as sa_family_t; storage.sin6_port = addr.port().to_be(); storage.sin6_addr = crate::sys::to_in6_addr(addr.ip()); storage.sin6_flowinfo = addr.flowinfo(); #[cfg(unix)] { storage.sin6_scope_id = addr.scope_id(); } #[cfg(windows)] { storage.Anonymous = SOCKADDR_IN6_0 { sin6_scope_id: addr.scope_id(), }; } mem::size_of::() as socklen_t }; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "haiku", target_os = "hermit", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "nto", target_os = "openbsd", target_os = "tvos", target_os = "vxworks", target_os = "watchos", ))] { storage.ss_len = len as u8; } SockAddr { storage, len } } } impl fmt::Debug for SockAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = fmt.debug_struct("SockAddr"); #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "haiku", target_os = "hermit", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "nto", target_os = "openbsd", target_os = "tvos", target_os = "vxworks", target_os = "watchos", ))] f.field("ss_len", &self.storage.ss_len); f.field("ss_family", &self.storage.ss_family) .field("len", &self.len) .finish() } } impl PartialEq for SockAddr { fn eq(&self, other: &Self) -> bool { self.as_bytes() == other.as_bytes() } } impl Eq for SockAddr {} impl Hash for SockAddr { fn hash(&self, state: &mut H) { self.as_bytes().hash(state); } } #[cfg(test)] mod tests { use super::*; #[test] fn ipv4() { use std::net::Ipv4Addr; let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); let addr = SockAddr::from(std); assert!(addr.is_ipv4()); assert!(!addr.is_ipv6()); assert!(!addr.is_unix()); assert_eq!(addr.family(), AF_INET as sa_family_t); assert_eq!(addr.domain(), Domain::IPV4); assert_eq!(addr.len(), size_of::() as socklen_t); assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std))); assert_eq!(addr.as_socket_ipv4(), Some(std)); assert!(addr.as_socket_ipv6().is_none()); let addr = SockAddr::from(SocketAddr::from(std)); assert_eq!(addr.family(), AF_INET as sa_family_t); assert_eq!(addr.len(), size_of::() as socklen_t); assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std))); assert_eq!(addr.as_socket_ipv4(), Some(std)); assert!(addr.as_socket_ipv6().is_none()); #[cfg(unix)] { assert!(addr.as_pathname().is_none()); assert!(addr.as_abstract_namespace().is_none()); } } #[test] fn ipv6() { use std::net::Ipv6Addr; let std = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); let addr = SockAddr::from(std); assert!(addr.is_ipv6()); assert!(!addr.is_ipv4()); assert!(!addr.is_unix()); assert_eq!(addr.family(), AF_INET6 as sa_family_t); assert_eq!(addr.domain(), Domain::IPV6); assert_eq!(addr.len(), size_of::() as socklen_t); assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std))); assert!(addr.as_socket_ipv4().is_none()); assert_eq!(addr.as_socket_ipv6(), Some(std)); let addr = SockAddr::from(SocketAddr::from(std)); assert_eq!(addr.family(), AF_INET6 as sa_family_t); assert_eq!(addr.len(), size_of::() as socklen_t); assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std))); assert!(addr.as_socket_ipv4().is_none()); assert_eq!(addr.as_socket_ipv6(), Some(std)); #[cfg(unix)] { assert!(addr.as_pathname().is_none()); assert!(addr.as_abstract_namespace().is_none()); } } #[test] fn ipv4_eq() { use std::net::Ipv4Addr; let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765); test_eq( SockAddr::from(std1), SockAddr::from(std1), SockAddr::from(std2), ); } #[test] fn ipv4_hash() { use std::net::Ipv4Addr; let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765); test_hash( SockAddr::from(std1), SockAddr::from(std1), SockAddr::from(std2), ); } #[test] fn ipv6_eq() { use std::net::Ipv6Addr; let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14); test_eq( SockAddr::from(std1), SockAddr::from(std1), SockAddr::from(std2), ); } #[test] fn ipv6_hash() { use std::net::Ipv6Addr; let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14); test_hash( SockAddr::from(std1), SockAddr::from(std1), SockAddr::from(std2), ); } #[test] fn ipv4_ipv6_eq() { use std::net::Ipv4Addr; use std::net::Ipv6Addr; let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); test_eq( SockAddr::from(std1), SockAddr::from(std1), SockAddr::from(std2), ); test_eq( SockAddr::from(std2), SockAddr::from(std2), SockAddr::from(std1), ); } #[test] fn ipv4_ipv6_hash() { use std::net::Ipv4Addr; use std::net::Ipv6Addr; let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); test_hash( SockAddr::from(std1), SockAddr::from(std1), SockAddr::from(std2), ); test_hash( SockAddr::from(std2), SockAddr::from(std2), SockAddr::from(std1), ); } #[allow(clippy::eq_op)] // allow a0 == a0 check fn test_eq(a0: SockAddr, a1: SockAddr, b: SockAddr) { assert!(a0 == a0); assert!(a0 == a1); assert!(a1 == a0); assert!(a0 != b); assert!(b != a0); } fn test_hash(a0: SockAddr, a1: SockAddr, b: SockAddr) { assert!(calculate_hash(&a0) == calculate_hash(&a0)); assert!(calculate_hash(&a0) == calculate_hash(&a1)); // technically unequal values can have the same hash, in this case x != z and both have different hashes assert!(calculate_hash(&a0) != calculate_hash(&b)); } fn calculate_hash(x: &SockAddr) -> u64 { use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; let mut hasher = DefaultHasher::new(); x.hash(&mut hasher); hasher.finish() } }