use crate::keeper::{check_type, Access, OType, OidErr, OidKeeper};
use crate::usm::User;
use log::{debug, warn};
use num_traits::cast::ToPrimitive;
use rasn::types::{Integer, ObjectIdentifier, OctetString};
use rasn_smi::v2::{ApplicationSyntax, ObjectSyntax, SimpleSyntax};
use rasn_snmp::v3::{VarBind, VarBindValue};
pub const ROW_STATUS_ACTIVE: u32 = 1u32;
pub const ROW_STATUS_NOT_IN_SERVICE: u32 = 2u32;
pub const ROW_STATUS_NOT_READY: u32 = 3u32;
pub const ROW_STATUS_CREATE_AND_GO: u32 = 4u32;
pub const ROW_STATUS_CREATE_AND_WAIT: u32 = 5u32;
pub const ROW_STATUS_DESTROY: u32 = 6u32;
#[derive(PartialEq, Eq)]
pub struct TableMemOid {
pub rows: Vec<(Vec<u32>, Vec<ObjectSyntax>)>,
default_row: Vec<ObjectSyntax>,
cols: usize,
base: Vec<u32>,
pub otypes: Vec<OType>,
access: Vec<Access>,
index_cols: Vec<usize>,
pending: Vec<(Vec<u32>, VarBindValue)>,
implied_last: bool,
pub in_transaction: bool,
}
impl TableMemOid {
pub fn new(
default_row: Vec<ObjectSyntax>,
cols: usize,
base: &ObjectIdentifier,
otypes: Vec<OType>,
access: Vec<Access>,
index_cols: Vec<usize>,
implied_last: bool,
) -> Self {
assert_eq!(cols, otypes.len());
assert_eq!(cols, access.len());
assert_eq!(cols, default_row.len());
TableMemOid {
rows: vec![], default_row,
cols,
base: base.to_vec(),
otypes,
access,
index_cols,
pending: vec![],
implied_last,
in_transaction: false,
}
}
pub fn set_data(&mut self, data: Vec<Vec<ObjectSyntax>>) {
let mut row_data = Vec::new();
for row in data {
let idx = TableMemOid::index_imp(&self.index_cols, &row, self.implied_last);
row_data.push((idx, row));
}
row_data.sort_by(|a, b| a.0.cmp(&b.0));
self.rows = row_data;
}
#[allow(dead_code)]
pub fn set_indexed_data(&mut self, data: Vec<(Vec<u32>, Vec<ObjectSyntax>)>) {
let mut row_data = Vec::new();
for row in data {
row_data.push(row);
}
row_data.sort_by(|a, b| a.0.cmp(&b.0));
self.rows = row_data;
}
fn index_imp(icols: &[usize], row: &[ObjectSyntax], implied_last: bool) -> Vec<u32> {
let mut ret: Vec<u32> = Vec::new();
for (n, index_column_number) in icols.iter().enumerate() {
let col = &row[*index_column_number - 1];
debug!("Index construction col {col:?}");
match col {
ObjectSyntax::Simple(os) => match os {
SimpleSyntax::Integer(i) => {
let iopt = i.to_i64().unwrap(); let iu32: u32 = iopt.try_into().unwrap(); ret.push(iu32);
}
SimpleSyntax::String(s) => {
if !implied_last || n < icols.len() - 1 {
let sl: u32 = s.len().try_into().unwrap(); ret.push(sl);
}
for ir in s.iter().copied() {
let ui32: u32 = ir.into();
ret.push(ui32);
}
}
SimpleSyntax::ObjectId(o) => {
if !implied_last || n < icols.len() - 1 {
let ol: u32 = o.len().try_into().unwrap(); ret.push(ol);
}
for ui32 in o.iter().copied() {
ret.push(ui32);
}
}
},
ObjectSyntax::ApplicationWide(os) => match os {
ApplicationSyntax::Address(a) => {
let fixed = *a.0;
for b in fixed {
let bl: u32 = b.into();
ret.push(bl);
}
}
ApplicationSyntax::Counter(_) => {}
ApplicationSyntax::BigCounter(_) => {}
ApplicationSyntax::Ticks(t) => {
ret.push(t.0);
}
_ => {
panic!("Unsupported type in ApplicationWide index construction {os:?}")
}
},
}
}
ret
}
pub fn suffix(&self, oid: ObjectIdentifier) -> Vec<u32> {
let base_len = self.base.len();
if oid.len() > base_len {
oid.to_vec()[base_len..].to_vec()
} else {
vec![]
}
}
pub fn row_from_index(&self, idx: &[u32]) -> Vec<ObjectSyntax> {
let mut row: Vec<ObjectSyntax> = vec![];
let mut idx_idx = 0;
let num_idx_cols = self.index_cols.len();
for item in &self.default_row {
row.push(item.clone());
}
for (n, index_column_number) in self.index_cols.iter().enumerate() {
let col = self.otypes[*index_column_number - 1];
match col {
OType::Integer => {
row[*index_column_number - 1] =
ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(idx[idx_idx])));
idx_idx += 1;
}
OType::String => {
let mut text: Vec<u8> = vec![];
if self.implied_last && n == num_idx_cols - 1 {
for itemp in &idx[idx_idx..] {
let item = *itemp;
text.push(item.try_into().unwrap()); }
} else {
let slen: usize = idx[idx_idx].try_into().unwrap(); idx_idx += 1;
for itemp in &idx[idx_idx..(idx_idx + slen)] {
let item = *itemp;
text.push(item.try_into().unwrap()); }
}
row[*index_column_number - 1] =
ObjectSyntax::Simple(SimpleSyntax::String(OctetString::from_slice(&text)));
}
OType::ObjectId => {
let mut arc: Vec<u32> = vec![];
if self.implied_last && n == num_idx_cols - 1 {
for itemp in &idx[idx_idx..] {
let item = *itemp;
arc.push(item);
}
} else {
let slen: usize = idx[idx_idx].try_into().unwrap(); idx_idx += 1;
for itemp in &idx[idx_idx..(idx_idx + slen)] {
let item = *itemp;
arc.push(item);
}
}
row[*index_column_number - 1] = ObjectSyntax::Simple(SimpleSyntax::ObjectId(
ObjectIdentifier::new(arc).unwrap().to_owned(), ));
}
_ => {
panic!("Unsupported type in row construction from index")
}
}
}
for (n, otype) in self.otypes.iter().enumerate() {
if *otype == OType::RowStatus {
row[n] = ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
ROW_STATUS_NOT_READY,
)));
}
}
row
}
pub fn add_row(&mut self, row: &[ObjectSyntax]) {
let idx = TableMemOid::index_imp(&self.index_cols, row, self.implied_last);
self.rows.push((idx, row.to_owned()));
self.rows.sort_by(|a, b| a.0.cmp(&b.0));
}
fn make_oid(&self, col: usize, index: &[u32]) -> ObjectIdentifier {
let mut tmp = self.base.clone();
tmp.push(1u32); let c32: u32 = col.try_into().unwrap(); tmp.push(c32); for i in index {
tmp.push(*i); }
ObjectIdentifier::new(tmp).unwrap().to_owned() }
}
impl OidKeeper for TableMemOid {
fn is_scalar(&self, _oid: ObjectIdentifier) -> bool {
false
}
fn is_empty(&self) -> bool {
self.rows.is_empty()
}
fn get(&self, oid: ObjectIdentifier) -> Result<VarBindValue, OidErr> {
let suffix = self.suffix(oid);
debug!("Suffix is {suffix:?}");
if suffix.len() < 3 {
return Err(OidErr::NoSuchInstance);
}
if suffix[0] != 1u32 {
return Err(OidErr::NoSuchName);
}
if suffix[1] > 16384 {
return Err(OidErr::NoSuchName);
}
let col: usize = suffix[1] as usize;
if col == 0 || col > self.cols {
return Err(OidErr::NoSuchName);
}
let index = &suffix[2..];
debug!("Col {col} Index is {index:?}");
for row in &self.rows {
let r0 = &row.0;
debug!("Index {index:?} r0 {r0:?}");
if index == row.0 {
return Ok(VarBindValue::Value(row.1[col - 1].clone()));
}
}
Err(OidErr::NoSuchName)
}
fn get_next(&self, oid: ObjectIdentifier) -> Result<VarBind, OidErr> {
if self.rows.is_empty() {
return Err(OidErr::OutOfRange);
}
let suffix = self.suffix(oid.clone());
let mut skip_col = false;
let mut col: usize = if suffix.len() < 3 {
1
} else {
suffix[1] as usize
};
if col == 0 || col > self.cols {
return Err(OidErr::NoSuchName);
}
loop {
let acc = self.access[col - 1];
if acc == Access::ReadOnly || acc == Access::ReadWrite || acc == Access::ReadCreate {
break;
}
skip_col = true;
col += 1;
if col > self.cols {
return Err(OidErr::OutOfRange);
}
}
if suffix.len() >= 3 && !skip_col {
let res = self
.rows
.binary_search_by(|a| a.0.cmp(&suffix[2..].to_vec()));
match res {
Ok(idx) => {
if idx < self.rows.len() - 1 {
let (next_index, next_row) = &self.rows[idx + 1];
let value = VarBindValue::Value(next_row[col - 1].clone());
let name = self.make_oid(col, next_index);
return Ok(VarBind { name, value });
} else if col < self.cols {
col += 1;
let value = VarBindValue::Value(self.rows[0].1[col - 1].clone());
let name = self.make_oid(col, &self.rows[0].0);
return Ok(VarBind { name, value });
}
}
Err(insert_point) => {
if insert_point < self.rows.len() {
let (next_index, next_row) = &self.rows[insert_point];
let value = VarBindValue::Value(next_row[col - 1].clone());
let name = self.make_oid(col, next_index);
return Ok(VarBind { name, value });
} else if col < self.cols {
col += 1;
let value = VarBindValue::Value(self.rows[0].1[col - 1].clone());
let name = self.make_oid(col, &self.rows[0].0);
return Ok(VarBind { name, value });
}
}
}
debug!("Off end of table");
Err(OidErr::OutOfRange)
} else {
let row = &self.rows[0];
let value = VarBindValue::Value(row.1[col - 1].clone());
let name = self.make_oid(col, &row.0);
Ok(VarBind { name, value })
}
}
fn access(&self, oid: ObjectIdentifier) -> Access {
let suffix = self.suffix(oid);
if suffix.len() < 2 {
return Access::NoAccess;
}
if suffix[0] != 1u32 {
return Access::NoAccess;
}
if suffix[1] > 16384 {
return Access::NoAccess;
}
let col: usize = suffix[1] as usize;
if col == 0 || col > self.cols {
return Access::NoAccess;
}
self.access[col - 1]
}
fn begin_transaction(&mut self) -> Result<(), OidErr> {
if self.in_transaction {
warn!("Begin transaction - but already in transaction!");
return Err(OidErr::GenErr);
}
self.pending.clear(); self.in_transaction = true;
Ok(())
}
fn set(
&mut self,
oid: ObjectIdentifier,
value: VarBindValue,
_user: &User,
) -> Result<VarBindValue, OidErr> {
if !self.in_transaction {
warn!("Not in transaction in set");
return Err(OidErr::GenErr);
}
let suffix = self.suffix(oid);
debug!("Suffix is {suffix:?}");
if suffix.len() < 3 {
return Err(OidErr::NoSuchInstance);
}
if suffix[0] != 1u32 {
return Err(OidErr::NoSuchName);
}
if suffix[1] > 16384 {
return Err(OidErr::NoSuchName);
}
let col: usize = suffix[1] as usize;
if col == 0 || col > self.cols {
return Err(OidErr::NoSuchName);
}
match self.access[col - 1] {
Access::NoAccess | Access::NotificationOnly | Access::ReadOnly => {
return Err(OidErr::NotWritable);
}
_ => {}
}
if let VarBindValue::Value(new_value) = value.clone() {
if !check_type(self.otypes[col - 1], &new_value) {
return Err(OidErr::WrongType);
}
}
let index = &suffix[2..];
let s_res = self.rows.binary_search_by(|a| a.0.cmp(&index.to_vec()));
if s_res.is_err() {
for (pidx, _) in &self.pending {
if pidx[2..] == *index {
self.pending.push((suffix, value.clone()));
return Ok(value);
}
}
if let VarBindValue::Value(new_value) = value.clone() {
if self.otypes[col - 1] == OType::RowStatus
&& new_value
== ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
ROW_STATUS_CREATE_AND_WAIT,
)))
{
self.pending.push((suffix, value.clone()));
return Ok(value);
}
}
return Err(OidErr::NoSuchInstance);
}
self.pending.push((suffix, value.clone()));
Ok(value)
}
fn commit(&mut self, _user: &User) -> Result<(), OidErr> {
if !self.in_transaction {
warn!("Commit - but not in transaction!");
}
self.in_transaction = false;
let pending = self.pending.clone();
for (suffix, value) in pending {
let col: usize = suffix[1] as usize;
let index = &suffix[2..];
let mut delete_me = false;
let mut delete_idx: usize = 0;
let s_res = self.rows.binary_search_by(|a| a.0.cmp(&index.to_vec()));
match s_res {
Ok(sidx) => {
let row = &mut self.rows[sidx];
if let VarBindValue::Value(new_value) = value.clone() {
if check_type(self.otypes[col - 1], &new_value) {
if self.otypes[col - 1] == OType::RowStatus {
if new_value
== ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
ROW_STATUS_DESTROY,
)))
{
delete_me = true;
delete_idx = sidx;
} else if new_value
== ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
ROW_STATUS_ACTIVE,
)))
|| new_value
== ObjectSyntax::Simple(SimpleSyntax::Integer(
Integer::from(ROW_STATUS_NOT_IN_SERVICE),
))
{
continue;
} else {
return Err(OidErr::WrongType);
}
} else {
row.1[col - 1] = new_value;
continue;
}
} else {
return Err(OidErr::WrongType);
}
}
if delete_me {
self.rows.remove(delete_idx);
continue;
}
}
Err(_) => {
warn!("index not matched in set {index:?}");
if let VarBindValue::Value(new_value) = value.clone() {
if self.otypes[col - 1] == OType::RowStatus {
if new_value
== ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
ROW_STATUS_CREATE_AND_WAIT,
)))
{
let row: Vec<ObjectSyntax> = self.row_from_index(index);
self.add_row(&row);
continue;
}
if new_value
== ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(
ROW_STATUS_CREATE_AND_GO,
)))
{
warn!["CreateAndGo not supported"];
return Err(OidErr::CommitFail);
}
}
}
}
}
}
self.pending.clear();
Ok(())
}
fn rollback(&mut self) -> Result<(), OidErr> {
if !self.in_transaction {
warn!("Rollback - but not in transaction!");
}
self.in_transaction = false;
self.pending.clear();
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::{Access, OidErr, TableMemOid};
use crate::perms::{Perm, Rule};
use crate::utils::*;
use rasn::types::{Integer, ObjectIdentifier};
use rasn_smi::v2::{ObjectSyntax, SimpleSyntax};
use rasn_snmp::v3::VarBindValue;
const ARC2: [u32; 2] = [1, 6];
const ARC3: [u32; 5] = [1, 6, 1, 2, 1];
#[test]
fn test_simple_from_int() {
let x = simple_from_int(21);
assert_eq!(
x,
ObjectSyntax::Simple(SimpleSyntax::Integer(Integer::from(21)))
);
}
fn perms() -> Vec<Perm> {
let rules = vec![Rule {
read: true,
write: true,
context: None,
include: vec![vec![1u32]],
exclude: vec![],
}];
vec![Perm {
rules,
security_level: 1u8, group_name: "test".as_bytes().to_vec(),
}]
}
fn user_fixture<'a>(pv: &'a Vec<Perm>) -> User {
let s ="test test sha1 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b aes 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c";
let u = User::from_str(s, pv).unwrap(); u
}
fn tab_fixture() -> TableMemOid {
let oid2: ObjectIdentifier = ObjectIdentifier::new(&ARC2).unwrap(); let first = simple_from_str(b"abc");
let last = simple_from_str(b"xyz");
let blank = simple_from_str(b"");
let s0 = simple_from_int(0);
let s42 = simple_from_int(42);
let s41 = simple_from_int(41);
let s4 = simple_from_int(4);
let s5 = simple_from_int(5);
let mut tab = TableMemOid::new(
vec![blank.clone(), s0.clone(), s0.clone()],
3,
&oid2,
vec![OType::String, OType::Integer, OType::Integer],
vec![Access::ReadOnly, Access::ReadOnly, Access::ReadWrite],
vec![1usize, 2usize],
false,
);
tab.set_data(vec![
vec![first.clone(), s4.clone(), s41.clone()],
vec![last.clone(), s5.clone(), s42.clone()],
]);
tab
}
#[test]
fn tab_get_test() {
let tab = tab_fixture();
let oid2: ObjectIdentifier = ObjectIdentifier::new(&ARC2).unwrap(); let res = tab.get(oid2);
assert_eq!(res, Err(OidErr::NoSuchInstance));
let o3 = ObjectIdentifier::new(&[1, 6, 1, 2, 3, 120, 121, 122, 5]).unwrap(); let res = tab.get(o3);
assert!(res.is_ok());
let s5 = simple_from_int(5);
assert_eq!(res.unwrap(), VarBindValue::Value(s5)); let o4 = ObjectIdentifier::new(&[1, 6, 1, 3, 3, 120, 121, 122, 5]).unwrap(); let res = tab.get(o4);
assert!(res.is_ok());
let s42 = simple_from_int(42);
assert_eq!(res.unwrap(), VarBindValue::Value(s42)); }
#[test]
fn tab_get_next_test() {
let oid2: ObjectIdentifier = ObjectIdentifier::new(&ARC2).unwrap(); let tab = tab_fixture();
let res = tab.get_next(oid2);
assert!(res.is_ok());
let o3 = ObjectIdentifier::new(&[1, 6, 1, 1]).unwrap(); let res = tab.get_next(o3);
assert!(res.is_ok());
let vb = res.unwrap(); let o4 = ObjectIdentifier::new(&[1, 6, 1, 1, 3, 97, 98, 99, 4]).unwrap(); assert_eq!(vb.name, o4);
let o4 = ObjectIdentifier::new(&[1, 6, 1, 1, 5]).unwrap(); let res = tab.get_next(o4);
assert!(res.is_ok());
let vb = res.unwrap(); let o5 = ObjectIdentifier::new(&[1, 6, 1, 2, 4]).unwrap(); assert_eq!(vb.value, VarBindValue::Value(simple_from_int(4)));
let res = tab.get_next(o5);
assert!(res.is_ok());
let s41 = simple_from_int(41);
let vb = res.unwrap(); assert_eq!(vb.value, VarBindValue::Value(s41));
let ol = ObjectIdentifier::new(&[1, 6, 1, 3, 5]).unwrap(); let res = tab.get_next(ol);
assert!(res.is_err());
}
#[test]
fn test_add_row() {
let mut tab = tab_fixture();
assert_eq!(tab.rows.len(), 2);
let s6 = simple_from_int(6);
let s37 = simple_from_int(37);
let row = vec![s6, s37];
tab.add_row(&row);
assert_eq!(tab.rows.len(), 3);
}
#[test]
fn test_access() {
let tab = tab_fixture();
let o1 = ObjectIdentifier::new(&[1, 6, 1]).unwrap(); assert_eq!(tab.access(o1), Access::NoAccess);
let o2 = ObjectIdentifier::new(&[1, 6, 5, 1]).unwrap(); assert_eq!(tab.access(o2), Access::NoAccess);
let o3 = ObjectIdentifier::new(&[1, 6, 1, 16385]).unwrap(); assert_eq!(tab.access(o3), Access::NoAccess);
let o4 = ObjectIdentifier::new(&[1, 6, 1, 0]).unwrap(); assert_eq!(tab.access(o4), Access::NoAccess);
let o5 = ObjectIdentifier::new(&[1, 6, 1, 2, 4]).unwrap(); assert_eq!(tab.access(o5), Access::ReadOnly);
}
#[test]
fn test_create_and_wait_obj() {
let oid2: ObjectIdentifier = ObjectIdentifier::new(&ARC2).unwrap(); let oid3: ObjectIdentifier = ObjectIdentifier::new(&ARC3).unwrap(); let s1 = simple_from_int(1);
let nr = simple_from_str(b"four");
let s5 = simple_from_int(5);
let ov = simple_from_vec(&[1, 1]);
let mut tab = TableMemOid::new(
vec![ov.clone(), nr.clone()],
2,
&oid2,
vec![OType::ObjectId, OType::RowStatus],
vec![Access::ReadOnly, Access::ReadWrite],
vec![1usize],
true,
);
let pv = perms();
let user = user_fixture(&pv);
assert_eq!(tab.rows.len(), 0);
assert!(tab.begin_transaction().is_ok());
let set_res = tab.set(oid3.clone(), VarBindValue::Value(s5.clone()), &user);
assert!(set_res.is_ok());
assert_eq!(tab.rows.len(), 0);
assert!(tab.commit(&user).is_ok());
assert_eq!(tab.rows.len(), 1);
assert!(tab.begin_transaction().is_ok());
let set_res = tab.set(oid3.clone(), VarBindValue::Value(s1.clone()), &user);
assert!(set_res.is_ok());
assert_eq!(tab.rows.len(), 1);
assert!(tab.commit(&user).is_ok());
assert!(tab.begin_transaction().is_ok());
let set_res = tab.set(oid3.clone(), VarBindValue::Value(nr.clone()), &user);
assert_eq!(set_res, Err(OidErr::WrongType));
assert!(tab.rollback().is_ok());
}
#[test]
fn test_create_and_wait2() {
let oid2: ObjectIdentifier = ObjectIdentifier::new(&ARC2).unwrap(); let oid3: ObjectIdentifier = ObjectIdentifier::new(&ARC3).unwrap(); let s1 = simple_from_int(1);
let nr = simple_from_str(b"four");
let s5 = simple_from_int(5);
let pv = perms();
let user = user_fixture(&pv);
let mut tab = TableMemOid::new(
vec![s1.clone(), nr.clone()],
2,
&oid2,
vec![OType::Integer, OType::RowStatus],
vec![Access::ReadOnly, Access::ReadWrite],
vec![1usize],
false,
);
assert_eq!(tab.rows.len(), 0);
assert!(tab.begin_transaction().is_ok());
let set_res = tab.set(oid3.clone(), VarBindValue::Value(s5.clone()), &user);
assert!(set_res.is_ok());
assert_eq!(tab.rows.len(), 0);
assert!(tab.commit(&user).is_ok());
assert_eq!(tab.rows.len(), 1);
assert!(tab.begin_transaction().is_ok());
let set_res = tab.set(oid3.clone(), VarBindValue::Value(s1.clone()), &user);
assert!(set_res.is_ok());
assert_eq!(tab.rows.len(), 1);
assert!(tab.commit(&user).is_ok());
assert!(tab.begin_transaction().is_ok());
let set_res = tab.set(oid3.clone(), VarBindValue::Value(nr.clone()), &user);
assert_eq!(set_res, Err(OidErr::WrongType));
assert!(tab.rollback().is_ok());
}
#[test]
fn test_foreign_table() {
let mut tab = tab_fixture();
assert_eq!(tab.rows.len(), 2);
let s1 = simple_from_int(1);
let name = simple_from_str(b"name");
let data = vec![(vec![1u32], vec![name, s1.clone(), s1.clone()])];
tab.set_indexed_data(data);
assert_eq!(tab.rows.len(), 1);
}
}