use std::{convert::TryFrom, fmt, marker::PhantomData, mem, slice, str};
use errno::Errno;
use libc;
use {AttrDataType, CbResult, CbStatus, Msghdr, Result};
#[repr(C)]
pub struct Attr<'a> {
pub nla_len: u16,
pub nla_type: u16,
_nlh: PhantomData<Msghdr<'a>>,
}
impl<'a> Attr<'a> {
pub const HDRLEN: usize =
((mem::size_of::<Self>() + crate::ALIGNTO - 1) & !(crate::ALIGNTO - 1));
pub fn atype(&self) -> u16 {
self.nla_type & libc::NLA_TYPE_MASK as u16
}
pub fn payload_len(&self) -> u16 {
self.nla_len - Self::HDRLEN as u16
}
unsafe fn payload_raw<T>(&self) -> &T {
&(*((self as *const _ as *const u8).offset(Self::HDRLEN as isize) as *const T))
}
unsafe fn payload_ptr(&self) -> *const u8 {
(self as *const _ as *const u8).offset(Self::HDRLEN as isize)
}
pub unsafe fn payload_raw_mut<T>(&mut self) -> &mut T {
&mut (*((self as *mut _ as *mut u8).offset(Self::HDRLEN as isize) as *mut T))
}
pub fn ok(&self, len: isize) -> bool {
len > Self::HDRLEN as isize
&& self.nla_len as usize >= Self::HDRLEN
&& self.nla_len as isize <= len
}
pub unsafe fn next(&self) -> &Self {
&*((self as *const _ as *const u8).offset(crate::align(self.nla_len as usize) as isize)
as *const Self)
}
pub fn type_valid(&self, max: u16) -> Result<()> {
if self.atype() > max {
return Err(Errno(libc::EOPNOTSUPP));
}
Ok(())
}
pub fn type_valid2(&self, max: impl Into<u16>) -> Result<()> {
if self.atype() > max.into() {
return Err(Errno(libc::EOPNOTSUPP));
}
Ok(())
}
pub fn as_intptr(&self) -> libc::intptr_t {
self as *const _ as libc::intptr_t
}
}
fn data_type_len(atype: AttrDataType) -> u16 {
match atype {
AttrDataType::U8 => mem::size_of::<u8>() as u16,
AttrDataType::U16 => mem::size_of::<u16>() as u16,
AttrDataType::U32 => mem::size_of::<u32>() as u16,
AttrDataType::U64 => mem::size_of::<u64>() as u16,
AttrDataType::MSecs => mem::size_of::<u64>() as u16,
_ => 0,
}
}
impl<'a> Attr<'a> {
fn _validate(&self, atype: AttrDataType, exp_len: u16) -> Result<()> {
let attr_len = self.payload_len();
if attr_len < exp_len {
return Err(Errno(libc::ERANGE));
}
match atype {
AttrDataType::Flag => {
if attr_len > 0 {
return Err(Errno(libc::ERANGE));
}
}
AttrDataType::NulString => {
if attr_len == 0 {
return Err(Errno(libc::ERANGE));
}
if unsafe {
*(self.payload_raw() as *const _ as *const u8).offset((attr_len - 1) as isize)
!= 0
} {
return Err(Errno(libc::EINVAL));
}
}
AttrDataType::String => {
if attr_len == 0 {
return Err(Errno(libc::ERANGE));
}
}
AttrDataType::Nested => {
if attr_len != 0 && attr_len < Self::HDRLEN as u16 {
return Err(Errno(libc::ERANGE));
}
}
_ => {}
}
if exp_len != 0 && attr_len > exp_len {
return Err(Errno(libc::ERANGE));
}
Ok(())
}
pub fn validate(&self, atype: AttrDataType) -> Result<()> {
self._validate(atype, data_type_len(atype))
}
pub fn validate2<T: Sized>(&self, atype: AttrDataType) -> Result<()> {
self._validate(atype, mem::size_of::<T>() as u16)
}
}
pub struct NestAttr<'a> {
head: &'a Attr<'a>,
cur: &'a Attr<'a>,
}
impl<'a> NestAttr<'a> {
pub fn new(attr: &'a Attr<'a>) -> Self {
Self {
head: attr,
cur: unsafe { attr.payload_raw::<Attr>() },
}
}
pub fn next(&mut self) -> Option<&'a Attr<'a>> {
if self.cur.ok(
unsafe { self.head.payload_raw::<u8>() } as *const _ as isize
+ self.head.payload_len() as isize
- self.cur as *const _ as isize,
) {
let ret = Some(self.cur);
self.cur = unsafe { self.cur.next() };
ret
} else {
None
}
}
}
impl<'a> Attr<'a> {
pub fn parse_nested<T: FnMut(&'a Self) -> CbResult>(&'a self, mut cb: T) -> CbResult {
let attr_len = self.payload_len();
if attr_len != 0 && attr_len < Self::HDRLEN as u16 {
return crate::gen_errno!(libc::ERANGE);
}
let mut ret: CbResult = crate::gen_errno!(libc::ENOENT);
let mut nested = NestAttr::new(self);
while let Some(attr) = nested.next() {
ret = cb(attr);
match ret {
Ok(CbStatus::Ok) => {}
_ => return ret,
}
}
ret
}
}
impl<'a> Attr<'a> {
pub fn value<T: Copy>(&self) -> Result<T> {
Ok(*(self.value_ref::<T>()?))
}
pub fn value_ref<T>(&self) -> Result<&T> {
if mem::size_of::<T>() > self.payload_len() as usize {
return Err(Errno(libc::ERANGE));
}
unsafe { Ok(self.payload_raw::<T>()) }
}
pub fn str(&self) -> Result<&str> {
let attr_len = self.payload_len() as usize;
if attr_len == 0 {
return Err(Errno(libc::ERANGE));
}
let s = unsafe { slice::from_raw_parts(self.payload_ptr(), attr_len) };
str::from_utf8(s).map_err(|_| Errno(libc::EILSEQ))
}
pub fn cstr(&self) -> Result<&str> {
let pptr = unsafe { self.payload_ptr() };
let attr_len = self.payload_len() as usize;
if attr_len == 0 {
return Err(Errno(libc::ERANGE));
}
if unsafe { *pptr.offset((attr_len - 1) as isize) } != 0 {
return Err(Errno(libc::EINVAL));
}
let s = unsafe { slice::from_raw_parts(pptr, attr_len - 1) };
str::from_utf8(s).map_err(|_| Errno(libc::EILSEQ))
}
pub fn bytes_ref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self.value_ref::<u8>().unwrap(), self.payload_len() as usize)
}
}
}
pub trait AttrTbl<'a>: std::marker::Sized {
type Index: std::convert::TryFrom<u16, Error = Errno>;
fn new() -> Self;
fn _set(&mut self, index: Self::Index, attr: &'a Attr);
fn try_from_nlmsg(offset: usize, nlh: &'a Msghdr) -> Result<Self> {
let mut tb = Self::new();
nlh.parse(offset, |attr: &Attr| {
tb._set(Self::Index::try_from(attr.atype())?, attr);
Ok(CbStatus::Ok)
})
.map_err(|err| {
if let Some(e) = err.downcast_ref::<Errno>() {
*e
} else {
unreachable!()
}
})?;
Ok(tb)
}
fn try_from_nest(nest: &'a Attr) -> Result<Self> {
nest.validate(crate::AttrDataType::Nested)?;
let mut tb = Self::new();
nest.parse_nested(|attr: &Attr| {
tb._set(Self::Index::try_from(attr.atype())?, attr);
Ok(CbStatus::Ok)
})
.map_err(|err| {
if let Some(e) = err.downcast_ref::<Errno>() {
*e
} else {
unreachable!()
}
})?;
Ok(tb)
}
fn add(&mut self, attr: &'a Attr<'a>, count: &mut usize) -> CbResult {
let _ = Self::Index::try_from(attr.atype()).map(|atype| {
self._set(atype, attr);
*count += 1;
});
Ok(CbStatus::Ok)
}
fn from_nlmsg(offset: usize, nlh: &'a Msghdr) -> Result<Self> {
let mut tb = Self::new();
let mut count = 0;
nlh.parse(offset, |attr: &Attr| tb.add(attr, &mut count))
.map_err(|err| {
if let Some(e) = err.downcast_ref::<Errno>() {
*e
} else {
unreachable!()
}
})?;
if count == 0 {
Err(Errno(libc::ENOENT))
} else {
Ok(tb)
}
}
fn from_nest(nest: &'a Attr) -> Result<Self> {
nest.validate(crate::AttrDataType::Nested)?;
let mut tb = Self::new();
let mut count = 0;
nest.parse_nested(|attr: &Attr| tb.add(attr, &mut count))
.map_err(|err| {
if let Some(e) = err.downcast_ref::<Errno>() {
*e
} else {
unreachable!()
}
})?;
if count == 0 {
Err(Errno(libc::ENOENT))
} else {
Ok(tb)
}
}
}
impl<'a> Attr<'a> {
pub fn nest_array<T: AttrTbl<'a>>(&'a self) -> Result<Vec<T>> {
let mut v = Vec::new();
self.parse_nested(|nest| {
v.push(T::from_nest(nest)?);
Ok(CbStatus::Ok)
})
.map_err(|err| {
if let Some(e) = err.downcast_ref::<Errno>() {
*e
} else {
unreachable!()
}
})?;
Ok(v)
}
}
impl<'a> fmt::Debug for Attr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"nla_len: {}, nla_type: {} ({})",
self.nla_len,
self.atype(),
self.nla_type
)?;
if self.nla_type & libc::NLA_F_NESTED as u16 != 0 {
write!(f, ", NESTED")?;
}
if self.nla_type & libc::NLA_F_NET_BYTEORDER as u16 != 0 {
write!(f, ", NET_BYTEORDER")?;
}
Ok(())
}
}