use super::mut_void;
use crate::platform::{htons, inet_ntop, inet_pton, ntohs};
use crate::{Error, Result};
use core::fmt;
use core::mem::{self, MaybeUninit};
use core::ptr;
use core::slice;
use core::str;
#[derive(Clone)]
pub struct SocketAddr {
addr: MaybeUninit<libc::sockaddr_storage>,
}
impl SocketAddr {
pub const fn uninit() -> Self {
Self {
addr: MaybeUninit::uninit(),
}
}
pub fn zeroed(family: i32) -> Self {
let mut addr = MaybeUninit::<libc::sockaddr_storage>::zeroed();
unsafe { addr.assume_init_mut() }.ss_family = family as u16;
Self { addr }
}
pub fn family(&self) -> i32 {
unsafe { self.addr.assume_init_ref() }.ss_family as i32
}
pub fn unix(path: &str) -> Result<Self> {
let mut un = libc::sockaddr_un {
sun_family: libc::AF_UNIX as u16,
sun_path: [0; 108],
};
if path.len() > 108 {
return Err(Error::new(libc::EINVAL));
}
unsafe {
ptr::copy_nonoverlapping(
path.as_ptr().cast::<libc::c_char>(),
un.sun_path.as_mut_ptr(),
path.len(),
);
}
Ok(Self::new(un))
}
pub fn inet(ip: &str, port: u16) -> Result<Self> {
let mut inet = unsafe { MaybeUninit::<libc::sockaddr_in>::zeroed().assume_init_read() };
inet.sin_family = libc::AF_INET as u16;
inet.sin_port = unsafe { htons(port) };
unsafe {
Self::inet_addr(libc::AF_INET, ip, mut_void(&mut inet.sin_addr))?;
}
Ok(Self::new(inet))
}
pub fn inet6(ip: &str, port: u16) -> Result<Self> {
let mut inet = unsafe { MaybeUninit::<libc::sockaddr_in6>::zeroed().assume_init_read() };
inet.sin6_family = libc::AF_INET6 as u16;
inet.sin6_port = unsafe { htons(port) };
unsafe {
Self::inet_addr(libc::AF_INET6, ip, mut_void(&mut inet.sin6_addr))?;
}
Ok(Self::new(inet))
}
pub fn get(&self) -> (&libc::sockaddr, libc::socklen_t) {
let addr = unsafe { self.addr.assume_init_ref() };
match addr.ss_family as i32 {
libc::AF_INET => (
unsafe { &*self.addr.as_ptr().cast::<libc::sockaddr>() },
mem::size_of::<libc::sockaddr_in>() as libc::socklen_t,
),
libc::AF_INET6 => (
unsafe { &*self.addr.as_ptr().cast::<libc::sockaddr>() },
mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t,
),
libc::AF_UNIX => (
unsafe { &*self.addr.as_ptr().cast::<libc::sockaddr>() },
mem::size_of::<libc::sockaddr_un>() as libc::socklen_t,
),
_ => panic!("only support AF_INET/AF_INET6/AF_UNIXDOMAIN"),
}
}
pub fn get_mut(&mut self) -> (&mut libc::sockaddr, libc::socklen_t) {
let addr = unsafe { self.addr.assume_init_ref() };
match addr.ss_family as i32 {
libc::AF_INET => (
unsafe { &mut *self.addr.as_mut_ptr().cast::<libc::sockaddr>() },
mem::size_of::<libc::sockaddr_in>() as libc::socklen_t,
),
libc::AF_INET6 => (
unsafe { &mut *self.addr.as_mut_ptr().cast::<libc::sockaddr>() },
mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t,
),
libc::AF_UNIX => (
unsafe { &mut *self.addr.as_mut_ptr().cast::<libc::sockaddr>() },
mem::size_of::<libc::sockaddr_un>() as libc::socklen_t,
),
_ => panic!("only support AF_INET/AF_INET6/AF_UNIXDOMAIN"),
}
}
pub fn get_uninit_mut(&mut self) -> (*mut libc::sockaddr, libc::socklen_t) {
(self.addr.as_mut_ptr().cast::<libc::sockaddr>(), mem::size_of_val(&self.addr) as libc::socklen_t)
}
fn new<T>(val: T) -> Self {
let mut addr = MaybeUninit::<libc::sockaddr_storage>::uninit();
unsafe {
addr.as_mut_ptr().cast::<T>().write(val);
}
Self { addr }
}
unsafe fn inet_addr(family: i32, src: &str, dst: *mut libc::c_void) -> Result<()> {
let mut name = [0_u8; 128];
if src.len() >= 128 {
return Err(Error::new(libc::EINVAL));
}
ptr::copy_nonoverlapping(src.as_ptr(), name.as_mut_ptr(), src.len());
let ret = inet_pton(family, name.as_ptr(), dst.cast::<u8>());
if ret == 1 {
Ok(())
} else {
Err(Error::last())
}
}
}
impl fmt::Debug for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let addr = unsafe { self.addr.assume_init_ref() };
match addr.ss_family as i32 {
libc::AF_INET => {
let addr = addr as *const _ as *const libc::sockaddr_in;
let mut buf = [0_u8; 24];
unsafe { inet_ntop(libc::AF_INET, addr.cast::<u8>(), buf.as_mut_ptr(), 24) };
let len = unsafe { libc::strlen(buf.as_ptr().cast::<i8>()) };
let ip = unsafe { str::from_utf8_unchecked(&buf[..len]) };
let port = unsafe { ntohs((*addr).sin_port) };
f.write_fmt(format_args!("inet:://{ip}:{port}"))
}
libc::AF_INET6 => {
let addr = addr as *const _ as *const libc::sockaddr_in6;
let mut buf = [0_u8; 128];
unsafe { inet_ntop(libc::AF_INET6, addr.cast::<u8>(), buf.as_mut_ptr(), 24) };
let len = unsafe { libc::strlen(buf.as_ptr().cast::<i8>()) };
let ip = unsafe { str::from_utf8_unchecked(&buf[..len]) };
let port = unsafe { ntohs((*addr).sin6_port) };
f.write_fmt(format_args!("inet6://{ip}:{port}"))
}
libc::AF_UNIX => {
let addr = unsafe { &*(addr as *const _ as *const libc::sockaddr_un) };
let mut len = addr.sun_path.len();
for (n, c) in addr.sun_path.iter().enumerate() {
if *c == 0 {
len = n;
break;
}
}
let path = addr.sun_path[..].as_ptr().cast::<u8>();
let path = unsafe { slice::from_raw_parts(path, len) };
let path = unsafe { str::from_utf8_unchecked(path) };
f.write_fmt(format_args!("unix:://{path}"))
}
_ => {
f.write_fmt(format_args!("unknown family {}", addr.ss_family))
}
}
}
}
impl fmt::Display for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}