]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/modbus/modbus.rs
app-layer: include decoder events in app-layer tx data
[people/ms/suricata.git] / rust / src / modbus / modbus.rs
CommitLineData
a458a94d
SD
1/* Copyright (C) 2021 Open Information Security Foundation
2*
3* You can copy, redistribute or modify this Program under the terms of
4* the GNU General Public License version 2 as published by the Free
5* Software Foundation.
6*
7* This program is distributed in the hope that it will be useful,
8* but WITHOUT ANY WARRANTY; without even the implied warranty of
9* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10* GNU General Public License for more details.
11*
12* You should have received a copy of the GNU General Public License
13* version 2 along with this program; if not, write to the Free Software
14* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15* 02110-1301, USA.
16*/
83887510 17use crate::applayer::{self, *};
a458a94d
SD
18use crate::core::{self, AppProto, ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_TCP};
19
20use std::ffi::CString;
a458a94d
SD
21
22use sawp::error::Error as SawpError;
23use sawp::error::ErrorKind as SawpErrorKind;
24use sawp::parser::{Direction, Parse};
25use sawp::probe::{Probe, Status};
26use sawp_modbus::{self, AccessType, ErrorFlags, Flags, Message};
27
28pub const REQUEST_FLOOD: usize = 500; // Default unreplied Modbus requests are considered a flood
29pub const MODBUS_PARSER: sawp_modbus::Modbus = sawp_modbus::Modbus {};
30
31static mut ALPROTO_MODBUS: AppProto = ALPROTO_UNKNOWN;
32
eb552978 33#[derive(AppLayerEvent)]
a458a94d 34enum ModbusEvent {
eb552978 35 UnsolicitedResponse,
a458a94d
SD
36 InvalidFunctionCode,
37 InvalidLength,
38 InvalidValue,
39 InvalidExceptionCode,
40 ValueMismatch,
41 Flooded,
42 InvalidProtocolId,
43}
a458a94d
SD
44pub struct ModbusTransaction {
45 pub id: u64,
46
47 pub request: Option<Message>,
48 pub response: Option<Message>,
49
a458a94d
SD
50 pub tx_data: AppLayerTxData,
51}
52
d71bcd82
JI
53impl Transaction for ModbusTransaction {
54 fn id(&self) -> u64 {
55 self.id
56 }
57}
58
a458a94d
SD
59impl ModbusTransaction {
60 pub fn new(id: u64) -> Self {
61 Self {
62 id,
63 request: None,
64 response: None,
a458a94d
SD
65 tx_data: AppLayerTxData::new(),
66 }
67 }
68
69 fn set_event(&mut self, event: ModbusEvent) {
7732efbe 70 self.tx_data.set_event(event as u8);
a458a94d
SD
71 }
72
73 fn set_events_from_flags(&mut self, flags: &Flags<ErrorFlags>) {
74 if flags.intersects(ErrorFlags::FUNC_CODE) {
75 self.set_event(ModbusEvent::InvalidFunctionCode);
76 }
77 if flags.intersects(ErrorFlags::DATA_VALUE) {
78 self.set_event(ModbusEvent::InvalidValue);
79 }
80 if flags.intersects(ErrorFlags::DATA_LENGTH) {
81 self.set_event(ModbusEvent::InvalidLength);
82 }
83 if flags.intersects(ErrorFlags::EXC_CODE) {
84 self.set_event(ModbusEvent::InvalidExceptionCode);
85 }
86 if flags.intersects(ErrorFlags::PROTO_ID) {
87 self.set_event(ModbusEvent::InvalidProtocolId);
88 }
89 }
90}
91
a458a94d
SD
92pub struct ModbusState {
93 pub transactions: Vec<ModbusTransaction>,
94 tx_id: u64,
95 givenup: bool, // Indicates flood
96}
97
d71bcd82
JI
98impl State<ModbusTransaction> for ModbusState {
99 fn get_transactions(&self) -> &[ModbusTransaction] {
100 &self.transactions
101 }
102}
103
a458a94d
SD
104impl ModbusState {
105 pub fn new() -> Self {
106 Self {
107 transactions: Vec::new(),
108 tx_id: 0,
109 givenup: false,
110 }
111 }
112
113 pub fn get_tx(&mut self, tx_id: u64) -> Option<&mut ModbusTransaction> {
114 for tx in &mut self.transactions {
115 if tx.id == tx_id + 1 {
116 return Some(tx);
117 }
118 }
119 None
120 }
121
122 /// Searches the requests in order to find one matching the given response. Returns the matching
123 /// transaction, if it exists
124 pub fn find_request_and_validate(
125 &mut self, resp: &mut Message,
126 ) -> Option<&mut ModbusTransaction> {
127 for tx in &mut self.transactions {
128 if let Some(req) = &tx.request {
129 if tx.response.is_none() && resp.matches(req) {
130 return Some(tx);
131 }
132 }
133 }
134 None
135 }
136
137 /// Searches the responses in order to find one matching the given request. Returns the matching
138 /// transaction, if it exists
139 pub fn find_response_and_validate(
140 &mut self, req: &mut Message,
141 ) -> Option<&mut ModbusTransaction> {
142 for tx in &mut self.transactions {
143 if let Some(resp) = &tx.response {
144 if tx.request.is_none() && req.matches(resp) {
145 return Some(tx);
146 }
147 }
148 }
149 None
150 }
151
152 pub fn new_tx(&mut self) -> Option<ModbusTransaction> {
153 // Check flood limit
154 if self.givenup {
155 return None;
156 }
157
158 self.tx_id += 1;
159 let mut tx = ModbusTransaction::new(self.tx_id);
160
161 if REQUEST_FLOOD != 0 && self.transactions.len() >= REQUEST_FLOOD {
162 tx.set_event(ModbusEvent::Flooded);
163 self.givenup = true;
164 }
165
166 Some(tx)
167 }
168
169 pub fn free_tx(&mut self, tx_id: u64) {
170 if let Some(index) = self.transactions.iter().position(|tx| tx.id == tx_id + 1) {
171 self.transactions.remove(index);
172
173 // Check flood limit
174 if self.givenup && REQUEST_FLOOD != 0 && self.transactions.len() < REQUEST_FLOOD {
175 self.givenup = false;
176 }
177 }
178 }
179
180 pub fn parse(&mut self, input: &[u8], direction: Direction) -> AppLayerResult {
181 let mut rest = input;
182 while rest.len() > 0 {
183 match MODBUS_PARSER.parse(rest, direction.clone()) {
184 Ok((inner_rest, Some(mut msg))) => {
185 match direction {
186 Direction::ToServer | Direction::Unknown => {
187 match self.find_response_and_validate(&mut msg) {
188 Some(tx) => {
189 tx.set_events_from_flags(&msg.error_flags);
190 tx.request = Some(msg);
191 }
192 None => {
193 let mut tx = match self.new_tx() {
194 Some(tx) => tx,
195 None => return AppLayerResult::ok(),
196 };
197 tx.set_events_from_flags(&msg.error_flags);
198 tx.request = Some(msg);
199 self.transactions.push(tx);
200 }
201 }
202 }
203 Direction::ToClient => match self.find_request_and_validate(&mut msg) {
204 Some(tx) => {
205 if msg
206 .access_type
207 .intersects(AccessType::READ | AccessType::WRITE)
208 && msg.error_flags.intersects(
209 ErrorFlags::DATA_LENGTH | ErrorFlags::DATA_VALUE,
210 )
211 {
212 tx.set_event(ModbusEvent::ValueMismatch);
213 } else {
214 tx.set_events_from_flags(&msg.error_flags);
215 }
216 tx.response = Some(msg);
217 }
218 None => {
219 let mut tx = match self.new_tx() {
220 Some(tx) => tx,
221 None => return AppLayerResult::ok(),
222 };
223 if msg
224 .access_type
225 .intersects(AccessType::READ | AccessType::WRITE)
226 && msg.error_flags.intersects(
227 ErrorFlags::DATA_LENGTH | ErrorFlags::DATA_VALUE,
228 )
229 {
230 tx.set_event(ModbusEvent::ValueMismatch);
231 } else {
232 tx.set_events_from_flags(&msg.error_flags);
233 }
234 tx.response = Some(msg);
235 tx.set_event(ModbusEvent::UnsolicitedResponse);
236 self.transactions.push(tx);
237 }
238 },
239 }
240
241 if inner_rest.len() >= rest.len() {
242 return AppLayerResult::err();
243 }
244 rest = inner_rest;
245 }
246 Ok((inner_rest, None)) => {
247 return AppLayerResult::incomplete(
248 (input.len() - inner_rest.len()) as u32,
249 inner_rest.len() as u32 + 1,
250 );
251 }
252 Err(SawpError {
253 kind: SawpErrorKind::Incomplete(sawp::error::Needed::Size(needed)),
254 }) => {
255 return AppLayerResult::incomplete(
256 (input.len() - rest.len()) as u32,
257 (rest.len() + needed.get()) as u32,
258 );
259 }
260 Err(SawpError {
261 kind: SawpErrorKind::Incomplete(sawp::error::Needed::Unknown),
262 }) => {
263 return AppLayerResult::incomplete(
264 (input.len() - rest.len()) as u32,
265 rest.len() as u32 + 1,
266 );
267 }
268 Err(_) => return AppLayerResult::err(),
269 }
270 }
271 AppLayerResult::ok()
272 }
273}
274
275/// Probe input to see if it looks like Modbus.
276#[no_mangle]
277pub extern "C" fn rs_modbus_probe(
278 _flow: *const core::Flow, _direction: u8, input: *const u8, len: u32, _rdir: *mut u8,
279) -> AppProto {
280 let slice: &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, len as usize) };
281 match MODBUS_PARSER.probe(slice, Direction::Unknown) {
282 Status::Recognized => unsafe { ALPROTO_MODBUS },
283 Status::Incomplete => ALPROTO_UNKNOWN,
284 Status::Unrecognized => unsafe { ALPROTO_FAILED },
285 }
286}
287
288#[no_mangle]
289pub extern "C" fn rs_modbus_state_new(
290 _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
291) -> *mut std::os::raw::c_void {
292 Box::into_raw(Box::new(ModbusState::new())) as *mut std::os::raw::c_void
293}
294
295#[no_mangle]
296pub extern "C" fn rs_modbus_state_free(state: *mut std::os::raw::c_void) {
297 let _state: Box<ModbusState> = unsafe { Box::from_raw(state as *mut ModbusState) };
298}
299
300#[no_mangle]
363b5f99 301pub unsafe extern "C" fn rs_modbus_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
a458a94d
SD
302 let state = cast_pointer!(state, ModbusState);
303 state.free_tx(tx_id);
304}
305
306#[no_mangle]
363b5f99 307pub unsafe extern "C" fn rs_modbus_parse_request(
a458a94d
SD
308 _flow: *const core::Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
309 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
310) -> AppLayerResult {
311 if input_len == 0 {
363b5f99 312 if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 {
a458a94d
SD
313 return AppLayerResult::ok();
314 } else {
315 return AppLayerResult::err();
316 }
317 }
318
319 let state = cast_pointer!(state, ModbusState);
363b5f99 320 let buf = std::slice::from_raw_parts(input, input_len as usize);
a458a94d
SD
321
322 state.parse(buf, Direction::ToServer)
323}
324
325#[no_mangle]
363b5f99 326pub unsafe extern "C" fn rs_modbus_parse_response(
a458a94d
SD
327 _flow: *const core::Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
328 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
329) -> AppLayerResult {
330 if input_len == 0 {
363b5f99
JI
331 if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 {
332 return AppLayerResult::ok();
333 } else {
334 return AppLayerResult::err();
a458a94d
SD
335 }
336 }
337
338 let state = cast_pointer!(state, ModbusState);
363b5f99 339 let buf = std::slice::from_raw_parts(input, input_len as usize);
a458a94d
SD
340
341 state.parse(buf, Direction::ToClient)
342}
343
344#[no_mangle]
363b5f99 345pub unsafe extern "C" fn rs_modbus_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
a458a94d
SD
346 let state = cast_pointer!(state, ModbusState);
347 state.tx_id
348}
349
350#[no_mangle]
363b5f99 351pub unsafe extern "C" fn rs_modbus_state_get_tx(
a458a94d
SD
352 state: *mut std::os::raw::c_void, tx_id: u64,
353) -> *mut std::os::raw::c_void {
354 let state = cast_pointer!(state, ModbusState);
355 match state.get_tx(tx_id) {
356 Some(tx) => (tx as *mut ModbusTransaction) as *mut std::os::raw::c_void,
357 None => std::ptr::null_mut(),
358 }
359}
360
361#[no_mangle]
363b5f99 362pub unsafe extern "C" fn rs_modbus_tx_get_alstate_progress(
a458a94d
SD
363 tx: *mut std::os::raw::c_void, _direction: u8,
364) -> std::os::raw::c_int {
365 let tx = cast_pointer!(tx, ModbusTransaction);
366 tx.response.is_some() as std::os::raw::c_int
367}
368
a458a94d 369#[no_mangle]
363b5f99 370pub unsafe extern "C" fn rs_modbus_state_get_tx_data(
a458a94d
SD
371 tx: *mut std::os::raw::c_void,
372) -> *mut AppLayerTxData {
373 let tx = cast_pointer!(tx, ModbusTransaction);
374 &mut tx.tx_data
375}
376
377#[no_mangle]
378pub unsafe extern "C" fn rs_modbus_register_parser() {
379 let default_port = std::ffi::CString::new("[502]").unwrap();
380 let parser = RustParser {
381 name: b"modbus\0".as_ptr() as *const std::os::raw::c_char,
382 default_port: default_port.as_ptr(),
383 ipproto: IPPROTO_TCP,
384 probe_ts: Some(rs_modbus_probe),
385 probe_tc: Some(rs_modbus_probe),
386 min_depth: 0,
387 max_depth: 16,
388 state_new: rs_modbus_state_new,
389 state_free: rs_modbus_state_free,
390 tx_free: rs_modbus_state_tx_free,
391 parse_ts: rs_modbus_parse_request,
392 parse_tc: rs_modbus_parse_response,
393 get_tx_count: rs_modbus_state_get_tx_count,
394 get_tx: rs_modbus_state_get_tx,
395 tx_comp_st_ts: 1,
396 tx_comp_st_tc: 1,
397 tx_get_progress: rs_modbus_tx_get_alstate_progress,
eb552978
JI
398 get_eventinfo: Some(ModbusEvent::get_event_info),
399 get_eventinfo_byid: Some(ModbusEvent::get_event_info_by_id),
a458a94d
SD
400 localstorage_new: None,
401 localstorage_free: None,
402 get_files: None,
d71bcd82 403 get_tx_iterator: Some(applayer::state_get_tx_iterator::<ModbusState, ModbusTransaction>),
a458a94d
SD
404 get_tx_data: rs_modbus_state_get_tx_data,
405 apply_tx_config: None,
31dccd11 406 flags: 0,
a458a94d
SD
407 truncate: None,
408 };
409
410 let ip_proto_str = CString::new("tcp").unwrap();
ea4a509a 411 if AppLayerProtoDetectConfProtoDetectionEnabledDefault(ip_proto_str.as_ptr(), parser.name, false) != 0 {
a458a94d
SD
412 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
413 ALPROTO_MODBUS = alproto;
414 if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
415 let _ = AppLayerRegisterParser(&parser, alproto);
416 }
417 }
418}
419
420// This struct and accessor functions are used for app-layer-modbus.c tests.
421pub mod test {
422 use super::ModbusState;
423 use sawp_modbus::{Data, Message, Read, Write};
424 use std::ffi::c_void;
425 #[repr(C)]
426 pub struct ModbusMessage(*const c_void);
427
428 #[no_mangle]
429 pub unsafe extern "C" fn rs_modbus_message_get_function(msg: *const ModbusMessage) -> u8 {
430 let msg = msg.as_ref().unwrap().0 as *const Message;
431 let msg = msg.as_ref().unwrap();
432 msg.function.raw
433 }
434
435 #[no_mangle]
436 pub unsafe extern "C" fn rs_modbus_message_get_subfunction(msg: *const ModbusMessage) -> u16 {
437 let msg = msg.as_ref().unwrap().0 as *const Message;
438 let msg = msg.as_ref().unwrap();
439 if let Data::Diagnostic { func, data: _ } = &msg.data {
440 func.raw
441 } else {
442 panic!("wrong modbus message data type");
443 }
444 }
445
446 #[no_mangle]
447 pub unsafe extern "C" fn rs_modbus_message_get_read_request_address(
448 msg: *const ModbusMessage,
449 ) -> u16 {
450 let msg = msg.as_ref().unwrap().0 as *const Message;
451 let msg = msg.as_ref().unwrap();
452 if let Data::Read(Read::Request {
453 address,
454 quantity: _,
455 }) = &msg.data
456 {
457 *address
458 } else {
459 panic!("wrong modbus message data type");
460 }
461 }
462
463 #[no_mangle]
464 pub unsafe extern "C" fn rs_modbus_message_get_read_request_quantity(
465 msg: *const ModbusMessage,
466 ) -> u16 {
467 let msg = msg.as_ref().unwrap().0 as *const Message;
468 let msg = msg.as_ref().unwrap();
469 if let Data::Read(Read::Request {
470 address: _,
471 quantity,
472 }) = &msg.data
473 {
474 *quantity
475 } else {
476 panic!("wrong modbus message data type");
477 }
478 }
479
480 #[no_mangle]
481 pub unsafe extern "C" fn rs_modbus_message_get_rw_multreq_read_address(
482 msg: *const ModbusMessage,
483 ) -> u16 {
484 let msg = msg.as_ref().unwrap().0 as *const Message;
485 let msg = msg.as_ref().unwrap();
486 if let Data::ReadWrite {
487 read:
488 Read::Request {
489 address,
490 quantity: _,
491 },
492 write: _,
493 } = &msg.data
494 {
495 *address
496 } else {
497 panic!("wrong modbus message data type");
498 }
499 }
500
501 #[no_mangle]
502 pub unsafe extern "C" fn rs_modbus_message_get_rw_multreq_read_quantity(
503 msg: *const ModbusMessage,
504 ) -> u16 {
505 let msg = msg.as_ref().unwrap().0 as *const Message;
506 let msg = msg.as_ref().unwrap();
507 if let Data::ReadWrite {
508 read:
509 Read::Request {
510 address: _,
511 quantity,
512 },
513 write: _,
514 } = &msg.data
515 {
516 *quantity
517 } else {
518 panic!("wrong modbus message data type");
519 }
520 }
521
522 #[no_mangle]
523 pub unsafe extern "C" fn rs_modbus_message_get_rw_multreq_write_address(
524 msg: *const ModbusMessage,
525 ) -> u16 {
526 let msg = msg.as_ref().unwrap().0 as *const Message;
527 let msg = msg.as_ref().unwrap();
528 if let Data::ReadWrite {
529 read: _,
530 write:
531 Write::MultReq {
532 address,
533 quantity: _,
534 data: _,
535 },
536 } = &msg.data
537 {
538 *address
539 } else {
540 panic!("wrong modbus message data type");
541 }
542 }
543
544 #[no_mangle]
545 pub unsafe extern "C" fn rs_modbus_message_get_rw_multreq_write_quantity(
546 msg: *const ModbusMessage,
547 ) -> u16 {
548 let msg = msg.as_ref().unwrap().0 as *const Message;
549 let msg = msg.as_ref().unwrap();
550 if let Data::ReadWrite {
551 read: _,
552 write:
553 Write::MultReq {
554 address: _,
555 quantity,
556 data: _,
557 },
558 } = &msg.data
559 {
560 *quantity
561 } else {
562 panic!("wrong modbus message data type");
563 }
564 }
565
566 #[no_mangle]
567 pub unsafe extern "C" fn rs_modbus_message_get_rw_multreq_write_data(
568 msg: *const ModbusMessage, data_len: *mut usize,
569 ) -> *const u8 {
570 let msg = msg.as_ref().unwrap().0 as *const Message;
571 let msg = msg.as_ref().unwrap();
572 if let Data::ReadWrite {
573 read: _,
574 write:
575 Write::MultReq {
576 address: _,
577 quantity: _,
578 data,
579 },
580 } = &msg.data
581 {
582 *data_len = data.len();
583 data.as_slice().as_ptr()
584 } else {
585 panic!("wrong modbus message data type");
586 }
587 }
588
589 #[no_mangle]
590 pub unsafe extern "C" fn rs_modbus_message_get_write_multreq_address(
591 msg: *const ModbusMessage,
592 ) -> u16 {
593 let msg = msg.as_ref().unwrap().0 as *const Message;
594 let msg = msg.as_ref().unwrap();
595 if let Data::Write(Write::MultReq {
596 address,
597 quantity: _,
598 data: _,
599 }) = &msg.data
600 {
601 *address
602 } else {
603 panic!("wrong modbus message data type");
604 }
605 }
606
607 #[no_mangle]
608 pub unsafe extern "C" fn rs_modbus_message_get_write_multreq_quantity(
609 msg: *const ModbusMessage,
610 ) -> u16 {
611 let msg = msg.as_ref().unwrap().0 as *const Message;
612 let msg = msg.as_ref().unwrap();
613 if let Data::Write(Write::MultReq {
614 address: _,
615 quantity,
616 data: _,
617 }) = &msg.data
618 {
619 *quantity
620 } else {
621 panic!("wrong modbus message data type");
622 }
623 }
624
625 #[no_mangle]
626 pub unsafe extern "C" fn rs_modbus_message_get_write_multreq_data(
627 msg: *const ModbusMessage, data_len: *mut usize,
628 ) -> *const u8 {
629 let msg = msg.as_ref().unwrap().0 as *const Message;
630 let msg = msg.as_ref().unwrap();
631 if let Data::Write(Write::MultReq {
632 address: _,
633 quantity: _,
634 data,
635 }) = &msg.data
636 {
637 *data_len = data.len();
638 data.as_slice().as_ptr()
639 } else {
640 panic!("wrong modbus message data type");
641 }
642 }
643
644 #[no_mangle]
645 pub unsafe extern "C" fn rs_modbus_message_get_and_mask(msg: *const ModbusMessage) -> u16 {
646 let msg = msg.as_ref().unwrap().0 as *const Message;
647 let msg = msg.as_ref().unwrap();
648 if let Data::Write(Write::Mask {
649 address: _,
650 and_mask,
651 or_mask: _,
652 }) = &msg.data
653 {
654 *and_mask
655 } else {
656 panic!("wrong modbus message data type");
657 }
658 }
659
660 #[no_mangle]
661 pub unsafe extern "C" fn rs_modbus_message_get_or_mask(msg: *const ModbusMessage) -> u16 {
662 let msg = msg.as_ref().unwrap().0 as *const Message;
663 let msg = msg.as_ref().unwrap();
664 if let Data::Write(Write::Mask {
665 address: _,
666 and_mask: _,
667 or_mask,
668 }) = &msg.data
669 {
670 *or_mask
671 } else {
672 panic!("wrong modbus message data type");
673 }
674 }
675
676 #[no_mangle]
677 pub unsafe extern "C" fn rs_modbus_message_get_write_address(msg: *const ModbusMessage) -> u16 {
678 let msg = msg.as_ref().unwrap().0 as *const Message;
679 let msg = msg.as_ref().unwrap();
680 if let Data::Write(Write::Other { address, data: _ }) = &msg.data {
681 *address
682 } else {
683 panic!("wrong modbus message data type");
684 }
685 }
686
687 #[no_mangle]
688 pub unsafe extern "C" fn rs_modbus_message_get_write_data(msg: *const ModbusMessage) -> u16 {
689 let msg = msg.as_ref().unwrap().0 as *const Message;
690 let msg = msg.as_ref().unwrap();
691 if let Data::Write(Write::Other { address: _, data }) = &msg.data {
692 *data
693 } else {
694 panic!("wrong modbus message data type");
695 }
696 }
697
698 #[no_mangle]
699 pub unsafe extern "C" fn rs_modbus_message_get_bytevec_data(
700 msg: *const ModbusMessage, data_len: *mut usize,
701 ) -> *const u8 {
702 let msg = msg.as_ref().unwrap().0 as *const Message;
703 let msg = msg.as_ref().unwrap();
704 if let Data::ByteVec(data) = &msg.data {
705 *data_len = data.len();
706 data.as_slice().as_ptr()
707 } else {
708 panic!("wrong modbus message data type");
709 }
710 }
711
712 #[no_mangle]
363b5f99 713 pub unsafe extern "C" fn rs_modbus_state_get_tx_request(
a458a94d
SD
714 state: *mut std::os::raw::c_void, tx_id: u64,
715 ) -> ModbusMessage {
716 let state = cast_pointer!(state, ModbusState);
717 if let Some(tx) = state.get_tx(tx_id) {
718 if let Some(request) = &tx.request {
719 ModbusMessage((request as *const Message) as *const c_void)
720 } else {
721 ModbusMessage(std::ptr::null())
722 }
723 } else {
724 ModbusMessage(std::ptr::null())
725 }
726 }
727
728 #[no_mangle]
363b5f99 729 pub unsafe extern "C" fn rs_modbus_state_get_tx_response(
a458a94d
SD
730 state: *mut std::os::raw::c_void, tx_id: u64,
731 ) -> ModbusMessage {
732 let state = cast_pointer!(state, ModbusState);
733 if let Some(tx) = state.get_tx(tx_id) {
734 if let Some(response) = &tx.response {
735 ModbusMessage((response as *const Message) as *const c_void)
736 } else {
737 ModbusMessage(std::ptr::null())
738 }
739 } else {
740 ModbusMessage(std::ptr::null())
741 }
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use super::*;
748 use sawp_modbus::{
749 Data, Diagnostic, DiagnosticSubfunction, Exception, ExceptionCode, FunctionCode, Read,
750 Write,
751 };
752
753 const INVALID_FUNC_CODE: &[u8] = &[
754 0x00, 0x00, // Transaction ID
755 0x00, 0x00, // Protocol ID
756 0x00, 0x02, // Length
757 0x00, // Unit ID
758 0x00, // Function code
759 ];
760
761 const RD_COILS_REQ: &[u8] = &[
762 0x00, 0x00, // Transaction ID
763 0x00, 0x00, // Protocol ID
764 0x00, 0x06, // Length
765 0x00, // Unit ID
766 0x01, // Function code
767 0x78, 0x90, // Starting Address
768 0x00, 0x13, // Quantity of coils
769 ];
770
771 const RD_COILS_RESP: &[u8] = &[
772 0x00, 0x00, // Transaction ID
773 0x00, 0x00, // Protocol ID
774 0x00, 0x06, // Length
775 0x00, // Unit ID
776 0x01, // Function code
777 0x03, // Byte count
778 0xCD, 0x6B, 0x05, // Coil Status
779 ];
780
781 const RD_COILS_ERR_RESP: &[u8] = &[
782 0x00, 0x00, // Transaction ID
783 0x00, 0x00, // Protocol ID
784 0x00, 0x03, // Length
785 0x00, // Unit ID
786 0x81, // Function code
787 0xFF, // Exception code
788 ];
789
790 const WR_SINGLE_REG_REQ: &[u8] = &[
791 0x00, 0x0A, // Transaction ID
792 0x00, 0x00, // Protocol ID
793 0x00, 0x06, // Length
794 0x00, // Unit ID
795 0x06, // Function code
796 0x00, 0x01, // Register Address
797 0x00, 0x03, // Register Value
798 ];
799
800 const INVALID_WR_SINGLE_REG_REQ: &[u8] = &[
801 0x00, 0x0A, // Transaction ID
802 0x00, 0x00, // Protocol ID
803 0x00, 0x04, // Length
804 0x00, // Unit ID
805 0x06, // Function code
806 0x00, 0x01, // Register Address
807 ];
808
809 const WR_SINGLE_REG_RESP: &[u8] = &[
810 0x00, 0x0A, // Transaction ID
811 0x00, 0x00, // Protocol ID
812 0x00, 0x06, // Length
813 0x00, // Unit ID
814 0x06, // Function code
815 0x00, 0x01, // Register Address
816 0x00, 0x03, // Register Value
817 ];
818
819 const WR_MULT_REG_REQ: &[u8] = &[
820 0x00, 0x0A, // Transaction ID
821 0x00, 0x00, // Protocol ID
822 0x00, 0x0B, // Length
823 0x00, // Unit ID
824 0x10, // Function code
825 0x00, 0x01, // Starting Address
826 0x00, 0x02, // Quantity of Registers
827 0x04, // Byte count
828 0x00, 0x0A, // Registers Value
829 0x01, 0x02,
830 ];
831
832 const INVALID_PDU_WR_MULT_REG_REQ: &[u8] = &[
833 0x00, 0x0A, // Transaction ID
834 0x00, 0x00, // Protocol ID
835 0x00, 0x02, // Length
836 0x00, // Unit ID
837 0x10, // Function code
838 ];
839
840 const WR_MULT_REG_RESP: &[u8] = &[
841 0x00, 0x0A, // Transaction ID
842 0x00, 0x00, // Protocol ID
843 0x00, 0x06, // Length
844 0x00, // Unit ID
845 0x10, // Function code
846 0x00, 0x01, // Starting Address
847 0x00, 0x02, // Quantity of Registers
848 ];
849
850 const MASK_WR_REG_REQ: &[u8] = &[
851 0x00, 0x0A, // Transaction ID
852 0x00, 0x00, // Protocol ID
853 0x00, 0x08, // Length
854 0x00, // Unit ID
855 0x16, // Function code
856 0x00, 0x04, // Reference Address
857 0x00, 0xF2, // And_Mask
858 0x00, 0x25, // Or_Mask
859 ];
860
861 const INVALID_MASK_WR_REG_REQ: &[u8] = &[
862 0x00, 0x0A, // Transaction ID
863 0x00, 0x00, // Protocol ID
864 0x00, 0x06, // Length
865 0x00, // Unit ID
866 0x16, // Function code
867 0x00, 0x04, // Reference Address
868 0x00, 0xF2, // And_Mask
869 ];
870
871 const MASK_WR_REG_RESP: &[u8] = &[
872 0x00, 0x0A, // Transaction ID
873 0x00, 0x00, // Protocol ID
874 0x00, 0x08, // Length
875 0x00, // Unit ID
876 0x16, // Function code
877 0x00, 0x04, // Reference Address
878 0x00, 0xF2, // And_Mask
879 0x00, 0x25, // Or_Mask
880 ];
881
882 const RD_WR_MULT_REG_REQ: &[u8] = &[
883 0x12, 0x34, // Transaction ID
884 0x00, 0x00, // Protocol ID
885 0x00, 0x11, // Length
886 0x00, // Unit ID
887 0x17, // Function code
888 0x00, 0x03, // Read Starting Address
889 0x00, 0x06, // Quantity to Read
890 0x00, 0x0E, // Write Starting Address
891 0x00, 0x03, // Quantity to Write
892 0x06, // Write Byte count
893 0x12, 0x34, // Write Registers Value
894 0x56, 0x78, 0x9A, 0xBC,
895 ];
896
897 // Mismatch value in Byte count 0x0B instead of 0x0C
898 const RD_WR_MULT_REG_RESP: &[u8] = &[
899 0x12, 0x34, // Transaction ID
900 0x00, 0x00, // Protocol ID
901 0x00, 0x0E, // Length
902 0x00, // Unit ID
903 0x17, // Function code
904 0x0B, // Byte count
905 0x00, 0xFE, // Read Registers Value
906 0x0A, 0xCD, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0D, 0x00,
907 ];
908
909 const FORCE_LISTEN_ONLY_MODE: &[u8] = &[
910 0x0A, 0x00, // Transaction ID
911 0x00, 0x00, // Protocol ID
912 0x00, 0x06, // Length
913 0x00, // Unit ID
914 0x08, // Function code
915 0x00, 0x04, // Sub-function code
916 0x00, 0x00, // Data
917 ];
918
919 const INVALID_PROTO_REQ: &[u8] = &[
920 0x00, 0x00, // Transaction ID
921 0x00, 0x01, // Protocol ID
922 0x00, 0x06, // Length
923 0x00, // Unit ID
924 0x01, // Function code
925 0x78, 0x90, // Starting Address
926 0x00, 0x13, // Quantity of coils
927 ];
928
929 const INVALID_LEN_WR_MULT_REG_REQ: &[u8] = &[
930 0x00, 0x0A, // Transaction ID
931 0x00, 0x00, // Protocol ID
932 0x00, 0x09, // Length
933 0x00, // Unit ID
934 0x10, // Function code
935 0x00, 0x01, // Starting Address
936 0x00, 0x02, // Quantity of Registers
937 0x04, // Byte count
938 0x00, 0x0A, // Registers Value
939 0x01, 0x02,
940 ];
941
942 const EXCEEDED_LEN_WR_MULT_REG_REQ: &[u8] = &[
943 0x00, 0x0A, // Transaction ID
944 0x00, 0x00, // Protocol ID
945 0xff, 0xfa, // Length
946 0x00, // Unit ID
947 0x10, // Function code
948 0x00, 0x01, // Starting Address
949 0x7f, 0xf9, // Quantity of Registers
950 0xff, // Byte count
951 ];
952
953 #[test]
954 fn read_coils() {
955 let mut state = ModbusState::new();
956 assert_eq!(
957 AppLayerResult::ok(),
69cf5c9e 958 state.parse(RD_COILS_REQ, Direction::ToServer)
a458a94d
SD
959 );
960 assert_eq!(state.transactions.len(), 1);
961
962 let tx = &state.transactions[0];
963 let msg = tx.request.as_ref().unwrap();
964 assert_eq!(msg.function.code, FunctionCode::RdCoils);
965 assert_eq!(
966 msg.data,
967 Data::Read(Read::Request {
968 address: 0x7890,
969 quantity: 0x0013
970 })
971 );
972
973 assert_eq!(
974 AppLayerResult::ok(),
69cf5c9e 975 state.parse(RD_COILS_RESP, Direction::ToClient)
a458a94d
SD
976 );
977 assert_eq!(state.transactions.len(), 1);
978
979 let tx = &state.transactions[0];
980 let msg = tx.response.as_ref().unwrap();
981 assert_eq!(msg.function.code, FunctionCode::RdCoils);
982 assert_eq!(msg.data, Data::Read(Read::Response(vec![0xCD, 0x6B, 0x05])));
983 }
984
985 #[test]
986 fn write_multiple_registers() {
987 let mut state = ModbusState::new();
988 assert_eq!(
989 AppLayerResult::ok(),
69cf5c9e 990 state.parse(WR_MULT_REG_REQ, Direction::ToServer)
a458a94d
SD
991 );
992 assert_eq!(state.transactions.len(), 1);
993
994 let tx = &state.transactions[0];
995 let msg = tx.request.as_ref().unwrap();
996 assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
997 assert_eq!(
998 msg.data,
999 Data::Write(Write::MultReq {
1000 address: 0x0001,
1001 quantity: 0x0002,
1002 data: vec![0x00, 0x0a, 0x01, 0x02],
1003 })
1004 );
1005
1006 assert_eq!(
1007 AppLayerResult::ok(),
69cf5c9e 1008 state.parse(WR_MULT_REG_RESP, Direction::ToClient)
a458a94d
SD
1009 );
1010 assert_eq!(state.transactions.len(), 1);
1011
1012 let tx = &state.transactions[0];
1013 let msg = tx.response.as_ref().unwrap();
1014 assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
1015 assert_eq!(
1016 msg.data,
1017 Data::Write(Write::Other {
1018 address: 0x0001,
1019 data: 0x0002
1020 })
1021 );
1022 }
1023
1024 #[test]
1025 fn read_write_multiple_registers() {
1026 let mut state = ModbusState::new();
1027 assert_eq!(
1028 AppLayerResult::ok(),
69cf5c9e 1029 state.parse(RD_WR_MULT_REG_REQ, Direction::ToServer)
a458a94d
SD
1030 );
1031 assert_eq!(state.transactions.len(), 1);
1032
1033 let tx = &state.transactions[0];
1034 let msg = tx.request.as_ref().unwrap();
1035 assert_eq!(msg.function.code, FunctionCode::RdWrMultRegs);
1036 assert_eq!(
1037 msg.data,
1038 Data::ReadWrite {
1039 read: Read::Request {
1040 address: 0x0003,
1041 quantity: 0x0006,
1042 },
1043 write: Write::MultReq {
1044 address: 0x000e,
1045 quantity: 0x0003,
1046 data: vec![0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]
1047 }
1048 }
1049 );
1050
1051 assert_eq!(
1052 AppLayerResult::ok(),
69cf5c9e 1053 state.parse(RD_WR_MULT_REG_RESP, Direction::ToClient)
a458a94d
SD
1054 );
1055 assert_eq!(state.transactions.len(), 1);
1056
1057 let tx = &state.transactions[0];
1058 let msg = tx.response.as_ref().unwrap();
1059 assert_eq!(msg.function.code, FunctionCode::RdWrMultRegs);
1060 assert_eq!(
1061 msg.data,
1062 Data::Read(Read::Response(vec![
1063 0x00, 0xFE, 0x0A, 0xCD, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0D, 0x00,
1064 ]))
1065 );
1066 }
1067
1068 #[test]
1069 fn force_listen_only_mode() {
1070 let mut state = ModbusState::new();
1071 assert_eq!(
1072 AppLayerResult::ok(),
69cf5c9e 1073 state.parse(FORCE_LISTEN_ONLY_MODE, Direction::ToServer)
a458a94d
SD
1074 );
1075 assert_eq!(state.transactions.len(), 1);
1076
1077 let tx = &state.transactions[0];
1078 let msg = tx.request.as_ref().unwrap();
1079 assert_eq!(msg.function.code, FunctionCode::Diagnostic);
1080 assert_eq!(
1081 msg.data,
1082 Data::Diagnostic {
1083 func: Diagnostic {
1084 raw: 4,
1085 code: DiagnosticSubfunction::ForceListenOnlyMode
1086 },
1087 data: vec![0x00, 0x00]
1088 }
1089 );
1090 }
1091
1092 #[test]
1093 fn invalid_protocol_version() {
1094 let mut state = ModbusState::new();
1095 assert_eq!(
1096 AppLayerResult::ok(),
69cf5c9e 1097 state.parse(INVALID_PROTO_REQ, Direction::ToServer)
a458a94d
SD
1098 );
1099
1100 assert_eq!(state.transactions.len(), 1);
1101 let tx = &state.transactions[0];
1102 let msg = tx.request.as_ref().unwrap();
1103 assert_eq!(msg.error_flags, ErrorFlags::PROTO_ID);
1104 }
1105
1106 #[test]
1107 fn unsolicited_response() {
1108 let mut state = ModbusState::new();
1109 assert_eq!(
1110 AppLayerResult::ok(),
69cf5c9e 1111 state.parse(RD_COILS_RESP, Direction::ToClient)
a458a94d
SD
1112 );
1113 assert_eq!(state.transactions.len(), 1);
1114
1115 let tx = &state.transactions[0];
1116 let msg = tx.response.as_ref().unwrap();
1117 assert_eq!(msg.function.code, FunctionCode::RdCoils);
1118 assert_eq!(msg.data, Data::Read(Read::Response(vec![0xCD, 0x6B, 0x05])));
1119 }
1120
1121 #[test]
1122 fn invalid_length_request() {
1123 let mut state = ModbusState::new();
1124 assert_eq!(
1125 AppLayerResult::incomplete(15, 4),
69cf5c9e 1126 state.parse(INVALID_LEN_WR_MULT_REG_REQ, Direction::ToServer)
a458a94d
SD
1127 );
1128 assert_eq!(state.transactions.len(), 1);
1129
1130 let tx = &state.transactions[0];
1131 let msg = tx.request.as_ref().unwrap();
1132 assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
1133 assert_eq!(
1134 msg.data,
1135 Data::Write(Write::MultReq {
1136 address: 0x0001,
1137 quantity: 0x0002,
1138 data: vec![0x00, 0x0a]
1139 })
1140 );
1141 assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
1142 }
1143
1144 #[test]
1145 fn exception_code_invalid() {
1146 let mut state = ModbusState::new();
1147 assert_eq!(
1148 AppLayerResult::ok(),
69cf5c9e 1149 state.parse(RD_COILS_REQ, Direction::ToServer)
a458a94d
SD
1150 );
1151 assert_eq!(state.transactions.len(), 1);
1152
1153 let tx = &state.transactions[0];
1154 let msg = tx.request.as_ref().unwrap();
1155 assert_eq!(msg.function.code, FunctionCode::RdCoils);
1156 assert_eq!(
1157 msg.data,
1158 Data::Read(Read::Request {
1159 address: 0x7890,
1160 quantity: 0x0013
1161 })
1162 );
1163
1164 assert_eq!(
1165 AppLayerResult::ok(),
69cf5c9e 1166 state.parse(RD_COILS_ERR_RESP, Direction::ToClient)
a458a94d
SD
1167 );
1168 assert_eq!(state.transactions.len(), 1);
1169
1170 let tx = &state.transactions[0];
1171 let msg = tx.response.as_ref().unwrap();
1172 assert_eq!(
1173 msg.data,
1174 Data::Exception(Exception {
1175 raw: 255,
1176 code: ExceptionCode::Unknown
1177 })
1178 );
1179 assert_eq!(msg.error_flags, ErrorFlags::EXC_CODE);
1180 }
1181
1182 #[test]
1183 fn fragmentation_1_adu_in_2_tcp_packets() {
1184 let mut state = ModbusState::new();
1185 assert_eq!(
1186 AppLayerResult::incomplete(0, 15),
1187 state.parse(
1188 &RD_COILS_REQ[0..(RD_COILS_REQ.len() - 3)],
1189 Direction::ToServer
1190 )
1191 );
1192 assert_eq!(state.transactions.len(), 0);
1193 assert_eq!(
1194 AppLayerResult::ok(),
69cf5c9e 1195 state.parse(RD_COILS_REQ, Direction::ToServer)
a458a94d
SD
1196 );
1197 assert_eq!(state.transactions.len(), 1);
1198
1199 let tx = &state.transactions[0];
1200 assert!(&tx.request.is_some());
1201 let msg = tx.request.as_ref().unwrap();
1202 assert_eq!(msg.function.code, FunctionCode::RdCoils);
1203 assert_eq!(
1204 msg.data,
1205 Data::Read(Read::Request {
1206 address: 0x7890,
1207 quantity: 0x0013
1208 })
1209 );
1210 }
1211
1212 #[test]
1213 fn fragmentation_2_adu_in_1_tcp_packet() {
1214 let req = [RD_COILS_REQ, WR_MULT_REG_REQ].concat();
1215 let resp = [RD_COILS_RESP, WR_MULT_REG_RESP].concat();
1216
1217 let mut state = ModbusState::new();
1218 assert_eq!(AppLayerResult::ok(), state.parse(&req, Direction::ToServer));
1219 assert_eq!(state.transactions.len(), 2);
1220
1221 let tx = &state.transactions[0];
1222 let msg = tx.request.as_ref().unwrap();
1223 assert_eq!(msg.function.code, FunctionCode::RdCoils);
1224 assert_eq!(
1225 msg.data,
1226 Data::Read(Read::Request {
1227 address: 0x7890,
1228 quantity: 0x0013
1229 })
1230 );
1231
1232 let tx = &state.transactions[1];
1233 let msg = tx.request.as_ref().unwrap();
1234 assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
1235 assert_eq!(
1236 msg.data,
1237 Data::Write(Write::MultReq {
1238 address: 0x0001,
1239 quantity: 0x0002,
1240 data: vec![0x00, 0x0a, 0x01, 0x02]
1241 })
1242 );
1243
1244 assert_eq!(
1245 AppLayerResult::ok(),
1246 state.parse(&resp, Direction::ToClient)
1247 );
1248 assert_eq!(state.transactions.len(), 2);
1249
1250 let tx = &state.transactions[0];
1251 let msg = tx.response.as_ref().unwrap();
1252 assert_eq!(msg.function.code, FunctionCode::RdCoils);
1253 assert_eq!(msg.data, Data::Read(Read::Response(vec![0xCD, 0x6B, 0x05])));
1254
1255 let tx = &state.transactions[1];
1256 let msg = tx.response.as_ref().unwrap();
1257 assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
1258 assert_eq!(
1259 msg.data,
1260 Data::Write(Write::Other {
1261 address: 0x0001,
1262 data: 0x0002
1263 })
1264 );
1265 }
1266
1267 #[test]
1268 fn exceeded_length_request() {
1269 let mut state = ModbusState::new();
1270 assert_eq!(
1271 AppLayerResult::ok(),
69cf5c9e 1272 state.parse(EXCEEDED_LEN_WR_MULT_REG_REQ, Direction::ToServer)
a458a94d
SD
1273 );
1274
1275 assert_eq!(state.transactions.len(), 1);
1276 let tx = &state.transactions[0];
1277 let msg = tx.request.as_ref().unwrap();
1278 assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
1279 }
1280
1281 #[test]
1282 fn invalid_pdu_len_req() {
1283 let mut state = ModbusState::new();
1284 assert_eq!(
1285 AppLayerResult::ok(),
69cf5c9e 1286 state.parse(INVALID_PDU_WR_MULT_REG_REQ, Direction::ToServer)
a458a94d
SD
1287 );
1288
1289 assert_eq!(state.transactions.len(), 1);
1290
1291 let tx = &state.transactions[0];
1292 let msg = tx.request.as_ref().unwrap();
1293 assert_eq!(msg.function.code, FunctionCode::WrMultRegs);
1294 assert_eq!(msg.data, Data::ByteVec(vec![]));
1295 }
1296
1297 #[test]
1298 fn mask_write_register_request() {
1299 let mut state = ModbusState::new();
1300 assert_eq!(
1301 AppLayerResult::ok(),
69cf5c9e 1302 state.parse(MASK_WR_REG_REQ, Direction::ToServer)
a458a94d
SD
1303 );
1304 assert_eq!(state.transactions.len(), 1);
1305
1306 let tx = &state.transactions[0];
1307 let msg = tx.request.as_ref().unwrap();
1308 assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
1309 assert_eq!(
1310 msg.data,
1311 Data::Write(Write::Mask {
1312 address: 0x0004,
1313 and_mask: 0x00f2,
1314 or_mask: 0x0025
1315 })
1316 );
1317
1318 assert_eq!(
1319 AppLayerResult::ok(),
69cf5c9e 1320 state.parse(MASK_WR_REG_RESP, Direction::ToClient)
a458a94d
SD
1321 );
1322 assert_eq!(state.transactions.len(), 1);
1323
1324 let tx = &state.transactions[0];
1325 let msg = tx.response.as_ref().unwrap();
1326 assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
1327 assert_eq!(
1328 msg.data,
1329 Data::Write(Write::Mask {
1330 address: 0x0004,
1331 and_mask: 0x00f2,
1332 or_mask: 0x0025
1333 })
1334 );
1335 }
1336
1337 #[test]
1338 fn write_single_register_request() {
1339 let mut state = ModbusState::new();
1340 assert_eq!(
1341 AppLayerResult::ok(),
69cf5c9e 1342 state.parse(WR_SINGLE_REG_REQ, Direction::ToServer)
a458a94d
SD
1343 );
1344 assert_eq!(state.transactions.len(), 1);
1345
1346 let tx = &state.transactions[0];
1347 let msg = tx.request.as_ref().unwrap();
1348 assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
1349 assert_eq!(
1350 msg.data,
1351 Data::Write(Write::Other {
1352 address: 0x0001,
1353 data: 0x0003
1354 })
1355 );
1356
1357 assert_eq!(
1358 AppLayerResult::ok(),
69cf5c9e 1359 state.parse(WR_SINGLE_REG_RESP, Direction::ToClient)
a458a94d
SD
1360 );
1361 assert_eq!(state.transactions.len(), 1);
1362
1363 let tx = &state.transactions[0];
1364 let msg = tx.response.as_ref().unwrap();
1365 assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
1366 assert_eq!(
1367 msg.data,
1368 Data::Write(Write::Other {
1369 address: 0x0001,
1370 data: 0x0003
1371 })
1372 );
1373 }
1374
1375 #[test]
1376 fn invalid_mask_write_register_request() {
1377 let mut state = ModbusState::new();
1378 assert_eq!(
1379 AppLayerResult::ok(),
69cf5c9e 1380 state.parse(INVALID_MASK_WR_REG_REQ, Direction::ToServer)
a458a94d
SD
1381 );
1382 assert_eq!(state.transactions.len(), 1);
1383
1384 let tx = &state.transactions[0];
1385 let msg = tx.request.as_ref().unwrap();
1386 assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
1387 assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
1388 assert_eq!(msg.data, Data::ByteVec(vec![0x00, 0x04, 0x00, 0xF2]));
1389
1390 assert_eq!(
1391 AppLayerResult::ok(),
69cf5c9e 1392 state.parse(MASK_WR_REG_RESP, Direction::ToClient)
a458a94d
SD
1393 );
1394 assert_eq!(state.transactions.len(), 1);
1395
1396 let tx = &state.transactions[0];
1397 let msg = tx.response.as_ref().unwrap();
1398 assert_eq!(msg.function.code, FunctionCode::MaskWrReg);
1399 assert_eq!(
1400 msg.data,
1401 Data::Write(Write::Mask {
1402 address: 0x0004,
1403 and_mask: 0x00f2,
1404 or_mask: 0x0025
1405 })
1406 );
1407 }
1408
1409 #[test]
1410 fn invalid_write_single_register_request() {
1411 let mut state = ModbusState::new();
1412 assert_eq!(
1413 AppLayerResult::ok(),
69cf5c9e 1414 state.parse(INVALID_WR_SINGLE_REG_REQ, Direction::ToServer)
a458a94d
SD
1415 );
1416 assert_eq!(state.transactions.len(), 1);
1417
1418 let tx = &state.transactions[0];
1419 let msg = tx.request.as_ref().unwrap();
1420 assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
1421 assert_eq!(msg.error_flags, ErrorFlags::DATA_LENGTH);
1422 assert_eq!(msg.data, Data::ByteVec(vec![0x00, 0x01]));
1423
1424 assert_eq!(
1425 AppLayerResult::ok(),
69cf5c9e 1426 state.parse(WR_SINGLE_REG_RESP, Direction::ToClient)
a458a94d
SD
1427 );
1428 assert_eq!(state.transactions.len(), 1);
1429
1430 let tx = &state.transactions[0];
1431 let msg = tx.response.as_ref().unwrap();
1432 assert_eq!(msg.function.code, FunctionCode::WrSingleReg);
1433 assert_eq!(
1434 msg.data,
1435 Data::Write(Write::Other {
1436 address: 0x0001,
1437 data: 0x0003
1438 })
1439 );
1440 }
1441
1442 #[test]
1443 fn invalid_function_code() {
1444 let mut state = ModbusState::new();
1445 assert_eq!(
1446 AppLayerResult::ok(),
69cf5c9e 1447 state.parse(INVALID_FUNC_CODE, Direction::ToServer)
a458a94d
SD
1448 );
1449 assert_eq!(state.transactions.len(), 1);
1450
1451 let tx = &state.transactions[0];
1452 let msg = tx.request.as_ref().unwrap();
1453 assert_eq!(msg.function.code, FunctionCode::Unknown);
1454 assert_eq!(msg.data, Data::ByteVec(vec![]));
1455 }
1456}