use anyhow::{Result, anyhow, bail};
use crate::{
client::pdu_connection::FromBytes,
models::{
common::{BasicHeaderSegment, HEADER_LEN, SendingData},
login::common::{LoginFlags, Stage},
opcode::{BhsOpcode, IfFlags, Opcode},
},
};
#[repr(C)]
#[derive(Debug, Default, PartialEq)]
pub struct LoginRequest {
pub opcode: BhsOpcode, pub flags: LoginFlags, pub version_max: u8, pub version_min: u8, pub total_ahs_length: u8, pub data_segment_length: [u8; 3], pub isid: [u8; 6], pub tsih: u16, pub initiator_task_tag: u32, pub cid: u16, reserved1: [u8; 2], pub cmd_sn: u32, pub exp_stat_sn: u32, reserved2: [u8; 16], }
impl LoginRequest {
pub fn to_bhs_bytes(&self) -> [u8; HEADER_LEN] {
let mut buf = [0u8; HEADER_LEN];
buf[0] = (&self.opcode).into();
buf[1] = self.flags.bits();
buf[2] = self.version_max;
buf[3] = self.version_min;
buf[4] = self.total_ahs_length;
buf[5..8].copy_from_slice(&self.data_segment_length);
buf[8..14].copy_from_slice(&self.isid);
buf[14..16].copy_from_slice(&self.tsih.to_be_bytes());
buf[16..20].copy_from_slice(&self.initiator_task_tag.to_be_bytes());
buf[20..22].copy_from_slice(&self.cid.to_be_bytes());
buf[24..28].copy_from_slice(&self.cmd_sn.to_be_bytes());
buf[28..32].copy_from_slice(&self.exp_stat_sn.to_be_bytes());
buf
}
pub fn from_bhs_bytes(buf: &[u8]) -> Result<Self> {
if buf.len() < HEADER_LEN {
return Err(anyhow!("buffer too small"));
}
let opcode = BhsOpcode::try_from(buf[0])?;
if opcode.opcode != Opcode::LoginReq {
bail!("LoginReq invalid opcode: {:?}", opcode.opcode);
}
let flags = LoginFlags::try_from(buf[1])?;
let version_max = buf[2];
let version_min = buf[3];
let total_ahs_length = buf[4];
let data_segment_length = [buf[5], buf[6], buf[7]];
let mut isid = [0u8; 6];
isid.copy_from_slice(&buf[8..14]);
let tsih = u16::from_be_bytes([buf[14], buf[15]]);
let initiator_task_tag = u32::from_be_bytes(buf[16..20].try_into()?);
let cid = u16::from_be_bytes(buf[20..22].try_into()?);
let cmd_sn = u32::from_be_bytes(buf[24..28].try_into()?);
let exp_stat_sn = u32::from_be_bytes(buf[28..32].try_into()?);
Ok(LoginRequest {
opcode,
flags,
version_max,
version_min,
total_ahs_length,
data_segment_length,
isid,
tsih,
initiator_task_tag,
cid,
reserved1: [0u8; 2],
cmd_sn,
exp_stat_sn,
reserved2: [0u8; 16],
})
}
}
#[derive(Debug)]
pub struct LoginRequestBuilder {
pub header: LoginRequest,
}
impl LoginRequestBuilder {
pub fn new(isid: [u8; 6], tsih: u16) -> Self {
LoginRequestBuilder {
header: LoginRequest {
opcode: BhsOpcode {
flags: IfFlags::I,
opcode: Opcode::LoginReq,
},
isid,
tsih,
..Default::default()
},
}
}
pub fn transit(mut self) -> Self {
self.header.flags.insert(LoginFlags::TRANSIT);
self
}
pub fn csg(mut self, stage: Stage) -> Self {
let bits = (stage as u8 & 0b11) << 2;
self.header.flags.remove(LoginFlags::CSG_MASK);
self.header
.flags
.insert(LoginFlags::from_bits_truncate(bits));
self
}
pub fn nsg(mut self, stage: Stage) -> Self {
let bits = stage as u8 & 0b11;
self.header.flags.remove(LoginFlags::NSG_MASK);
self.header
.flags
.insert(LoginFlags::from_bits_truncate(bits));
self
}
pub fn versions(mut self, max: u8, min: u8) -> Self {
self.header.version_max = max;
self.header.version_min = min;
self
}
pub fn initiator_task_tag(mut self, tag: u32) -> Self {
self.header.initiator_task_tag = tag;
self
}
pub fn connection_id(mut self, cid: u16) -> Self {
self.header.cid = cid;
self
}
pub fn cmd_sn(mut self, sn: u32) -> Self {
self.header.cmd_sn = sn;
self
}
pub fn exp_stat_sn(mut self, sn: u32) -> Self {
self.header.exp_stat_sn = sn;
self
}
pub fn isid(mut self, isid: &[u8; 8]) -> Self {
self.header.isid.clone_from_slice(isid);
self
}
}
impl SendingData for LoginRequest {
fn get_final_bit(&self) -> bool {
!self.flags.contains(LoginFlags::CONTINUE)
}
fn set_final_bit(&mut self) {
self.flags.remove(LoginFlags::CONTINUE);
}
fn get_continue_bit(&self) -> bool {
self.flags.contains(LoginFlags::CONTINUE)
}
fn set_continue_bit(&mut self) {
self.flags.insert(LoginFlags::CONTINUE);
}
}
impl FromBytes for LoginRequest {
fn from_bhs_bytes(bytes: &[u8]) -> Result<Self> {
Self::from_bhs_bytes(bytes)
}
}
impl BasicHeaderSegment for LoginRequest {
fn to_bhs_bytes(&self) -> Result<[u8; HEADER_LEN]> {
Ok(self.to_bhs_bytes())
}
fn get_opcode(&self) -> &BhsOpcode {
&self.opcode
}
fn get_initiator_task_tag(&self) -> u32 {
self.initiator_task_tag
}
fn get_ahs_length_bytes(&self) -> usize {
(self.total_ahs_length as usize) * 4
}
fn set_ahs_length_bytes(&mut self, len: u8) {
self.total_ahs_length = len >> 2;
}
fn get_data_length_bytes(&self) -> usize {
u32::from_be_bytes([
0,
self.data_segment_length[0],
self.data_segment_length[1],
self.data_segment_length[2],
]) as usize
}
fn set_data_length_bytes(&mut self, len: u32) {
let be = len.to_be_bytes();
self.data_segment_length = [be[1], be[2], be[3]];
}
}