use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::{
convert::TryInto,
fmt,
io::{Error, ErrorKind},
};
use crate::core::{SMBiosData, SMBiosVersion};
pub struct WinSMBiosData {
windows_header: Vec<u8>,
pub smbios_data: SMBiosData,
}
impl WinSMBiosData {
pub const USED20_CALLING_METHOD_OFFSET: usize = 0usize;
pub const SMBIOS_MAJOR_VERSION_OFFSET: usize = 1usize;
pub const SMBIOS_MINOR_VERSION_OFFSET: usize = 2usize;
pub const DMI_REVISION_OFFSET: usize = 3usize;
pub const TABLE_DATA_LENGTH_OFFSET: usize = 4usize;
pub const SMBIOS_TABLE_DATA_OFFSET: usize = 8usize;
pub fn new(raw_smbios_data: Vec<u8>) -> Result<WinSMBiosData, Error> {
if !WinSMBiosData::is_valid_win_smbios_data(&raw_smbios_data) {
Err(Error::new(
ErrorKind::InvalidData,
"Invalid WinSMBiosData structure",
))
} else {
let windows_header =
Vec::from(&raw_smbios_data[..WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET]);
let version = WinSMBiosData::version_from_raw_header(&windows_header);
Ok(WinSMBiosData {
windows_header,
smbios_data: {
SMBiosData::from_vec_and_version(
Vec::from(&raw_smbios_data[WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET..]),
Some(version),
)
},
})
}
}
pub fn is_valid_win_smbios_data(raw_data: &Vec<u8>) -> bool {
let length = raw_data.len();
if length <= WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET {
return false;
}
let slice = raw_data
.get(
WinSMBiosData::TABLE_DATA_LENGTH_OFFSET
..WinSMBiosData::TABLE_DATA_LENGTH_OFFSET + 4,
)
.unwrap();
let table_data_length = u32::from_le_bytes(
slice
.try_into()
.expect("array length does not match type width"),
) as usize;
table_data_length == length - WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET
}
pub fn raw_smbios_data(&self) -> &[u8] {
self.windows_header.as_slice()
}
pub fn used20_calling_method(&self) -> u8 {
self.windows_header[WinSMBiosData::USED20_CALLING_METHOD_OFFSET]
}
pub fn smbios_major_version(&self) -> u8 {
self.windows_header[WinSMBiosData::SMBIOS_MAJOR_VERSION_OFFSET]
}
pub fn smbios_minor_version(&self) -> u8 {
self.windows_header[WinSMBiosData::SMBIOS_MINOR_VERSION_OFFSET]
}
pub fn dmi_revision(&self) -> u8 {
self.windows_header[WinSMBiosData::DMI_REVISION_OFFSET]
}
fn version_from_raw_header(windows_header: &Vec<u8>) -> SMBiosVersion {
SMBiosVersion {
major: windows_header[WinSMBiosData::SMBIOS_MAJOR_VERSION_OFFSET],
minor: windows_header[WinSMBiosData::SMBIOS_MINOR_VERSION_OFFSET],
revision: windows_header[WinSMBiosData::DMI_REVISION_OFFSET],
}
}
pub fn table_data_length(&self) -> u32 {
let slice = self
.windows_header
.get(
WinSMBiosData::TABLE_DATA_LENGTH_OFFSET
..WinSMBiosData::TABLE_DATA_LENGTH_OFFSET + 4,
)
.unwrap();
u32::from_le_bytes(
slice
.try_into()
.expect("array length does not match type width"),
)
}
}
impl fmt::Debug for WinSMBiosData {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<WinSMBiosData>())
.field("used20_calling_method", &self.used20_calling_method())
.field("smbios_major_version", &self.smbios_major_version())
.field("smbios_minor_version", &self.smbios_minor_version())
.field("dmi_revision", &self.dmi_revision())
.field("table_data_length", &self.table_data_length())
.field("smbios_data", &self.smbios_data)
.finish()
}
}
impl Serialize for WinSMBiosData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("WinSMBiosData", 6)?;
state.serialize_field("used20_calling_method", &self.used20_calling_method())?;
state.serialize_field("smbios_major_version", &self.smbios_major_version())?;
state.serialize_field("smbios_minor_version", &self.smbios_minor_version())?;
state.serialize_field("dmi_revision", &self.dmi_revision())?;
state.serialize_field("table_data_length", &self.table_data_length())?;
state.serialize_field("smbios_data", &self.smbios_data)?;
state.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_valid_raw_smbios_data() {
let struct_data = vec![0x00u8, 0x03, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAB];
assert!(WinSMBiosData::is_valid_win_smbios_data(&struct_data));
let struct_data = vec![0x00u8, 0x03, 0x03];
assert!(!WinSMBiosData::is_valid_win_smbios_data(&struct_data));
let struct_data = vec![0x00u8, 0x03, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xAB];
assert!(!WinSMBiosData::is_valid_win_smbios_data(&struct_data));
}
#[test]
fn test_win_smbios_data_headers() {
let raw_win_data = vec![0x00u8, 0x03, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
let win_smbios_data = WinSMBiosData::new(raw_win_data).unwrap();
assert_eq!(win_smbios_data.used20_calling_method(), 0x00);
assert_eq!(win_smbios_data.smbios_major_version(), 0x03);
assert_eq!(win_smbios_data.smbios_minor_version(), 0x04);
assert_eq!(win_smbios_data.dmi_revision(), 0x00);
assert_eq!(win_smbios_data.table_data_length(), 0x01);
}
#[test]
fn test_win_smbios_data_constructor() {
let raw_win_data = vec![0x00u8, 0x03, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0xFF];
let win_smbios_data = WinSMBiosData::new(raw_win_data.clone()).unwrap();
assert_eq!(
win_smbios_data.windows_header.as_slice(),
&raw_win_data[..8]
);
}
}