1use std::io::{Cursor, Read, Write};
9
10use opcua_types::{
11 process_decode_io_result, process_encode_io_result, read_u32, read_u8, status_code::StatusCode,
12 write_u32, write_u8, DecodingOptions, EncodingResult, Error, SimpleBinaryDecodable,
13 SimpleBinaryEncodable,
14};
15use tracing::trace;
16
17use super::{
18 message_chunk_info::ChunkInfo,
19 secure_channel::SecureChannel,
20 security_header::{SecurityHeader, SequenceHeader},
21 tcp_types::{
22 CHUNK_FINAL, CHUNK_FINAL_ERROR, CHUNK_INTERMEDIATE, CHUNK_MESSAGE,
23 CLOSE_SECURE_CHANNEL_MESSAGE, MIN_CHUNK_SIZE, OPEN_SECURE_CHANNEL_MESSAGE,
24 },
25};
26
27pub const MESSAGE_CHUNK_HEADER_SIZE: usize = 3 + 1 + 4 + 4;
29pub const MESSAGE_SIZE_OFFSET: usize = 3 + 1;
32
33#[derive(Debug, Clone, Copy, PartialEq)]
34pub enum MessageChunkType {
36 Message,
38 OpenSecureChannel,
40 CloseSecureChannel,
42}
43
44impl MessageChunkType {
45 pub fn is_open_secure_channel(&self) -> bool {
47 *self == MessageChunkType::OpenSecureChannel
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq)]
52pub enum MessageIsFinalType {
54 Intermediate,
56 Final,
58 FinalError,
60}
61
62#[derive(Debug, Clone, PartialEq)]
63pub struct MessageChunkHeader {
65 pub message_type: MessageChunkType,
67 pub is_final: MessageIsFinalType,
69 pub message_size: u32,
71 pub secure_channel_id: u32,
73}
74
75impl SimpleBinaryEncodable for MessageChunkHeader {
76 fn byte_len(&self) -> usize {
77 MESSAGE_CHUNK_HEADER_SIZE
78 }
79
80 fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
81 let message_type = match self.message_type {
82 MessageChunkType::Message => CHUNK_MESSAGE,
83 MessageChunkType::OpenSecureChannel => OPEN_SECURE_CHANNEL_MESSAGE,
84 MessageChunkType::CloseSecureChannel => CLOSE_SECURE_CHANNEL_MESSAGE,
85 };
86
87 let is_final = match self.is_final {
88 MessageIsFinalType::Intermediate => CHUNK_INTERMEDIATE,
89 MessageIsFinalType::Final => CHUNK_FINAL,
90 MessageIsFinalType::FinalError => CHUNK_FINAL_ERROR,
91 };
92
93 process_encode_io_result(stream.write_all(message_type))?;
94 write_u8(stream, is_final)?;
95 write_u32(stream, self.message_size)?;
96 write_u32(stream, self.secure_channel_id)
97 }
98}
99
100impl SimpleBinaryDecodable for MessageChunkHeader {
101 fn decode<S: Read + ?Sized>(stream: &mut S, _: &DecodingOptions) -> EncodingResult<Self> {
102 let mut message_type_code = [0u8; 3];
103 process_decode_io_result(stream.read_exact(&mut message_type_code))?;
104 let message_type = match &message_type_code as &[u8] {
105 CHUNK_MESSAGE => MessageChunkType::Message,
106 OPEN_SECURE_CHANNEL_MESSAGE => MessageChunkType::OpenSecureChannel,
107 CLOSE_SECURE_CHANNEL_MESSAGE => MessageChunkType::CloseSecureChannel,
108 r => {
109 return Err(Error::decoding(format!(
110 "Invalid message chunk type: {r:?}"
111 )));
112 }
113 };
114
115 let chunk_type_code = read_u8(stream)?;
116 let is_final = match chunk_type_code {
117 CHUNK_FINAL => MessageIsFinalType::Final,
118 CHUNK_INTERMEDIATE => MessageIsFinalType::Intermediate,
119 CHUNK_FINAL_ERROR => MessageIsFinalType::FinalError,
120 r => {
121 return Err(Error::decoding(format!("Invalid message final type: {r}")));
122 }
123 };
124
125 let message_size = read_u32(stream)?;
126 let secure_channel_id = read_u32(stream)?;
127
128 Ok(MessageChunkHeader {
129 message_type,
130 is_final,
131 message_size,
132 secure_channel_id,
133 })
134 }
135}
136
137impl MessageChunkHeader {}
138
139#[derive(Debug)]
143pub struct MessageChunk {
144 pub data: Vec<u8>,
146}
147
148impl SimpleBinaryEncodable for MessageChunk {
149 fn byte_len(&self) -> usize {
150 self.data.len()
151 }
152
153 fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
154 stream.write_all(&self.data).map_err(|e| {
155 Error::encoding(format!(
156 "Encoding error while writing message chunk to stream: {e}"
157 ))
158 })
159 }
160}
161
162impl SimpleBinaryDecodable for MessageChunk {
163 fn decode<S: Read + ?Sized>(
164 in_stream: &mut S,
165 decoding_options: &DecodingOptions,
166 ) -> EncodingResult<Self> {
167 let chunk_header =
169 MessageChunkHeader::decode(in_stream, decoding_options).map_err(|err| {
170 Error::new(
171 StatusCode::BadCommunicationError,
172 format!("Cannot decode chunk header {err:?}"),
173 )
174 })?;
175
176 let message_size = chunk_header.message_size as usize;
177 if decoding_options.max_message_size > 0 && message_size > decoding_options.max_message_size
178 {
179 Err(Error::new(
181 StatusCode::BadTcpMessageTooLarge,
182 format!(
183 "Message size {} exceeds maximum message size {}",
184 message_size, decoding_options.max_message_size
185 ),
186 ))
187 } else {
188 let data = vec![0u8; message_size];
190 let mut stream = Cursor::new(data);
191
192 let chunk_header_size = chunk_header.byte_len();
194 chunk_header.encode(&mut stream)?;
195
196 let mut data = stream.into_inner();
198
199 in_stream.read_exact(&mut data[chunk_header_size..])?;
201
202 Ok(MessageChunk { data })
203 }
204 }
205}
206
207#[derive(Debug)]
208pub struct MessageChunkTooSmall;
211
212impl MessageChunk {
213 pub fn new(
215 sequence_number: u32,
216 request_id: u32,
217 message_type: MessageChunkType,
218 is_final: MessageIsFinalType,
219 secure_channel: &SecureChannel,
220 data: &[u8],
221 ) -> EncodingResult<MessageChunk> {
222 let security_header = secure_channel.make_security_header(message_type);
224 let sequence_header = SequenceHeader {
225 sequence_number,
226 request_id,
227 };
228
229 let mut message_size = MESSAGE_CHUNK_HEADER_SIZE;
231 message_size += security_header.byte_len();
232 message_size += sequence_header.byte_len();
233 message_size += data.len();
234
235 trace!(
236 "Creating a chunk with a size of {}, data excluding padding & signature",
237 message_size
238 );
239 let secure_channel_id = secure_channel.secure_channel_id();
240 let chunk_header = MessageChunkHeader {
241 message_type,
242 is_final,
243 message_size: message_size as u32,
244 secure_channel_id,
245 };
246
247 let mut buf = vec![0u8; message_size];
248 let buf_ref = &mut buf as &mut [u8];
249 let mut stream = Cursor::new(buf_ref);
250 chunk_header.encode(&mut stream)?;
252 security_header.encode(&mut stream)?;
254 sequence_header.encode(&mut stream)?;
256 stream.write_all(data)?;
258
259 Ok(MessageChunk { data: buf })
260 }
261
262 pub fn body_size_from_message_size(
266 message_type: MessageChunkType,
267 secure_channel: &SecureChannel,
268 max_chunk_size: usize,
269 ) -> Result<usize, Error> {
270 if max_chunk_size < MIN_CHUNK_SIZE {
271 return Err(Error::new(
272 StatusCode::BadTcpInternalError,
273 format!(
274 "chunk size {} is less than minimum allowed by the spec",
275 max_chunk_size
276 ),
277 ));
278 }
279 let security_header = secure_channel.make_security_header(message_type);
281
282 let mut header_size = MESSAGE_CHUNK_HEADER_SIZE;
283 header_size += security_header.byte_len();
284 header_size += (SequenceHeader {
285 sequence_number: 0,
286 request_id: 0,
287 })
288 .byte_len();
289
290 let signature_size = secure_channel.signature_size(&security_header);
293
294 let (plain_text_block_size, minimum_padding) =
297 secure_channel.get_padding_block_sizes(&security_header, message_type)?;
298
299 let aligned_max_chunk_size = if plain_text_block_size > 0 {
303 max_chunk_size - (max_chunk_size % plain_text_block_size)
304 } else {
305 max_chunk_size
306 };
307 Ok(aligned_max_chunk_size - header_size - signature_size - minimum_padding)
324 }
325
326 pub fn message_header(
328 &self,
329 decoding_options: &DecodingOptions,
330 ) -> EncodingResult<MessageChunkHeader> {
331 let mut stream = Cursor::new(&self.data);
333 MessageChunkHeader::decode(&mut stream, decoding_options)
334 }
335
336 pub fn is_open_secure_channel(&self, decoding_options: &DecodingOptions) -> bool {
338 if let Ok(message_header) = self.message_header(decoding_options) {
339 message_header.message_type.is_open_secure_channel()
340 } else {
341 false
342 }
343 }
344
345 pub fn chunk_info(&self, secure_channel: &SecureChannel) -> EncodingResult<ChunkInfo> {
347 ChunkInfo::new(self, secure_channel)
348 }
349
350 pub(crate) fn encrypted_data_offset(
351 &self,
352 decoding_options: &DecodingOptions,
353 ) -> EncodingResult<usize> {
354 let mut stream = Cursor::new(&self.data);
357 let message_header = MessageChunkHeader::decode(&mut stream, decoding_options)?;
358 SecurityHeader::decode_from_stream(
359 &mut stream,
360 message_header.message_type.is_open_secure_channel(),
361 decoding_options,
362 )?;
363 Ok(stream.position() as usize)
364 }
365}