| 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<F, T>(init: F) -> io::Result<(T, SockAddr)> |
| where |
| F: FnOnce(*mut sockaddr_storage, *mut socklen_t) -> io::Result<T>, |
| { |
| const STORAGE_SIZE: socklen_t = size_of::<sockaddr_storage>() 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::<sockaddr_storage>::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<P>(path: P) -> io::Result<SockAddr> |
| where |
| P: AsRef<Path>, |
| { |
| 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<SocketAddr> { |
| 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::<sockaddr_in>()) }; |
| 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::<sockaddr_in6>()) }; |
| 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<SocketAddrV4> { |
| 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<SocketAddrV6> { |
| 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<SocketAddr> for SockAddr { |
| fn from(addr: SocketAddr) -> SockAddr { |
| match addr { |
| SocketAddr::V4(addr) => addr.into(), |
| SocketAddr::V6(addr) => addr.into(), |
| } |
| } |
| } |
| |
| impl From<SocketAddrV4> for SockAddr { |
| fn from(addr: SocketAddrV4) -> SockAddr { |
| // SAFETY: a `sockaddr_storage` of all zeros is valid. |
| let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() }; |
| let len = { |
| let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<sockaddr_in>() }; |
| 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::<sockaddr_in>() 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<SocketAddrV6> for SockAddr { |
| fn from(addr: SocketAddrV6) -> SockAddr { |
| // SAFETY: a `sockaddr_storage` of all zeros is valid. |
| let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() }; |
| let len = { |
| let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<sockaddr_in6>() }; |
| 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::<sockaddr_in6>() 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<H: std::hash::Hasher>(&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::<sockaddr_in>() 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::<sockaddr_in>() 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::<sockaddr_in6>() 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::<sockaddr_in6>() 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() |
| } |
| } |