rust-ipmi 0.1.1

IPMI client library for rust
Documentation
use crate::{
    commands::Privilege,
    err::IpmiPayloadError,
    parser::{AuthType, IpmiHeader, IpmiV2Header, Packet, Payload, PayloadType},
};

use bitvec::prelude::*;

use super::rmcp_open_session::StatusCode;

#[derive(Clone, Debug)]
pub enum RAKP {
    Message1(RAKPMessage1),
    Message2(RAKPMessage2),
    Message3(RAKPMessage3),
    Message4(RAKPMessage4),
}

impl Into<Vec<u8>> for RAKP {
    fn into(self) -> Vec<u8> {
        match self {
            RAKP::Message1(payload) => payload.into(),
            RAKP::Message3(payload) => payload.into(),
            _ => todo!(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct RAKPMessage1 {
    pub message_tag: u8,
    pub managed_system_session_id: u32,
    pub remote_console_random_number: u128,
    pub inherit_role: bool,
    pub requested_max_privilege: Privilege,
    pub username_length: u8,
    pub username: String,
}

impl Into<Vec<u8>> for RAKPMessage1 {
    fn into(self) -> Vec<u8> {
        let mut result = Vec::new();
        result.push(self.message_tag);
        result.extend([0x0, 0x0, 0x0]);
        result.extend(u32::to_le_bytes(self.managed_system_session_id));
        result.extend(u128::to_le_bytes(self.remote_console_random_number));
        result.push({
            let mut bv: BitVec<u8, Msb0> = bitvec![u8, Msb0; 0;8];
            *bv.get_mut(3).unwrap() = self.inherit_role;
            bv[4..].store::<u8>(self.requested_max_privilege.into());
            let max_priv = bv[..].load::<u8>();
            max_priv
        });
        result.extend([0x0, 0x0]);
        result.push(self.username_length);
        result.extend(self.username.into_bytes());
        result
    }
}

impl Into<Packet> for RAKPMessage1 {
    fn into(self) -> Packet {
        Packet::new(
            IpmiHeader::V2_0(IpmiV2Header::new(
                AuthType::RmcpPlus,
                false,
                false,
                PayloadType::RAKP1,
                0x0,
                0x0,
                (self.username_length + 28).try_into().unwrap(),
            )),
            Payload::RAKP(RAKP::Message1(self.clone())),
        )
    }
}

impl RAKPMessage1 {
    pub fn new(
        message_tag: u8,
        managed_system_session_id: u32,
        remote_console_random_number: u128,
        inherit_role: bool,
        requested_max_privilege: Privilege,
        username: String,
    ) -> RAKPMessage1 {
        RAKPMessage1 {
            message_tag,
            managed_system_session_id,
            remote_console_random_number,
            inherit_role,
            requested_max_privilege,
            username_length: { username.len().try_into().unwrap() },
            username,
        }
    }
}

#[derive(Clone, Debug)]
pub struct RAKPMessage2 {
    pub message_tag: u8,
    pub rmcp_plus_status_code: StatusCode,
    pub remote_console_session_id: u32,
    pub managed_system_random_number: u128,
    pub managed_system_guid: u128,
    pub key_exchange_auth_code: Option<Vec<u8>>,
}

impl TryFrom<&[u8]> for RAKPMessage2 {
    type Error = IpmiPayloadError;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        if value.len() < 8 {
            Err(IpmiPayloadError::WrongLength)?
        }
        let message_tag = value[0];
        let rmcp_plus_status_code: StatusCode = value[1].into();
        let remote_console_session_id = u32::from_le_bytes(value[4..8].try_into().unwrap());
        let mut managed_system_random_number = 0;
        let mut managed_system_guid = 0;
        let mut key_exchange_auth_code = None;

        if value.len() >= 40 {
            managed_system_random_number = u128::from_le_bytes(value[8..24].try_into().unwrap());
            managed_system_guid = u128::from_le_bytes(value[24..40].try_into().unwrap());
            if value.len() > 40 {
                key_exchange_auth_code = Some(value[40..].to_vec())
            }
        };

        Ok(RAKPMessage2 {
            message_tag,
            rmcp_plus_status_code,
            remote_console_session_id,
            managed_system_random_number,
            managed_system_guid,
            key_exchange_auth_code,
        })
    }
}

#[derive(Clone, Debug)]
pub struct RAKPMessage3 {
    pub message_tag: u8,
    pub rmcp_plus_status_code: StatusCode,
    pub managed_system_session_id: u32,
    pub key_exchange_auth_code: Option<Vec<u8>>,
}

impl Into<Vec<u8>> for RAKPMessage3 {
    fn into(self) -> Vec<u8> {
        let mut result = Vec::new();
        result.push(self.message_tag);
        result.push(self.rmcp_plus_status_code.into());
        result.extend([0x0, 0x0]);
        result.extend(u32::to_le_bytes(self.managed_system_session_id));
        if let Some(auth_code) = &self.key_exchange_auth_code {
            result.append(&mut auth_code.clone());
        }
        result
    }
}

impl Into<Packet> for RAKPMessage3 {
    fn into(self) -> Packet {
        Packet::new(
            IpmiHeader::V2_0(IpmiV2Header::new(
                AuthType::RmcpPlus,
                false,
                false,
                PayloadType::RAKP3,
                0x0,
                0x0,
                {
                    match &self.key_exchange_auth_code {
                        None => 8 as u16,
                        Some(auth_code) => (auth_code.len() + 8) as u16,
                    }
                },
            )),
            Payload::RAKP(RAKP::Message3(self.clone())),
        )
    }
}

impl RAKPMessage3 {
    pub fn new(
        message_tag: u8,
        rmcp_plus_status_code: StatusCode,
        managed_system_session_id: u32,
        key_exchange_auth_code: Option<Vec<u8>>,
    ) -> RAKPMessage3 {
        RAKPMessage3 {
            message_tag,
            rmcp_plus_status_code,
            managed_system_session_id,
            key_exchange_auth_code,
        }
    }
}

#[derive(Clone, Debug)]
pub struct RAKPMessage4 {
    pub message_tag: u8,
    pub rmcp_plus_status_code: StatusCode,
    pub management_console_session_id: u32,
    pub integrity_check_value: Option<Vec<u8>>,
}

impl TryFrom<&[u8]> for RAKPMessage4 {
    type Error = IpmiPayloadError;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        if value.len() < 8 {
            Err(IpmiPayloadError::WrongLength)?
        }
        Ok(RAKPMessage4 {
            message_tag: value[0],
            rmcp_plus_status_code: value[1].into(),
            management_console_session_id: u32::from_le_bytes(value[4..8].try_into().unwrap()),
            integrity_check_value: {
                if value.len() > 8 {
                    Some(value[8..].to_vec())
                } else {
                    None
                }
            },
        })
    }
}