Skip to main content

iscsi_client_rs/models/
parse.rs

1//! This module provides utilities for parsing iSCSI Protocol Data Units (PDUs).
2//! It defines a generic `Pdu` enum that can represent any PDU type and provides
3//! a function to parse a PDU from its Basic Header Segment (BHS) bytes.
4
5// SPDX-License-Identifier: AGPL-3.0-or-later
6// Copyright (C) 2012-2025 Andrei Maltsev
7
8use anyhow::{Result, bail};
9use enum_dispatch::enum_dispatch;
10
11use crate::models::{
12    command::{request::ScsiCommandRequest, response::ScsiCommandResponse},
13    common::{BasicHeaderSegment, SendingData},
14    data::{request::ScsiDataOut, response::ScsiDataIn},
15    login::{request::LoginRequest, response::LoginResponse},
16    logout::{request::LogoutRequest, response::LogoutResponse},
17    nop::{request::NopOutRequest, response::NopInResponse},
18    opcode::{BhsOpcode, Opcode},
19    ready_2_transfer::response::ReadyToTransfer,
20    reject::response::RejectPdu,
21    text::{request::TextRequest, response::TextResponse},
22};
23
24/// An enum representing any iSCSI Protocol Data Unit (PDU).
25/// This enum dispatches to the `BasicHeaderSegment` and `SendingData` traits.
26#[enum_dispatch(BasicHeaderSegment, SendingData)]
27#[derive(Debug)]
28pub enum Pdu<'a> {
29    NopOutRequest(&'a mut NopOutRequest),
30    ScsiCommandRequest(&'a mut ScsiCommandRequest),
31    TextRequest(&'a mut TextRequest),
32    LoginRequest(&'a mut LoginRequest),
33    ScsiDataOut(&'a mut ScsiDataOut),
34    NopInResponse(&'a mut NopInResponse),
35    ScsiCommandResponse(&'a mut ScsiCommandResponse),
36    TextResponse(&'a mut TextResponse),
37    LoginResponse(&'a mut LoginResponse),
38    ScsiDataIn(&'a mut ScsiDataIn),
39    RejectPdu(&'a mut RejectPdu),
40    ReadyToTransfer(&'a mut ReadyToTransfer),
41    LogoutRequest(&'a mut LogoutRequest),
42    LogoutResponse(&'a mut LogoutResponse),
43}
44
45impl<'a> Pdu<'a> {
46    /// Parses a PDU from its Basic Header Segment (BHS) bytes.
47    pub fn from_bhs_bytes(bytes: &'a mut [u8]) -> Result<Self> {
48        let bhs = BhsOpcode::try_from(bytes[0])
49            .map_err(|e| anyhow::anyhow!("invalid opcode: {}", e))?;
50        match bhs.opcode {
51            Opcode::NopOut => {
52                let req = NopOutRequest::from_bhs_bytes(bytes)?;
53                Ok(Pdu::NopOutRequest(req))
54            },
55            Opcode::NopIn => {
56                let req = NopInResponse::from_bhs_bytes(bytes)?;
57                Ok(Pdu::NopInResponse(req))
58            },
59            Opcode::ScsiCommandReq => {
60                let req = ScsiCommandRequest::from_bhs_bytes(bytes)?;
61                Ok(Pdu::ScsiCommandRequest(req))
62            },
63            Opcode::ScsiCommandResp => {
64                let req = ScsiCommandResponse::from_bhs_bytes(bytes)?;
65                Ok(Pdu::ScsiCommandResponse(req))
66            },
67            Opcode::TextReq => {
68                let req = TextRequest::from_bhs_bytes(bytes)?;
69                Ok(Pdu::TextRequest(req))
70            },
71            Opcode::LoginReq => {
72                let req = LoginRequest::from_bhs_bytes(bytes)?;
73                Ok(Pdu::LoginRequest(req))
74            },
75            Opcode::ScsiDataOut => {
76                let req = ScsiDataOut::from_bhs_bytes(bytes)?;
77                Ok(Pdu::ScsiDataOut(req))
78            },
79            Opcode::LoginResp => {
80                let rsp = LoginResponse::from_bhs_bytes(bytes)?;
81                Ok(Pdu::LoginResponse(rsp))
82            },
83            Opcode::Reject => {
84                let rsp = RejectPdu::from_bhs_bytes(bytes)?;
85                Ok(Pdu::RejectPdu(rsp))
86            },
87            Opcode::ScsiDataIn => {
88                let req = ScsiDataIn::from_bhs_bytes(bytes)?;
89                Ok(Pdu::ScsiDataIn(req))
90            },
91            Opcode::ReadyToTransfer => {
92                let req = ReadyToTransfer::from_bhs_bytes(bytes)?;
93                Ok(Pdu::ReadyToTransfer(req))
94            },
95            Opcode::LogoutReq => {
96                let req = LogoutRequest::from_bhs_bytes(bytes)?;
97                Ok(Pdu::LogoutRequest(req))
98            },
99            Opcode::LogoutResp => {
100                let req = LogoutResponse::from_bhs_bytes(bytes)?;
101                Ok(Pdu::LogoutResponse(req))
102            },
103            other => bail!("unsupported opcode: {:?}", other),
104        }
105    }
106}