Coverage Report

Created: 2025-11-16 07:09

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