smb_msg/
error.rs

1//! Error response message
2
3use binrw::prelude::*;
4
5#[cfg(feature = "client")]
6use binrw::io::TakeSeekExt;
7use smb_dtyp::binrw_util::prelude::*;
8use smb_msg_derive::*;
9
10/// The SMB2 ERROR Response packet is sent by the server to respond to a request
11/// that has failed or encountered an error.
12///
13/// Reference: MS-SMB2 2.2.2
14#[smb_response(size = 9)]
15pub struct ErrorResponse {
16    /// For SMB dialects other than 3.1.1, this must be set to 0.
17    /// For SMB dialect 3.1.1, if nonzero, the ErrorData field is formatted as
18    /// a variable-length array of SMB2 ERROR Context structures.
19    #[bw(try_calc = error_data.len().try_into())]
20    #[br(temp)]
21    _error_context_count: u8,
22
23    reserved: u8,
24
25    #[bw(calc = PosMarker::default())]
26    #[br(temp)]
27    _byte_count: PosMarker<u32>,
28
29    /// Variable-length data field that contains extended error information.
30    /// For SMB 3.1.1 with nonzero ErrorContextCount, formatted as SMB2 ERROR Context structures.
31    #[br(count = _error_context_count, map_stream = |s| s.take_seek(_byte_count.value.into()))]
32    #[bw(write_with = PosMarker::write_size, args(&_byte_count))]
33    pub error_data: Vec<ErrorResponseContext>,
34}
35
36/// For SMB dialect 3.1.1, error data is formatted as an array of SMB2 ERROR Context structures.
37/// Each error context contains an identifier for the error context followed by the error data.
38/// Each context must start at an 8-byte aligned boundary relative to the start of the SMB2 ERROR Response.
39///
40/// Reference: MS-SMB2 2.2.2.1
41#[smb_response_binrw]
42pub struct ErrorResponseContext {
43    // each context item should be aligned to 8 bytes,
44    // relative to the start of the error context.
45    // luckily, it appears after the header, which is, itself, aligned to 8 bytes.
46    #[brw(align_before = 8)]
47    /// The length, in bytes, of the ErrorContextData field
48    #[bw(try_calc = error_data.len().try_into())]
49    _error_data_length: u32,
50    /// An identifier for the error context
51    pub error_id: ErrorId,
52    /// Variable-length error data formatted according to the ErrorId
53    #[br(count = _error_data_length)]
54    pub error_data: Vec<u8>,
55}
56
57impl ErrorResponse {
58    /// Locates a context by its ID,
59    /// returning a reference to it if found.
60    pub fn find_context(&self, id: ErrorId) -> Option<&ErrorResponseContext> {
61        self.error_data.iter().find(|c| c.error_id == id)
62    }
63}
64
65impl ErrorResponseContext {
66    /// Interprets the error data as a u32, if possible.
67    /// Returns an error if the data length is not 4 bytes.
68    pub fn as_u32(&self) -> crate::Result<u32> {
69        if self.error_data.len() == std::mem::size_of::<u32>() {
70            Ok(u32::from_le_bytes(
71                self.error_data.as_slice().try_into().unwrap(),
72            ))
73        } else {
74            Err(crate::SmbMsgError::InvalidData(
75                "Invalid error data length for u32".into(),
76            ))
77        }
78    }
79
80    /// Interprets the error data as a u64, if possible.
81    /// Returns an error if the data length is not 8 bytes.
82    pub fn as_u64(&self) -> crate::Result<u64> {
83        if self.error_data.len() == std::mem::size_of::<u64>() {
84            Ok(u64::from_le_bytes(
85                self.error_data.as_slice().try_into().unwrap(),
86            ))
87        } else {
88            Err(crate::SmbMsgError::InvalidData(
89                "Invalid error data length for u64".into(),
90            ))
91        }
92    }
93}
94
95/// An identifier for the error context in SMB2 ERROR Context structures.
96///
97/// Reference: MS-SMB2 2.2.2.1
98#[smb_response_binrw]
99#[brw(repr(u32))]
100pub enum ErrorId {
101    /// Unless otherwise specified, all errors defined in the MS-SMB2 protocol use this error ID
102    Default = 0,
103    /// The ErrorContextData field contains a share redirect message
104    ShareRedirect = 0x72645253,
105}
106
107#[cfg(test)]
108mod tests {
109    use crate::*;
110
111    test_response! {
112        error_simple, Command::Cancel => Error { error_data: vec![], } => "0900000000000000"
113    }
114
115    // TODO(TEST): Add a test with added context items.
116}