]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/rdp/rdp.rs
app-layer: include decoder events in app-layer tx data
[people/ms/suricata.git] / rust / src / rdp / rdp.rs
CommitLineData
caef8b5b
ZK
1/* Copyright (C) 2019 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 */
17
18// Author: Zach Kelly <zach.kelly@lmco.com>
19
20//! RDP application layer
21
f4b4d531 22use crate::applayer::{self, *};
9c67c634 23use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN, IPPROTO_TCP};
e68dfa46 24use crate::rdp::parser::*;
ef397dab 25use nom;
caef8b5b 26use std;
ef397dab 27use tls_parser::{parse_tls_plaintext, TlsMessage, TlsMessageHandshake, TlsRecordType};
caef8b5b
ZK
28
29static mut ALPROTO_RDP: AppProto = ALPROTO_UNKNOWN;
30
31//
32// transactions
33//
34
35#[derive(Debug, PartialEq)]
36pub struct CertificateBlob {
37 pub data: Vec<u8>,
38}
39
40#[derive(Debug, PartialEq)]
41pub enum RdpTransactionItem {
42 X224ConnectionRequest(X224ConnectionRequest),
43 X224ConnectionConfirm(X224ConnectionConfirm),
44 McsConnectRequest(McsConnectRequest),
45 McsConnectResponse(McsConnectResponse),
46 TlsCertificateChain(Vec<CertificateBlob>),
47}
48
49#[derive(Debug, PartialEq)]
50pub struct RdpTransaction {
51 pub id: u64,
52 pub item: RdpTransactionItem,
53 // managed by macros `export_tx_get_detect_state!` and `export_tx_set_detect_state!`
88dd0abb 54 tx_data: AppLayerTxData,
caef8b5b
ZK
55}
56
f4b4d531
JI
57impl Transaction for RdpTransaction {
58 fn id(&self) -> u64 {
59 self.id
60 }
61}
62
caef8b5b
ZK
63impl RdpTransaction {
64 fn new(id: u64, item: RdpTransactionItem) -> Self {
65 Self {
66 id,
67 item,
88dd0abb 68 tx_data: AppLayerTxData::new(),
caef8b5b
ZK
69 }
70 }
caef8b5b
ZK
71}
72
73#[no_mangle]
363b5f99 74pub unsafe extern "C" fn rs_rdp_state_get_tx(
ef397dab 75 state: *mut std::os::raw::c_void, tx_id: u64,
caef8b5b
ZK
76) -> *mut std::os::raw::c_void {
77 let state = cast_pointer!(state, RdpState);
78 match state.get_tx(tx_id) {
79 Some(tx) => {
53413f2d 80 return tx as *const _ as *mut _;
caef8b5b
ZK
81 }
82 None => {
83 return std::ptr::null_mut();
84 }
85 }
86}
87
88#[no_mangle]
363b5f99 89pub unsafe extern "C" fn rs_rdp_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
caef8b5b
ZK
90 let state = cast_pointer!(state, RdpState);
91 return state.next_id;
92}
93
caef8b5b
ZK
94#[no_mangle]
95pub extern "C" fn rs_rdp_tx_get_progress(
ef397dab 96 _tx: *mut std::os::raw::c_void, _direction: u8,
caef8b5b
ZK
97) -> std::os::raw::c_int {
98 // tx complete when `rs_rdp_tx_get_progress(...) == rs_rdp_tx_get_progress_complete(...)`
99 // here, all transactions are immediately complete on insert
100 return 1;
101}
102
103//
104// state
105//
106
107#[derive(Debug, PartialEq)]
108pub struct RdpState {
109 next_id: u64,
caef8b5b
ZK
110 transactions: Vec<RdpTransaction>,
111 tls_parsing: bool,
112 bypass_parsing: bool,
113}
114
f4b4d531
JI
115impl State<RdpTransaction> for RdpState {
116 fn get_transactions(&self) -> &[RdpTransaction] {
117 &self.transactions
118 }
119}
120
caef8b5b
ZK
121impl RdpState {
122 fn new() -> Self {
123 Self {
124 next_id: 0,
caef8b5b
ZK
125 transactions: Vec::new(),
126 tls_parsing: false,
127 bypass_parsing: false,
128 }
129 }
130
131 fn free_tx(&mut self, tx_id: u64) {
132 let len = self.transactions.len();
133 let mut found = false;
134 let mut index = 0;
135 for ii in 0..len {
136 let tx = &self.transactions[ii];
137 if tx.id == tx_id {
138 found = true;
139 index = ii;
140 break;
141 }
142 }
143 if found {
144 self.transactions.remove(index);
145 }
146 }
147
148 fn get_tx(&self, tx_id: u64) -> Option<&RdpTransaction> {
149 for tx in &self.transactions {
150 if tx.id == tx_id {
151 return Some(tx);
152 }
153 }
154 return None;
155 }
156
157 fn new_tx(&mut self, item: RdpTransactionItem) -> RdpTransaction {
caef8b5b 158 self.next_id += 1;
b57280ff 159 let tx = RdpTransaction::new(self.next_id, item);
caef8b5b
ZK
160 return tx;
161 }
162
163 /// parse buffer captures from client to server
b25de4d9 164 fn parse_ts(&mut self, input: &[u8]) -> AppLayerResult {
caef8b5b
ZK
165 // no need to process input buffer
166 if self.bypass_parsing {
b25de4d9 167 return AppLayerResult::ok();
caef8b5b 168 }
b25de4d9 169 let mut available = input;
caef8b5b
ZK
170
171 loop {
172 if available.len() == 0 {
b25de4d9 173 return AppLayerResult::ok();
caef8b5b
ZK
174 }
175 if self.tls_parsing {
69cf5c9e 176 match parse_tls_plaintext(available) {
caef8b5b 177 Ok((remainder, _tls)) => {
b25de4d9 178 // bytes available for futher parsing are what remain
caef8b5b
ZK
179 available = remainder;
180 }
181
182 Err(nom::Err::Incomplete(_)) => {
b25de4d9
ZK
183 // nom need not compatible with applayer need, request one more byte
184 return AppLayerResult::incomplete(
185 (input.len() - available.len()) as u32,
186 (available.len() + 1) as u32,
187 );
caef8b5b
ZK
188 }
189
190 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
b25de4d9 191 return AppLayerResult::err();
caef8b5b
ZK
192 }
193 }
194 } else {
195 // every message should be encapsulated within a T.123 tpkt
69cf5c9e 196 match parse_t123_tpkt(available) {
caef8b5b
ZK
197 // success
198 Ok((remainder, t123)) => {
b25de4d9 199 // bytes available for futher parsing are what remain
caef8b5b
ZK
200 available = remainder;
201 // evaluate message within the tpkt
202 match t123.child {
203 // X.224 connection request
204 T123TpktChild::X224ConnectionRequest(x224) => {
ef397dab
ZK
205 let tx =
206 self.new_tx(RdpTransactionItem::X224ConnectionRequest(x224));
caef8b5b
ZK
207 self.transactions.push(tx);
208 }
209
210 // X.223 data packet, evaluate what it encapsulates
211 T123TpktChild::Data(x223) => {
212 match x223.child {
213 X223DataChild::McsConnectRequest(mcs) => {
214 let tx =
215 self.new_tx(RdpTransactionItem::McsConnectRequest(mcs));
216 self.transactions.push(tx);
217 }
218 // unknown message in X.223, skip
219 _ => (),
220 }
221 }
222
223 // unknown message in T.123, skip
224 _ => (),
225 }
226 }
227
228 Err(nom::Err::Incomplete(_)) => {
b25de4d9
ZK
229 // nom need not compatible with applayer need, request one more byte
230 return AppLayerResult::incomplete(
231 (input.len() - available.len()) as u32,
232 (available.len() + 1) as u32,
233 );
caef8b5b
ZK
234 }
235
236 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
237 if probe_tls_handshake(available) {
238 self.tls_parsing = true;
3e96f961
PA
239 let r = self.parse_ts(available);
240 if r.status == 1 {
241 //adds bytes already consumed to incomplete result
242 let consumed = (input.len() - available.len()) as u32;
243 return AppLayerResult::incomplete(r.consumed + consumed, r.needed);
244 } else {
245 return r;
246 }
caef8b5b 247 } else {
b25de4d9 248 return AppLayerResult::err();
caef8b5b
ZK
249 }
250 }
251 }
252 }
253 }
254 }
255
256 /// parse buffer captures from server to client
b25de4d9 257 fn parse_tc(&mut self, input: &[u8]) -> AppLayerResult {
caef8b5b
ZK
258 // no need to process input buffer
259 if self.bypass_parsing {
b25de4d9 260 return AppLayerResult::ok();
caef8b5b 261 }
b25de4d9 262 let mut available = input;
caef8b5b
ZK
263
264 loop {
265 if available.len() == 0 {
b25de4d9 266 return AppLayerResult::ok();
caef8b5b
ZK
267 }
268 if self.tls_parsing {
69cf5c9e 269 match parse_tls_plaintext(available) {
caef8b5b 270 Ok((remainder, tls)) => {
b25de4d9 271 // bytes available for futher parsing are what remain
caef8b5b
ZK
272 available = remainder;
273 for message in &tls.msg {
274 match message {
ef397dab
ZK
275 TlsMessage::Handshake(TlsMessageHandshake::Certificate(
276 contents,
277 )) => {
caef8b5b
ZK
278 let mut chain = Vec::new();
279 for cert in &contents.cert_chain {
280 chain.push(CertificateBlob {
281 data: cert.data.to_vec(),
282 });
283 }
ef397dab
ZK
284 let tx =
285 self.new_tx(RdpTransactionItem::TlsCertificateChain(chain));
caef8b5b
ZK
286 self.transactions.push(tx);
287 self.bypass_parsing = true;
288 }
289 _ => {}
290 }
291 }
292 }
293
294 Err(nom::Err::Incomplete(_)) => {
b25de4d9
ZK
295 // nom need not compatible with applayer need, request one more byte
296 return AppLayerResult::incomplete(
297 (input.len() - available.len()) as u32,
298 (available.len() + 1) as u32,
299 );
caef8b5b
ZK
300 }
301
302 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
b25de4d9 303 return AppLayerResult::err();
caef8b5b
ZK
304 }
305 }
306 } else {
307 // every message should be encapsulated within a T.123 tpkt
69cf5c9e 308 match parse_t123_tpkt(available) {
caef8b5b
ZK
309 // success
310 Ok((remainder, t123)) => {
b25de4d9 311 // bytes available for futher parsing are what remain
caef8b5b
ZK
312 available = remainder;
313 // evaluate message within the tpkt
314 match t123.child {
315 // X.224 connection confirm
316 T123TpktChild::X224ConnectionConfirm(x224) => {
ef397dab
ZK
317 let tx =
318 self.new_tx(RdpTransactionItem::X224ConnectionConfirm(x224));
caef8b5b
ZK
319 self.transactions.push(tx);
320 }
321
322 // X.223 data packet, evaluate what it encapsulates
323 T123TpktChild::Data(x223) => {
324 match x223.child {
325 X223DataChild::McsConnectResponse(mcs) => {
326 let tx = self
327 .new_tx(RdpTransactionItem::McsConnectResponse(mcs));
328 self.transactions.push(tx);
329 self.bypass_parsing = true;
b25de4d9 330 return AppLayerResult::ok();
caef8b5b
ZK
331 }
332
333 // unknown message in X.223, skip
334 _ => (),
335 }
336 }
337
338 // unknown message in T.123, skip
339 _ => (),
340 }
341 }
342
343 Err(nom::Err::Incomplete(_)) => {
b25de4d9
ZK
344 // nom need not compatible with applayer need, request one more byte
345 return AppLayerResult::incomplete(
346 (input.len() - available.len()) as u32,
347 (available.len() + 1) as u32,
348 );
caef8b5b
ZK
349 }
350
351 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
352 if probe_tls_handshake(available) {
353 self.tls_parsing = true;
6da9a372
PA
354 let r = self.parse_tc(available);
355 if r.status == 1 {
356 //adds bytes already consumed to incomplete result
357 let consumed = (input.len() - available.len()) as u32;
358 return AppLayerResult::incomplete(r.consumed + consumed, r.needed);
359 } else {
360 return r;
361 }
caef8b5b 362 } else {
b25de4d9 363 return AppLayerResult::err();
caef8b5b
ZK
364 }
365 }
366 }
367 }
368 }
369 }
370}
371
372#[no_mangle]
547d6c2d 373pub extern "C" fn rs_rdp_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
caef8b5b
ZK
374 let state = RdpState::new();
375 let boxed = Box::new(state);
53413f2d 376 return Box::into_raw(boxed) as *mut _;
caef8b5b
ZK
377}
378
379#[no_mangle]
380pub extern "C" fn rs_rdp_state_free(state: *mut std::os::raw::c_void) {
53413f2d 381 std::mem::drop(unsafe { Box::from_raw(state as *mut RdpState) });
caef8b5b
ZK
382}
383
384#[no_mangle]
363b5f99 385pub unsafe extern "C" fn rs_rdp_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
caef8b5b
ZK
386 let state = cast_pointer!(state, RdpState);
387 state.free_tx(tx_id);
388}
389
caef8b5b
ZK
390//
391// probe
392//
393
394/// probe for T.123 type identifier, as each message is encapsulated in T.123
395fn probe_rdp(input: &[u8]) -> bool {
396 input.len() > 0 && input[0] == TpktVersion::T123 as u8
397}
398
399/// probe for T.123 message, whether to client or to server
400#[no_mangle]
363b5f99 401pub unsafe extern "C" fn rs_rdp_probe_ts_tc(
ef397dab 402 _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
caef8b5b 403) -> AppProto {
922a453d 404 if !input.is_null() {
caef8b5b
ZK
405 // probe bytes for `rdp` protocol pattern
406 let slice = build_slice!(input, input_len as usize);
407
408 // Some sessions immediately (first byte) switch to TLS/SSL, e.g.
409 // https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=view&target=rdp-ssl.pcap.gz
410 // but this callback will not be exercised, so `probe_tls_handshake` not needed here.
411 if probe_rdp(slice) {
363b5f99 412 return ALPROTO_RDP;
caef8b5b
ZK
413 }
414 }
415 return ALPROTO_UNKNOWN;
416}
417
418/// probe for TLS
419fn probe_tls_handshake(input: &[u8]) -> bool {
420 input.len() > 0 && input[0] == u8::from(TlsRecordType::Handshake)
421}
422
423//
424// parse
425//
426
427#[no_mangle]
363b5f99 428pub unsafe extern "C" fn rs_rdp_parse_ts(
ef397dab
ZK
429 _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
430 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
44d3f264 431) -> AppLayerResult {
caef8b5b
ZK
432 let state = cast_pointer!(state, RdpState);
433 let buf = build_slice!(input, input_len as usize);
434 // attempt to parse bytes as `rdp` protocol
b25de4d9 435 return state.parse_ts(buf);
caef8b5b
ZK
436}
437
438#[no_mangle]
363b5f99 439pub unsafe extern "C" fn rs_rdp_parse_tc(
ef397dab
ZK
440 _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
441 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
44d3f264 442) -> AppLayerResult {
caef8b5b
ZK
443 let state = cast_pointer!(state, RdpState);
444 let buf = build_slice!(input, input_len as usize);
445 // attempt to parse bytes as `rdp` protocol
b25de4d9 446 return state.parse_tc(buf);
caef8b5b
ZK
447}
448
88dd0abb
VJ
449export_tx_data_get!(rs_rdp_get_tx_data, RdpTransaction);
450
caef8b5b
ZK
451//
452// registration
453//
454
455const PARSER_NAME: &'static [u8] = b"rdp\0";
456
457#[no_mangle]
458pub unsafe extern "C" fn rs_rdp_register_parser() {
459 let default_port = std::ffi::CString::new("[3389]").unwrap();
460 let parser = RustParser {
461 name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
462 default_port: default_port.as_ptr(),
463 ipproto: IPPROTO_TCP,
66632465
PA
464 probe_ts: Some(rs_rdp_probe_ts_tc),
465 probe_tc: Some(rs_rdp_probe_ts_tc),
caef8b5b
ZK
466 min_depth: 0,
467 max_depth: 16,
468 state_new: rs_rdp_state_new,
469 state_free: rs_rdp_state_free,
470 tx_free: rs_rdp_state_tx_free,
471 parse_ts: rs_rdp_parse_ts,
472 parse_tc: rs_rdp_parse_tc,
473 get_tx_count: rs_rdp_state_get_tx_count,
474 get_tx: rs_rdp_state_get_tx,
efc9a7a3
VJ
475 tx_comp_st_ts: 1,
476 tx_comp_st_tc: 1,
caef8b5b 477 tx_get_progress: rs_rdp_tx_get_progress,
caef8b5b
ZK
478 get_eventinfo: None,
479 get_eventinfo_byid: None,
480 localstorage_new: None,
481 localstorage_free: None,
caef8b5b 482 get_files: None,
f4b4d531 483 get_tx_iterator: Some(applayer::state_get_tx_iterator::<RdpState, RdpTransaction>),
c94a5e63 484 get_tx_data: rs_rdp_get_tx_data,
5665fc83 485 apply_tx_config: None,
1d40d0c5 486 flags: APP_LAYER_PARSER_OPT_UNIDIR_TXS,
4da0d9bd 487 truncate: None,
caef8b5b
ZK
488 };
489
490 let ip_proto_str = std::ffi::CString::new("tcp").unwrap();
491
ef397dab 492 if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
caef8b5b
ZK
493 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
494 ALPROTO_RDP = alproto;
ef397dab 495 if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
caef8b5b
ZK
496 let _ = AppLayerRegisterParser(&parser, alproto);
497 }
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
e68dfa46 504 use crate::rdp::parser::{RdpCookie, X224ConnectionRequest};
caef8b5b
ZK
505
506 #[test]
507 fn test_probe_rdp() {
508 let buf: &[u8] = &[0x03, 0x00];
69cf5c9e 509 assert_eq!(true, probe_rdp(buf));
caef8b5b
ZK
510 }
511
512 #[test]
513 fn test_probe_rdp_other() {
514 let buf: &[u8] = &[0x04, 0x00];
69cf5c9e 515 assert_eq!(false, probe_rdp(buf));
caef8b5b
ZK
516 }
517
518 #[test]
519 fn test_probe_tls_handshake() {
520 let buf: &[u8] = &[0x16, 0x00];
69cf5c9e 521 assert_eq!(true, probe_tls_handshake(buf));
caef8b5b
ZK
522 }
523
524 #[test]
525 fn test_probe_tls_handshake_other() {
526 let buf: &[u8] = &[0x17, 0x00];
69cf5c9e 527 assert_eq!(false, probe_tls_handshake(buf));
caef8b5b
ZK
528 }
529
530 #[test]
531 fn test_parse_ts_rdp() {
532 let buf_1: &[u8] = &[0x03, 0x00, 0x00, 0x25, 0x20, 0xe0, 0x00, 0x00];
533 let buf_2: &[u8] = &[
b25de4d9
ZK
534 0x03, 0x00, 0x00, 0x25, 0x20, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6f,
535 0x6b, 0x69, 0x65, 0x3a, 0x20, 0x6d, 0x73, 0x74, 0x73, 0x68, 0x61, 0x73, 0x68, 0x3d,
536 0x75, 0x73, 0x65, 0x72, 0x31, 0x32, 0x33, 0x0d, 0x0a,
caef8b5b
ZK
537 ];
538 let mut state = RdpState::new();
b25de4d9 539 // will consume 0, request length + 1
69cf5c9e 540 assert_eq!(AppLayerResult::incomplete(0, 9), state.parse_ts(buf_1));
caef8b5b 541 assert_eq!(0, state.transactions.len());
b25de4d9 542 // exactly aligns with transaction
69cf5c9e 543 assert_eq!(AppLayerResult::ok(), state.parse_ts(buf_2));
caef8b5b 544 assert_eq!(1, state.transactions.len());
ef397dab
ZK
545 let item = RdpTransactionItem::X224ConnectionRequest(X224ConnectionRequest {
546 cdt: 0,
547 dst_ref: 0,
548 src_ref: 0,
549 class: 0,
550 options: 0,
551 cookie: Some(RdpCookie {
552 mstshash: String::from("user123"),
553 }),
554 negotiation_request: None,
555 data: Vec::new(),
556 });
caef8b5b
ZK
557 assert_eq!(item, state.transactions[0].item);
558 }
559
560 #[test]
561 fn test_parse_ts_other() {
562 let buf: &[u8] = &[0x03, 0x00, 0x00, 0x01, 0x00];
563 let mut state = RdpState::new();
69cf5c9e 564 assert_eq!(AppLayerResult::err(), state.parse_ts(buf));
caef8b5b
ZK
565 }
566
567 #[test]
568 fn test_parse_tc_rdp() {
569 let buf_1: &[u8] = &[0x03, 0x00, 0x00, 0x09, 0x02];
b25de4d9 570 let buf_2: &[u8] = &[0x03, 0x00, 0x00, 0x09, 0x02, 0xf0, 0x80, 0x7f, 0x66];
caef8b5b 571 let mut state = RdpState::new();
b25de4d9 572 // will consume 0, request length + 1
69cf5c9e 573 assert_eq!(AppLayerResult::incomplete(0, 6), state.parse_tc(buf_1));
caef8b5b 574 assert_eq!(0, state.transactions.len());
b25de4d9 575 // exactly aligns with transaction
69cf5c9e 576 assert_eq!(AppLayerResult::ok(), state.parse_tc(buf_2));
caef8b5b 577 assert_eq!(1, state.transactions.len());
ef397dab 578 let item = RdpTransactionItem::McsConnectResponse(McsConnectResponse {});
caef8b5b
ZK
579 assert_eq!(item, state.transactions[0].item);
580 }
581
582 #[test]
583 fn test_parse_tc_other() {
584 let buf: &[u8] = &[0x03, 0x00, 0x00, 0x01, 0x00];
585 let mut state = RdpState::new();
69cf5c9e 586 assert_eq!(AppLayerResult::err(), state.parse_tc(buf));
caef8b5b
ZK
587 }
588
589 #[test]
590 fn test_state_new_tx() {
591 let mut state = RdpState::new();
592 let item0 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
593 children: Vec::new(),
594 });
595 let item1 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
596 children: Vec::new(),
597 });
598 let tx0 = state.new_tx(item0);
599 let tx1 = state.new_tx(item1);
600 assert_eq!(2, state.next_id);
601 state.transactions.push(tx0);
602 state.transactions.push(tx1);
603 assert_eq!(2, state.transactions.len());
b57280ff
JI
604 assert_eq!(1, state.transactions[0].id);
605 assert_eq!(2, state.transactions[1].id);
caef8b5b
ZK
606 assert_eq!(false, state.tls_parsing);
607 assert_eq!(false, state.bypass_parsing);
608 }
609
610 #[test]
611 fn test_state_get_tx() {
612 let mut state = RdpState::new();
613 let item0 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
614 children: Vec::new(),
615 });
616 let item1 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
617 children: Vec::new(),
618 });
619 let item2 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
620 children: Vec::new(),
621 });
622 let tx0 = state.new_tx(item0);
623 let tx1 = state.new_tx(item1);
624 let tx2 = state.new_tx(item2);
625 state.transactions.push(tx0);
626 state.transactions.push(tx1);
627 state.transactions.push(tx2);
b57280ff 628 assert_eq!(Some(&state.transactions[1]), state.get_tx(2));
caef8b5b
ZK
629 }
630
631 #[test]
632 fn test_state_free_tx() {
633 let mut state = RdpState::new();
634 let item0 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
635 children: Vec::new(),
636 });
637 let item1 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
638 children: Vec::new(),
639 });
640 let item2 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
641 children: Vec::new(),
642 });
643 let tx0 = state.new_tx(item0);
644 let tx1 = state.new_tx(item1);
645 let tx2 = state.new_tx(item2);
646 state.transactions.push(tx0);
647 state.transactions.push(tx1);
648 state.transactions.push(tx2);
649 state.free_tx(1);
650 assert_eq!(3, state.next_id);
651 assert_eq!(2, state.transactions.len());
b57280ff
JI
652 assert_eq!(2, state.transactions[0].id);
653 assert_eq!(3, state.transactions[1].id);
caef8b5b
ZK
654 assert_eq!(None, state.get_tx(1));
655 }
656}