use std::mem::{self, size_of, MaybeUninit};
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::{fmt, io};
use crate::sys::{
sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
AF_INET6,
};
#[cfg(windows)]
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH_u;
pub struct SockAddr {
storage: sockaddr_storage,
len: socklen_t,
}
impl SockAddr {
#[cfg_attr(unix, doc = "```")]
#[cfg_attr(not(unix), doc = "```ignore")]
pub unsafe fn 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;
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 {
storage: storage.assume_init(),
len,
};
(res, addr)
})
}
pub const fn family(&self) -> sa_family_t {
self.storage.ss_family
}
pub const fn len(&self) -> socklen_t {
self.len
}
pub const fn as_ptr(&self) -> *const sockaddr {
&self.storage as *const _ as *const _
}
#[cfg(all(unix, not(target_os = "redox")))]
pub(crate) const fn as_storage_ptr(&self) -> *const sockaddr_storage {
&self.storage
}
pub fn as_socket(&self) -> Option<SocketAddr> {
if self.storage.ss_family == AF_INET as sa_family_t {
let addr = unsafe { &*(&self.storage as *const _ as *const 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 {
let addr = unsafe { &*(&self.storage as *const _ as *const 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.u.sin6_scope_id()
},
)))
} else {
None
}
}
pub fn as_socket_ipv4(&self) -> Option<SocketAddrV4> {
match self.as_socket() {
Some(SocketAddr::V4(addr)) => Some(addr),
_ => None,
}
}
pub fn as_socket_ipv6(&self) -> Option<SocketAddrV6> {
match self.as_socket() {
Some(SocketAddr::V6(addr)) => Some(addr),
_ => None,
}
}
}
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 {
let sockaddr_in = sockaddr_in {
sin_family: AF_INET as sa_family_t,
sin_port: addr.port().to_be(),
sin_addr: crate::sys::to_in_addr(&addr.ip()),
sin_zero: Default::default(),
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
sin_len: 0,
};
let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
unsafe { (storage.as_mut_ptr() as *mut sockaddr_in).write(sockaddr_in) };
SockAddr {
storage: unsafe { storage.assume_init() },
len: mem::size_of::<sockaddr_in>() as socklen_t,
}
}
}
impl From<SocketAddrV6> for SockAddr {
fn from(addr: SocketAddrV6) -> SockAddr {
#[cfg(windows)]
let u = unsafe {
let mut u = mem::zeroed::<SOCKADDR_IN6_LH_u>();
*u.sin6_scope_id_mut() = addr.scope_id();
u
};
let sockaddr_in6 = sockaddr_in6 {
sin6_family: AF_INET6 as sa_family_t,
sin6_port: addr.port().to_be(),
sin6_addr: crate::sys::to_in6_addr(addr.ip()),
sin6_flowinfo: addr.flowinfo(),
#[cfg(unix)]
sin6_scope_id: addr.scope_id(),
#[cfg(windows)]
u,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
sin6_len: 0,
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
__sin6_src_id: 0,
};
let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
unsafe { (storage.as_mut_ptr() as *mut sockaddr_in6).write(sockaddr_in6) };
SockAddr {
storage: unsafe { storage.assume_init() },
len: mem::size_of::<sockaddr_in6>() as socklen_t,
}
}
}
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 = "openbsd",
target_os = "vxworks",
))]
f.field("ss_len", &self.storage.ss_len);
f.field("ss_family", &self.storage.ss_family)
.field("len", &self.len)
.finish()
}
}
#[test]
fn ipv4() {
use std::net::Ipv4Addr;
let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
let addr = SockAddr::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());
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());
}
#[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_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));
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));
}