/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 | | } |