use std::{
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
os::fd::RawFd,
};
use crate::{control_message::zeroed_sockaddr_storage, interface::InterfaceName};
use self::sealed::{PrivateToken, SealedMC, SealedNA};
#[cfg(target_os = "linux")]
pub use self::linux::*;
#[cfg(target_os = "linux")]
mod linux;
pub(crate) mod sealed {
pub trait SealedNA {}
pub trait SealedMC {}
pub struct PrivateToken;
}
pub trait NetworkAddress: Sized + SealedNA {
#[doc(hidden)]
fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage;
#[doc(hidden)]
fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self>;
}
pub trait MulticastJoinable: NetworkAddress + SealedMC {
#[doc(hidden)]
fn join_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()>;
#[doc(hidden)]
fn leave_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()>;
}
impl SealedNA for SocketAddrV4 {}
impl NetworkAddress for SocketAddrV4 {
fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
const _: () = assert!(
std::mem::size_of::<libc::sockaddr_storage>()
>= std::mem::size_of::<libc::sockaddr_in>()
);
const _: () = assert!(
std::mem::align_of::<libc::sockaddr_storage>()
>= std::mem::align_of::<libc::sockaddr_in>()
);
let mut result = zeroed_sockaddr_storage();
let out = unsafe { &mut (*(&mut result as *mut _ as *mut libc::sockaddr_in)) };
out.sin_family = libc::AF_INET as _;
out.sin_port = u16::from_ne_bytes(self.port().to_be_bytes());
out.sin_addr = libc::in_addr {
s_addr: u32::from_ne_bytes(self.ip().octets()),
};
result
}
fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
const _: () = assert!(
std::mem::size_of::<libc::sockaddr_storage>()
>= std::mem::size_of::<libc::sockaddr_in>()
);
const _: () = assert!(
std::mem::align_of::<libc::sockaddr_storage>()
>= std::mem::align_of::<libc::sockaddr_in>()
);
if addr.ss_family != libc::AF_INET as _ {
return None;
}
let input = unsafe { &(*(&addr as *const _ as *const libc::sockaddr_in)) };
Some(SocketAddrV4::new(
Ipv4Addr::from(input.sin_addr.s_addr.to_ne_bytes()),
u16::from_be_bytes(input.sin_port.to_ne_bytes()),
))
}
}
impl SealedNA for SocketAddrV6 {}
impl NetworkAddress for SocketAddrV6 {
fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
const _: () = assert!(
std::mem::size_of::<libc::sockaddr_storage>()
>= std::mem::size_of::<libc::sockaddr_in6>()
);
const _: () = assert!(
std::mem::align_of::<libc::sockaddr_storage>()
>= std::mem::align_of::<libc::sockaddr_in6>()
);
let mut result = zeroed_sockaddr_storage();
let out = unsafe { &mut (*(&mut result as *mut _ as *mut libc::sockaddr_in6)) };
out.sin6_family = libc::AF_INET6 as _;
out.sin6_port = u16::from_ne_bytes(self.port().to_be_bytes());
out.sin6_addr = libc::in6_addr {
s6_addr: self.ip().octets(),
};
out.sin6_flowinfo = self.flowinfo();
out.sin6_scope_id = self.scope_id();
result
}
fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
const _: () = assert!(
std::mem::size_of::<libc::sockaddr_storage>()
>= std::mem::size_of::<libc::sockaddr_in6>()
);
const _: () = assert!(
std::mem::align_of::<libc::sockaddr_storage>()
>= std::mem::align_of::<libc::sockaddr_in6>()
);
if addr.ss_family != libc::AF_INET6 as _ {
return None;
}
let input = unsafe { &(*(&addr as *const _ as *const libc::sockaddr_in6)) };
Some(SocketAddrV6::new(
Ipv6Addr::from(input.sin6_addr.s6_addr),
u16::from_be_bytes(input.sin6_port.to_ne_bytes()),
input.sin6_flowinfo,
input.sin6_scope_id,
))
}
}
impl SealedNA for SocketAddr {}
impl NetworkAddress for SocketAddr {
fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
match self {
SocketAddr::V4(addr) => addr.to_sockaddr(PrivateToken),
SocketAddr::V6(addr) => addr.to_sockaddr(PrivateToken),
}
}
fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
match addr.ss_family as _ {
libc::AF_INET => Some(SocketAddr::V4(SocketAddrV4::from_sockaddr(
addr,
PrivateToken,
)?)),
libc::AF_INET6 => Some(SocketAddr::V6(SocketAddrV6::from_sockaddr(
addr,
PrivateToken,
)?)),
_ => None,
}
}
}