use std::cmp::Ordering;
use std::ffi::{CStr, CString};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::ffi::scamper_addr::{
self, ScamperAddrT,
SCAMPER_ADDR_TYPE_IPV4, SCAMPER_ADDR_TYPE_IPV6,
};
pub struct ScamperAddr {
inner: *mut ScamperAddrT,
}
impl ScamperAddr {
pub(crate) unsafe fn from_ptr(ptr: *mut ScamperAddrT) -> Option<Self> {
if ptr.is_null() {
return None;
}
let ptr = unsafe { scamper_addr::scamper_addr_use(ptr) };
Some(ScamperAddr { inner: ptr })
}
pub fn from_str_typed(kind: i32, addr: &str) -> Option<Self> {
let s = CString::new(addr).ok()?;
let ptr = unsafe { scamper_addr::scamper_addr_fromstr(kind, s.as_ptr()) };
if ptr.is_null() {
None
} else {
Some(ScamperAddr { inner: ptr })
}
}
pub fn from_str(addr: &str) -> Option<Self> {
Self::from_str_typed(SCAMPER_ADDR_TYPE_IPV4, addr)
.or_else(|| Self::from_str_typed(SCAMPER_ADDR_TYPE_IPV6, addr))
}
pub fn addr_type(&self) -> i32 {
unsafe { scamper_addr::scamper_addr_type_get(self.inner) }
}
pub fn is_ipv4(&self) -> bool {
unsafe { scamper_addr::scamper_addr_isipv4(self.inner) != 0 }
}
pub fn is_ipv6(&self) -> bool {
unsafe { scamper_addr::scamper_addr_isipv6(self.inner) != 0 }
}
pub fn is_ethernet(&self) -> bool {
unsafe { scamper_addr::scamper_addr_isethernet(self.inner) != 0 }
}
pub fn is_linklocal(&self) -> bool {
unsafe { scamper_addr::scamper_addr_islinklocal(self.inner) != 0 }
}
pub fn is_rfc1918(&self) -> bool {
unsafe { scamper_addr::scamper_addr_isrfc1918(self.inner) != 0 }
}
pub fn is_unicast(&self) -> bool {
unsafe { scamper_addr::scamper_addr_isunicast(self.inner) != 0 }
}
pub fn is_6to4(&self) -> bool {
unsafe { scamper_addr::scamper_addr_is6to4(self.inner) != 0 }
}
pub fn is_reserved(&self) -> bool {
unsafe { scamper_addr::scamper_addr_isreserved(self.inner) != 0 }
}
pub fn packed(&self) -> Vec<u8> {
let len = unsafe { scamper_addr::scamper_addr_len_get(self.inner) };
let ptr = unsafe { scamper_addr::scamper_addr_addr_get(self.inner) };
if ptr.is_null() || len == 0 {
return Vec::new();
}
unsafe { std::slice::from_raw_parts(ptr as *const u8, len).to_vec() }
}
pub fn to_ipaddr(&self) -> Option<IpAddr> {
if self.is_ipv4() {
let bytes = self.packed();
if bytes.len() == 4 {
let arr: [u8; 4] = bytes.try_into().ok()?;
return Some(IpAddr::V4(Ipv4Addr::from(arr)));
}
} else if self.is_ipv6() {
let bytes = self.packed();
if bytes.len() == 16 {
let arr: [u8; 16] = bytes.try_into().ok()?;
return Some(IpAddr::V6(Ipv6Addr::from(arr)));
}
}
None
}
}
impl Drop for ScamperAddr {
fn drop(&mut self) {
unsafe { scamper_addr::scamper_addr_free(self.inner) };
}
}
impl Clone for ScamperAddr {
fn clone(&self) -> Self {
let ptr = unsafe { scamper_addr::scamper_addr_use(self.inner) };
ScamperAddr { inner: ptr }
}
}
impl fmt::Display for ScamperAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buf = vec![0u8; 64];
let ptr = unsafe {
scamper_addr::scamper_addr_tostr(
self.inner,
buf.as_mut_ptr() as *mut libc::c_char,
buf.len(),
)
};
if ptr.is_null() {
write!(f, "<invalid>")
} else {
let s = unsafe { CStr::from_ptr(ptr) };
write!(f, "{}", s.to_string_lossy())
}
}
}
impl fmt::Debug for ScamperAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ScamperAddr({})", self)
}
}
impl PartialEq for ScamperAddr {
fn eq(&self, other: &Self) -> bool {
unsafe { scamper_addr::scamper_addr_cmp(self.inner, other.inner) == 0 }
}
}
impl Eq for ScamperAddr {}
impl PartialOrd for ScamperAddr {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ScamperAddr {
fn cmp(&self, other: &Self) -> Ordering {
let r = unsafe { scamper_addr::scamper_addr_cmp(self.inner, other.inner) };
r.cmp(&0)
}
}
impl Hash for ScamperAddr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.packed().hash(state);
self.addr_type().hash(state);
}
}
unsafe impl Send for ScamperAddr {}
unsafe impl Sync for ScamperAddr {}