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}