use std::hash::Hash; use std::mem::{self, size_of}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; #[cfg(not(target_os = "wasi"))] use std::path::Path; use std::{fmt, io, ptr};
#[cfg(windows)] use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN6_0;
/// The integer type used with `getsockname` on this platform. #[allow(non_camel_case_types)] pubtype socklen_t = crate::sys::socklen_t;
/// The integer type for the `ss_family` field on this platform. #[allow(non_camel_case_types)] pubtype sa_family_t = crate::sys::sa_family_t;
/// Rust version of the [`sockaddr_storage`] type. /// /// This type is intended to be used with with direct calls to the `getsockname` syscall. See the /// documentation of [`SockAddr::new`] for examples. /// /// This crate defines its own `sockaddr_storage` type to avoid semver concerns with upgrading /// `windows-sys`. #[repr(transparent)] pubstruct SockAddrStorage {
storage: sockaddr_storage,
}
impl SockAddrStorage { /// Construct a new storage containing all zeros. #[inline] pubfn zeroed() -> Self { // SAFETY: All zeros is valid for this type. unsafe { mem::zeroed() }
}
/// Returns the size of this storage. #[inline] pubfn size_of(&self) -> socklen_t {
size_of::<Self>() as socklen_t
}
/// View this type as another type. /// /// # Safety /// /// The type `T` must be one of the `sockaddr_*` types defined by this platform. /// /// # Examples /// ``` /// # #[allow(dead_code)] /// # #[cfg(unix)] mod unix_example { /// # use core::mem::size_of; /// use libc::sockaddr_storage; /// use socket2::{SockAddr, SockAddrStorage, socklen_t}; /// /// fn from_sockaddr_storage(recv_address: &sockaddr_storage) -> SockAddr { /// let mut storage = SockAddrStorage::zeroed(); /// let libc_address = unsafe { storage.view_as::<sockaddr_storage>() }; /// *libc_address = *recv_address; /// unsafe { SockAddr::new(storage, size_of::<sockaddr_storage>() as socklen_t) } /// } /// # } /// ``` #[inline] pubunsafefn view_as<T>(&mutself) -> &mut T {
assert!(size_of::<T>() <= size_of::<Self>()); // SAFETY: This type is repr(transparent) over `sockaddr_storage` and `T` is one of the // `sockaddr_*` types defined by this platform.
&mut *(selfas *mutSelfas *mut T)
}
}
/// The address of a socket. /// /// `SockAddr`s may be constructed directly to and from the standard library /// [`SocketAddr`], [`SocketAddrV4`], and [`SocketAddrV6`] types. #[derive(Clone)] pubstruct 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::os::fd::AsRawFd; /// /// use socket2::{SockAddr, SockAddrStorage, Socket, Domain, Type}; /// /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; /// /// // Initialise a `SocketAddr` by calling `getsockname(2)`. /// let mut addr_storage = SockAddrStorage::zeroed(); /// let mut len = addr_storage.size_of(); /// /// // The `getsockname(2)` system call will initialize `storage` for /// // us, setting `len` to the correct length. /// let res = unsafe { /// libc::getsockname( /// socket.as_raw_fd(), /// addr_storage.view_as(), /// &mut len, /// ) /// }; /// if res == -1 { /// return Err(io::Error::last_os_error()); /// } /// /// let address = unsafe { SockAddr::new(addr_storage, len) }; /// # drop(address); /// # } /// # Ok(()) /// # } /// ``` pubconstunsafefn new(storage: SockAddrStorage, len: socklen_t) -> SockAddr {
SockAddr {
storage: storage.storage,
len: len as socklen_t,
}
}
/// 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::fd::AsRawFd; /// /// use socket2::{SockAddr, Socket, Domain, Type}; /// /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; /// /// // Initialise a `SocketAddr` by calling `getsockname(2)`. /// let (_, address) = unsafe { /// SockAddr::try_init(|addr_storage, len| { /// // The `getsockname(2)` system call will initialize `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(()) /// # } /// ``` pubunsafefn try_init<F, T>(init: F) -> io::Result<(T, SockAddr)> where
F: FnOnce(*mut SockAddrStorage, *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. letmut storage = SockAddrStorage::zeroed(); letmut len = STORAGE_SIZE;
init(&mut storage, &mut len).map(|res| {
debug_assert!(len <= STORAGE_SIZE, "overflown address storage");
(res, SockAddr::new(storage, len))
})
}
/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. /// /// Returns an error if the path is longer than `SUN_LEN`. #[cfg(not(target_os = "wasi"))] pubfn 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. pubunsafefn set_length(&mutself, length: socklen_t) { self.len = length;
}
/// Returns this address's `Domain`. pubconstfn domain(&self) -> Domain {
Domain(self.storage.ss_family as c_int)
}
/// Returns the size of this address in bytes. pubconstfn len(&self) -> socklen_t { self.len
}
/// Returns a raw pointer to the address. pubconstfn as_ptr(&self) -> *const SockAddrStorage {
&self.storage as *const sockaddr_storage as *const SockAddrStorage
}
/// Returns the address as the storage. pubconstfn as_storage(self) -> SockAddrStorage {
SockAddrStorage {
storage: self.storage,
}
}
/// Returns true if this address is in the `AF_INET` (IPv4) family, false otherwise. pubconstfn 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. pubconstfn 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. #[cfg(not(target_os = "wasi"))] pubfn 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`. pubfn as_socket(&self) -> Option<SocketAddr> { ifself.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)))
} elseifself.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(any(unix, all(target_os = "wasi", not(target_env = "p1"))))]
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. pubfn as_socket_ipv4(&self) -> Option<SocketAddrV4> { matchself.as_socket() {
Some(SocketAddr::V4(addr)) => Some(addr),
_ => None,
}
}
/// Returns this address as a [`SocketAddrV6`] if it is in the `AF_INET6` /// family. pubfn as_socket_ipv6(&self) -> Option<SocketAddrV6> { matchself.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. Furthermore, we ensure we don't read any uninitialised // bytes by using `self.len`. unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len as usize) }
}
}
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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.