smb_msg/
error.rs

1//! Error response message
2
3use binrw::prelude::*;
4
5use smb_dtyp::binrw_util::prelude::*;
6
7#[binrw::binrw]
8#[derive(Debug, PartialEq, Eq)]
9pub struct ErrorResponse {
10    #[bw(calc = 9)]
11    #[br(assert(_structure_size == 9))]
12    _structure_size: u16,
13
14    #[bw(try_calc = error_data.len().try_into())]
15    _error_context_count: u8,
16
17    #[br(assert(_reserved == 0))]
18    #[bw(calc = 0)]
19    _reserved: u8,
20
21    #[bw(calc = PosMarker::default())]
22    _byte_count: PosMarker<u32>,
23
24    #[br(count = _error_context_count)]
25    pub error_data: Vec<ErrorResponseContext>,
26}
27
28#[binrw::binrw]
29#[derive(Debug, PartialEq, Eq)]
30pub struct ErrorResponseContext {
31    // each context item should be aligned to 8 bytes,
32    // relative to the start of the error context.
33    // luckily, it appears after the header, which is, itself, aligned to 8 bytes.
34    #[brw(align_before = 8)]
35    #[bw(try_calc = error_data.len().try_into())]
36    _error_data_length: u32,
37    pub error_id: ErrorId,
38    #[br(count = _error_data_length)]
39    pub error_data: Vec<u8>,
40}
41
42impl ErrorResponse {
43    /// Locates a context by its ID,
44    /// returning a reference to it if found.
45    pub fn find_context(&self, id: ErrorId) -> Option<&ErrorResponseContext> {
46        self.error_data.iter().find(|c| c.error_id == id)
47    }
48}
49
50impl ErrorResponseContext {
51    /// Interprets the error data as a u32, if possible.
52    /// Returns an error if the data length is not 4 bytes.
53    pub fn as_u32(&self) -> crate::Result<u32> {
54        if self.error_data.len() == std::mem::size_of::<u32>() {
55            Ok(u32::from_le_bytes(
56                self.error_data.as_slice().try_into().unwrap(),
57            ))
58        } else {
59            Err(crate::SmbMsgError::InvalidData(
60                "Invalid error data length for u32".into(),
61            ))
62        }
63    }
64
65    /// Interprets the error data as a u64, if possible.
66    /// Returns an error if the data length is not 8 bytes.
67    pub fn as_u64(&self) -> crate::Result<u64> {
68        if self.error_data.len() == std::mem::size_of::<u64>() {
69            Ok(u64::from_le_bytes(
70                self.error_data.as_slice().try_into().unwrap(),
71            ))
72        } else {
73            Err(crate::SmbMsgError::InvalidData(
74                "Invalid error data length for u64".into(),
75            ))
76        }
77    }
78}
79
80#[binrw::binrw]
81#[derive(Debug, PartialEq, Eq)]
82#[brw(repr(u32))]
83pub enum ErrorId {
84    Default = 0,
85    ShareRedirect = 0x72645253,
86}
87
88#[cfg(test)]
89mod tests {
90    use crate::*;
91
92    use super::*;
93
94    #[test]
95    pub fn test_simple_error_pasrsed() {
96        let msg = decode_content(&[
97            0xfe, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x34, 0x0, 0x0, 0xc0, 0x5, 0x0, 0x1, 0x0,
98            0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
99            0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x71, 0x0, 0x0, 0x28, 0x0, 0x30, 0x0, 0x0, 0xf7,
100            0xd, 0xa6, 0x1d, 0x9b, 0x2c, 0x43, 0xd3, 0x26, 0x88, 0x74, 0xf, 0xdf, 0x47, 0x59, 0x24,
101            0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
102        ]);
103        assert_eq!(msg.header.status, Status::ObjectNameNotFound as u32);
104        let msg = match msg.content {
105            ResponseContent::Error(msg) => msg,
106            _ => panic!("Unexpected response"),
107        };
108        assert_eq!(msg, ErrorResponse { error_data: vec![] })
109    }
110}