/src/suricata7/rust/src/quic/quic.rs
Line | Count | Source |
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 | | */ |
17 | | |
18 | | use super::{ |
19 | | crypto::{quic_keys_initial, QuicKeys, AES128_KEY_LEN}, |
20 | | cyu::Cyu, |
21 | | frames::{Frame, QuicTlsExtension, StreamTag}, |
22 | | parser::{quic_pkt_num, QuicData, QuicHeader, QuicType}, |
23 | | }; |
24 | | use crate::applayer::{self, *}; |
25 | | use crate::core::{AppProto, Direction, Flow, ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_UDP}; |
26 | | use std::collections::VecDeque; |
27 | | use std::ffi::CString; |
28 | | use tls_parser::TlsExtensionType; |
29 | | |
30 | | static mut ALPROTO_QUIC: AppProto = ALPROTO_UNKNOWN; |
31 | | |
32 | | const DEFAULT_DCID_LEN: usize = 16; |
33 | | const PKT_NUM_BUF_MAX_LEN: usize = 4; |
34 | | pub(super) const QUIC_MAX_CRYPTO_FRAG_LEN: u64 = 65535; |
35 | | |
36 | | #[derive(FromPrimitive, Debug, AppLayerEvent)] |
37 | | pub enum QuicEvent { |
38 | | FailedDecrypt, |
39 | | ErrorOnData, |
40 | | ErrorOnHeader, |
41 | | CryptoFragTooLong, |
42 | | } |
43 | | |
44 | | #[derive(Debug)] |
45 | | pub struct QuicTransaction { |
46 | | tx_id: u64, |
47 | | pub header: QuicHeader, |
48 | | pub cyu: Vec<Cyu>, |
49 | | pub sni: Option<Vec<u8>>, |
50 | | pub ua: Option<Vec<u8>>, |
51 | | pub extv: Vec<QuicTlsExtension>, |
52 | | pub ja3: Option<String>, |
53 | | pub ja4: Option<String>, |
54 | | pub client: bool, |
55 | | tx_data: AppLayerTxData, |
56 | | } |
57 | | |
58 | | impl QuicTransaction { |
59 | 1.75M | fn new( |
60 | 1.75M | header: QuicHeader, data: QuicData, sni: Option<Vec<u8>>, ua: Option<Vec<u8>>, |
61 | 1.75M | extv: Vec<QuicTlsExtension>, ja3: Option<String>, ja4: Option<String>, client: bool, |
62 | 1.75M | ) -> Self { |
63 | 1.75M | let direction = if client { |
64 | 1.06M | Direction::ToServer |
65 | | } else { |
66 | 690k | Direction::ToClient |
67 | | }; |
68 | 1.75M | let cyu = Cyu::generate(&header, &data.frames); |
69 | 1.75M | QuicTransaction { |
70 | 1.75M | tx_id: 0, |
71 | 1.75M | header, |
72 | 1.75M | cyu, |
73 | 1.75M | sni, |
74 | 1.75M | ua, |
75 | 1.75M | extv, |
76 | 1.75M | ja3, |
77 | 1.75M | ja4, |
78 | 1.75M | client, |
79 | 1.75M | tx_data: AppLayerTxData::for_direction(direction), |
80 | 1.75M | } |
81 | 1.75M | } |
82 | | |
83 | 10.8k | fn new_empty(client: bool, header: QuicHeader) -> Self { |
84 | 10.8k | let direction = if client { |
85 | 10.4k | Direction::ToServer |
86 | | } else { |
87 | 342 | Direction::ToClient |
88 | | }; |
89 | 10.8k | QuicTransaction { |
90 | 10.8k | tx_id: 0, |
91 | 10.8k | header, |
92 | 10.8k | cyu: Vec::new(), |
93 | 10.8k | sni: None, |
94 | 10.8k | ua: None, |
95 | 10.8k | extv: Vec::new(), |
96 | 10.8k | ja3: None, |
97 | 10.8k | ja4: None, |
98 | 10.8k | client, |
99 | 10.8k | tx_data: AppLayerTxData::for_direction(direction), |
100 | 10.8k | } |
101 | 10.8k | } |
102 | | } |
103 | | |
104 | | pub struct QuicState { |
105 | | state_data: AppLayerStateData, |
106 | | max_tx_id: u64, |
107 | | keys: Option<QuicKeys>, |
108 | | /// crypto fragment data already seen and reassembled to client |
109 | | crypto_frag_tc: Vec<u8>, |
110 | | /// number of bytes set in crypto fragment data to client |
111 | | crypto_fraglen_tc: u32, |
112 | | /// crypto fragment data already seen and reassembled to server |
113 | | crypto_frag_ts: Vec<u8>, |
114 | | /// number of bytes set in crypto fragment data to server |
115 | | crypto_fraglen_ts: u32, |
116 | | hello_tc: bool, |
117 | | hello_ts: bool, |
118 | | has_retried: bool, |
119 | | transactions: VecDeque<QuicTransaction>, |
120 | | } |
121 | | |
122 | | impl Default for QuicState { |
123 | 12.6k | fn default() -> Self { |
124 | 12.6k | Self { |
125 | 12.6k | state_data: AppLayerStateData::new(), |
126 | 12.6k | max_tx_id: 0, |
127 | 12.6k | keys: None, |
128 | 12.6k | crypto_frag_tc: Vec::new(), |
129 | 12.6k | crypto_frag_ts: Vec::new(), |
130 | 12.6k | crypto_fraglen_tc: 0, |
131 | 12.6k | crypto_fraglen_ts: 0, |
132 | 12.6k | hello_tc: false, |
133 | 12.6k | hello_ts: false, |
134 | 12.6k | has_retried: false, |
135 | 12.6k | transactions: VecDeque::new(), |
136 | 12.6k | } |
137 | 12.6k | } |
138 | | } |
139 | | |
140 | | impl QuicState { |
141 | 12.6k | fn new() -> Self { |
142 | 12.6k | Self::default() |
143 | 12.6k | } |
144 | | |
145 | | // Free a transaction by ID. |
146 | 1.10M | fn free_tx(&mut self, tx_id: u64) { |
147 | 1.10M | let tx = self |
148 | 1.10M | .transactions |
149 | 1.10M | .iter() |
150 | 1.10M | .position(|tx| tx.tx_id == tx_id + 1); |
151 | 1.10M | if let Some(idx) = tx { |
152 | 1.10M | let _ = self.transactions.remove(idx); |
153 | 1.10M | } |
154 | 1.10M | } |
155 | | |
156 | 3.61k | fn get_tx(&mut self, tx_id: u64) -> Option<&QuicTransaction> { |
157 | 4.28k | self.transactions.iter().find(|&tx| tx.tx_id == tx_id + 1) |
158 | 3.61k | } |
159 | | |
160 | 1.75M | fn new_tx( |
161 | 1.75M | &mut self, header: QuicHeader, data: QuicData, sni: Option<Vec<u8>>, ua: Option<Vec<u8>>, |
162 | 1.75M | extb: Vec<QuicTlsExtension>, ja3: Option<String>, ja4: Option<String>, client: bool, |
163 | 1.75M | frag_long: bool, |
164 | 1.75M | ) { |
165 | 1.75M | let mut tx = QuicTransaction::new(header, data, sni, ua, extb, ja3, ja4, client); |
166 | 1.75M | self.max_tx_id += 1; |
167 | 1.75M | tx.tx_id = self.max_tx_id; |
168 | 1.75M | if frag_long { |
169 | 11.7k | tx.tx_data.set_event(QuicEvent::CryptoFragTooLong as u8); |
170 | 1.74M | } |
171 | 1.75M | self.transactions.push_back(tx); |
172 | 1.75M | } |
173 | | |
174 | 1.13M | fn tx_iterator( |
175 | 1.13M | &mut self, min_tx_id: u64, state: &mut u64, |
176 | 1.13M | ) -> Option<(&QuicTransaction, u64, bool)> { |
177 | 1.13M | let mut index = *state as usize; |
178 | 1.13M | let len = self.transactions.len(); |
179 | | |
180 | 1.14M | while index < len { |
181 | 1.11M | let tx = &self.transactions[index]; |
182 | 1.11M | if tx.tx_id < min_tx_id + 1 { |
183 | 791 | index += 1; |
184 | 791 | continue; |
185 | 1.11M | } |
186 | 1.11M | *state = index as u64; |
187 | 1.11M | return Some((tx, tx.tx_id - 1, (len - index) > 1)); |
188 | | } |
189 | | |
190 | 24.5k | return None; |
191 | 1.13M | } |
192 | | |
193 | 3.49k | fn decrypt<'a>( |
194 | 3.49k | &mut self, to_server: bool, header: &QuicHeader, framebuf: &'a [u8], buf: &'a [u8], |
195 | 3.49k | hlen: usize, output: &'a mut Vec<u8>, |
196 | 3.49k | ) -> Result<usize, ()> { |
197 | 3.49k | if let Some(keys) = &self.keys { |
198 | 3.49k | let hkey = if to_server { |
199 | 3.41k | &keys.remote.header |
200 | | } else { |
201 | 80 | &keys.local.header |
202 | | }; |
203 | 3.49k | if framebuf.len() < PKT_NUM_BUF_MAX_LEN + AES128_KEY_LEN { |
204 | 43 | return Err(()); |
205 | 3.45k | } |
206 | 3.45k | let h2len = hlen + usize::from(header.length); |
207 | 3.45k | let mut h2 = Vec::with_capacity(h2len); |
208 | 3.45k | h2.extend_from_slice(&buf[..h2len]); |
209 | 3.45k | let mut h20 = h2[0]; |
210 | 3.45k | let mut pktnum_buf = Vec::with_capacity(PKT_NUM_BUF_MAX_LEN); |
211 | 3.45k | pktnum_buf.extend_from_slice(&h2[hlen..hlen + PKT_NUM_BUF_MAX_LEN]); |
212 | 3.45k | let r1 = hkey.decrypt_in_place( |
213 | 3.45k | &h2[hlen + PKT_NUM_BUF_MAX_LEN..hlen + PKT_NUM_BUF_MAX_LEN + AES128_KEY_LEN], |
214 | 3.45k | &mut h20, |
215 | 3.45k | &mut pktnum_buf, |
216 | | ); |
217 | 3.45k | if r1.is_err() { |
218 | 0 | return Err(()); |
219 | 3.45k | } |
220 | | // mutate one at a time |
221 | 3.45k | h2[0] = h20; |
222 | 3.45k | let _ = &h2[hlen..hlen + 1 + ((h20 & 3) as usize)] |
223 | 3.45k | .copy_from_slice(&pktnum_buf[..1 + ((h20 & 3) as usize)]); |
224 | 3.45k | let pkt_num = quic_pkt_num(&h2[hlen..hlen + 1 + ((h20 & 3) as usize)]); |
225 | 3.45k | if framebuf.len() < 1 + ((h20 & 3) as usize) { |
226 | 0 | return Err(()); |
227 | 3.45k | } |
228 | 3.45k | output.extend_from_slice(&framebuf[1 + ((h20 & 3) as usize)..]); |
229 | 3.45k | let pkey = if to_server { |
230 | 3.38k | &keys.remote.packet |
231 | | } else { |
232 | 75 | &keys.local.packet |
233 | | }; |
234 | 3.45k | let r = pkey.decrypt_in_place(pkt_num, &h2[..hlen + 1 + ((h20 & 3) as usize)], output); |
235 | 3.45k | if let Ok(r2) = r { |
236 | 84 | return Ok(r2.len()); |
237 | 3.37k | } |
238 | 0 | } |
239 | 3.37k | return Err(()); |
240 | 3.49k | } |
241 | | |
242 | 1.74M | fn handle_frames(&mut self, data: QuicData, header: QuicHeader, to_server: bool) { |
243 | 1.74M | let mut sni: Option<Vec<u8>> = None; |
244 | 1.74M | let mut ua: Option<Vec<u8>> = None; |
245 | 1.74M | let mut ja3: Option<String> = None; |
246 | 1.74M | let mut ja4: Option<String> = None; |
247 | 1.74M | let mut extv: Vec<QuicTlsExtension> = Vec::new(); |
248 | 1.74M | let mut frag_long = false; |
249 | 3.19M | for frame in &data.frames { |
250 | 1.45M | match frame { |
251 | 86.5k | Frame::Stream(s) => { |
252 | 86.5k | if let Some(tags) = &s.tags { |
253 | 38.0k | for (tag, value) in tags { |
254 | 29.6k | if tag == &StreamTag::Sni { |
255 | 1.76k | sni = Some(value.to_vec()); |
256 | 27.8k | } else if tag == &StreamTag::Uaid { |
257 | 1.73k | ua = Some(value.to_vec()); |
258 | 26.1k | } |
259 | 29.6k | if sni.is_some() && ua.is_some() { |
260 | 994 | break; |
261 | 28.6k | } |
262 | | } |
263 | 77.1k | } |
264 | | } |
265 | 601k | Frame::CryptoFrag(frag) => { |
266 | | // means we had some fragments but not full TLS hello |
267 | | // save it for a later packet |
268 | 601k | if to_server { |
269 | | // use a hardcoded limit to not grow indefinitely |
270 | 198k | if frag.length < QUIC_MAX_CRYPTO_FRAG_LEN { |
271 | 192k | self.crypto_frag_ts.clone_from(&frag.data); |
272 | 192k | self.crypto_fraglen_ts = frag.offset as u32; |
273 | 192k | } else { |
274 | 6.58k | frag_long = true; |
275 | 6.58k | } |
276 | 403k | } else if frag.length < QUIC_MAX_CRYPTO_FRAG_LEN { |
277 | 398k | self.crypto_frag_tc.clone_from(&frag.data); |
278 | 398k | self.crypto_fraglen_tc = frag.offset as u32; |
279 | 398k | } else { |
280 | 5.14k | frag_long = true; |
281 | 5.14k | } |
282 | | } |
283 | 40.6k | Frame::Crypto(c) => { |
284 | 40.6k | if let Some(ja3str) = &c.ja3 { |
285 | 40.6k | ja3 = Some(ja3str.clone()); |
286 | 40.6k | } |
287 | | // we only do client fingerprints for now |
288 | 40.6k | if to_server { |
289 | | // our hash is complete, let's only use strings from |
290 | | // now on |
291 | 38.5k | if let Some(ref rja4) = c.ja4 { |
292 | 34.6k | ja4 = Some(rja4.get_hash()); |
293 | 34.6k | } |
294 | 2.09k | } |
295 | 2.51M | for e in &c.extv { |
296 | 2.47M | if e.etype == TlsExtensionType::ServerName && !e.values.is_empty() { |
297 | 826 | sni = Some(e.values[0].to_vec()); |
298 | 2.47M | } |
299 | | } |
300 | 40.6k | extv.extend_from_slice(&c.extv); |
301 | 40.6k | if to_server { |
302 | 38.5k | self.hello_ts = true |
303 | | } else { |
304 | 2.09k | self.hello_tc = true |
305 | | } |
306 | | } |
307 | 722k | _ => {} |
308 | | } |
309 | | } |
310 | 1.74M | self.new_tx(header, data, sni, ua, extv, ja3, ja4, to_server, frag_long); |
311 | 1.74M | } |
312 | | |
313 | 10.8k | fn set_event_notx(&mut self, event: QuicEvent, header: QuicHeader, client: bool) { |
314 | 10.8k | let mut tx = QuicTransaction::new_empty(client, header); |
315 | 10.8k | self.max_tx_id += 1; |
316 | 10.8k | tx.tx_id = self.max_tx_id; |
317 | 10.8k | tx.tx_data.set_event(event as u8); |
318 | 10.8k | self.transactions.push_back(tx); |
319 | 10.8k | } |
320 | | |
321 | 287k | fn parse(&mut self, input: &[u8], to_server: bool) -> bool { |
322 | | // so as to loop over multiple quic headers in one packet |
323 | 287k | let mut buf = input; |
324 | 2.04M | while !buf.is_empty() { |
325 | 1.78M | match QuicHeader::from_bytes(buf, DEFAULT_DCID_LEN) { |
326 | 1.78M | Ok((rest, header)) => { |
327 | 1.78M | if (to_server && self.hello_ts) || (!to_server && self.hello_tc) { |
328 | | // payload is encrypted, stop parsing here |
329 | 1.23k | return true; |
330 | 1.78M | } |
331 | 1.78M | if header.ty == QuicType::Short { |
332 | | // nothing to get |
333 | 20.5k | return true; |
334 | 1.76M | } |
335 | | |
336 | | // unprotect/decrypt packet |
337 | 1.76M | if self.keys.is_none() && header.ty == QuicType::Initial { |
338 | 1.71M | self.keys = quic_keys_initial(u32::from(header.version), &header.dcid); |
339 | 1.71M | } else if !to_server |
340 | 4.66k | && self.keys.is_some() |
341 | 1.41k | && header.ty == QuicType::Retry |
342 | 1.10k | && !self.has_retried |
343 | 41 | { |
344 | 41 | // a retry packet discards the current keys, client will resend an initial packet with new keys |
345 | 41 | self.hello_ts = false; |
346 | 41 | self.keys = None; |
347 | 41 | // RFC 9000 17.2.5.2 After the client has received and processed an Initial or Retry packet |
348 | 41 | // from the server, it MUST discard any subsequent Retry packets that it receives. |
349 | 41 | self.has_retried = true; |
350 | 46.6k | } |
351 | | // header.length was checked against rest.len() during parsing |
352 | 1.76M | let (mut framebuf, next_buf) = rest.split_at(header.length.into()); |
353 | 1.76M | if header.ty != QuicType::Initial { |
354 | | // only version is interesting, no frames |
355 | 8.80k | self.new_tx( |
356 | 8.80k | header, |
357 | 8.80k | QuicData { frames: Vec::new() }, |
358 | 8.80k | None, |
359 | 8.80k | None, |
360 | 8.80k | Vec::new(), |
361 | 8.80k | None, |
362 | 8.80k | None, |
363 | 8.80k | to_server, |
364 | | false, |
365 | | ); |
366 | 8.80k | buf = next_buf; |
367 | 8.80k | continue; |
368 | 1.75M | } |
369 | 1.75M | let hlen = buf.len() - rest.len(); |
370 | | let mut output; |
371 | 1.75M | if self.keys.is_some() && !framebuf.is_empty() { |
372 | 3.49k | output = Vec::with_capacity(framebuf.len() + 4); |
373 | 84 | if let Ok(dlen) = |
374 | 3.49k | self.decrypt(to_server, &header, framebuf, buf, hlen, &mut output) |
375 | 84 | { |
376 | 84 | output.resize(dlen, 0); |
377 | 84 | } else { |
378 | 3.41k | self.set_event_notx(QuicEvent::FailedDecrypt, header, to_server); |
379 | 3.41k | return false; |
380 | | } |
381 | 84 | framebuf = &output; |
382 | 1.75M | } |
383 | 1.75M | buf = next_buf; |
384 | | |
385 | 1.75M | let mut frag = Vec::new(); |
386 | | // take the current fragment and reset it in the state |
387 | 1.75M | let past_frag = if to_server { |
388 | 1.06M | std::mem::swap(&mut self.crypto_frag_ts, &mut frag); |
389 | 1.06M | &frag |
390 | | } else { |
391 | 686k | std::mem::swap(&mut self.crypto_frag_tc, &mut frag); |
392 | 686k | &frag |
393 | | }; |
394 | 1.75M | let past_fraglen = if to_server { |
395 | 1.06M | self.crypto_fraglen_ts |
396 | | } else { |
397 | 686k | self.crypto_fraglen_tc |
398 | | }; |
399 | 1.75M | if to_server { |
400 | 1.06M | self.crypto_fraglen_ts = 0 |
401 | | } else { |
402 | 686k | self.crypto_fraglen_tc = 0 |
403 | | } |
404 | 1.75M | match QuicData::from_bytes(framebuf, past_frag, past_fraglen) { |
405 | 1.74M | Ok(data) => { |
406 | 1.74M | self.handle_frames(data, header, to_server); |
407 | 1.74M | } |
408 | 7.40k | Err(_e) => { |
409 | 7.40k | self.set_event_notx(QuicEvent::ErrorOnData, header, to_server); |
410 | 7.40k | return false; |
411 | | } |
412 | | } |
413 | | } |
414 | 3.21k | Err(_e) => { |
415 | | // should we make an event with an empty header ? |
416 | 3.21k | return false; |
417 | | } |
418 | | } |
419 | | } |
420 | 251k | return true; |
421 | 287k | } |
422 | | } |
423 | | |
424 | | #[no_mangle] |
425 | 12.6k | pub extern "C" fn rs_quic_state_new( |
426 | 12.6k | _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto, |
427 | 12.6k | ) -> *mut std::os::raw::c_void { |
428 | 12.6k | let state = QuicState::new(); |
429 | 12.6k | let boxed = Box::new(state); |
430 | 12.6k | return Box::into_raw(boxed) as *mut _; |
431 | 12.6k | } |
432 | | |
433 | | #[no_mangle] |
434 | 12.6k | pub extern "C" fn rs_quic_state_free(state: *mut std::os::raw::c_void) { |
435 | | // Just unbox... |
436 | 12.6k | std::mem::drop(unsafe { Box::from_raw(state as *mut QuicState) }); |
437 | 12.6k | } |
438 | | |
439 | | #[no_mangle] |
440 | 1.10M | pub unsafe extern "C" fn rs_quic_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { |
441 | 1.10M | let state = cast_pointer!(state, QuicState); |
442 | 1.10M | state.free_tx(tx_id); |
443 | 1.10M | } |
444 | | |
445 | | #[no_mangle] |
446 | 2.38k | pub unsafe extern "C" fn rs_quic_probing_parser( |
447 | 2.38k | _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, |
448 | 2.38k | ) -> AppProto { |
449 | 2.38k | if input.is_null() { |
450 | 0 | return ALPROTO_UNKNOWN; |
451 | 2.38k | } |
452 | 2.38k | let slice = build_slice!(input, input_len as usize); |
453 | | |
454 | 2.38k | if QuicHeader::from_bytes(slice, DEFAULT_DCID_LEN).is_ok() { |
455 | 1.84k | return ALPROTO_QUIC; |
456 | | } else { |
457 | 538 | return ALPROTO_FAILED; |
458 | | } |
459 | 2.38k | } |
460 | | |
461 | | #[no_mangle] |
462 | 127k | pub unsafe extern "C" fn rs_quic_parse_tc( |
463 | 127k | _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, |
464 | 127k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
465 | 127k | ) -> AppLayerResult { |
466 | 127k | let state = cast_pointer!(state, QuicState); |
467 | 127k | let buf = stream_slice.as_slice(); |
468 | | |
469 | 127k | if state.parse(buf, false) { |
470 | 127k | return AppLayerResult::ok(); |
471 | | } else { |
472 | 528 | return AppLayerResult::err(); |
473 | | } |
474 | 127k | } |
475 | | |
476 | | #[no_mangle] |
477 | 159k | pub unsafe extern "C" fn rs_quic_parse_ts( |
478 | 159k | _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, |
479 | 159k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
480 | 159k | ) -> AppLayerResult { |
481 | 159k | let state = cast_pointer!(state, QuicState); |
482 | 159k | let buf = stream_slice.as_slice(); |
483 | | |
484 | 159k | if state.parse(buf, true) { |
485 | 146k | return AppLayerResult::ok(); |
486 | | } else { |
487 | 13.4k | return AppLayerResult::err(); |
488 | | } |
489 | 159k | } |
490 | | |
491 | | #[no_mangle] |
492 | 3.61k | pub unsafe extern "C" fn rs_quic_state_get_tx( |
493 | 3.61k | state: *mut std::os::raw::c_void, tx_id: u64, |
494 | 3.61k | ) -> *mut std::os::raw::c_void { |
495 | 3.61k | let state = cast_pointer!(state, QuicState); |
496 | 3.61k | match state.get_tx(tx_id) { |
497 | 3.61k | Some(tx) => { |
498 | 3.61k | return tx as *const _ as *mut _; |
499 | | } |
500 | | None => { |
501 | 2 | return std::ptr::null_mut(); |
502 | | } |
503 | | } |
504 | 3.61k | } |
505 | | |
506 | | #[no_mangle] |
507 | 850k | pub unsafe extern "C" fn rs_quic_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { |
508 | 850k | let state = cast_pointer!(state, QuicState); |
509 | 850k | return state.max_tx_id; |
510 | 850k | } |
511 | | |
512 | | #[no_mangle] |
513 | 0 | pub extern "C" fn rs_quic_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int { |
514 | | // This parser uses 1 to signal transaction completion status. |
515 | 0 | return 1; |
516 | 0 | } |
517 | | |
518 | | #[no_mangle] |
519 | 2.22M | pub unsafe extern "C" fn rs_quic_tx_get_alstate_progress( |
520 | 2.22M | tx: *mut std::os::raw::c_void, _direction: u8, |
521 | 2.22M | ) -> std::os::raw::c_int { |
522 | 2.22M | let _tx = cast_pointer!(tx, QuicTransaction); |
523 | 2.22M | return 1; |
524 | 2.22M | } |
525 | | |
526 | | #[no_mangle] |
527 | 1.13M | pub unsafe extern "C" fn rs_quic_state_get_tx_iterator( |
528 | 1.13M | _ipproto: u8, _alproto: AppProto, state: *mut std::os::raw::c_void, min_tx_id: u64, |
529 | 1.13M | _max_tx_id: u64, istate: &mut u64, |
530 | 1.13M | ) -> applayer::AppLayerGetTxIterTuple { |
531 | 1.13M | let state = cast_pointer!(state, QuicState); |
532 | 1.13M | match state.tx_iterator(min_tx_id, istate) { |
533 | 1.11M | Some((tx, out_tx_id, has_next)) => { |
534 | 1.11M | let c_tx = tx as *const _ as *mut _; |
535 | 1.11M | let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next); |
536 | 1.11M | return ires; |
537 | | } |
538 | | None => { |
539 | 24.5k | return applayer::AppLayerGetTxIterTuple::not_found(); |
540 | | } |
541 | | } |
542 | 1.13M | } |
543 | | |
544 | | export_tx_data_get!(rs_quic_get_tx_data, QuicTransaction); |
545 | | export_state_data_get!(rs_quic_get_state_data, QuicState); |
546 | | |
547 | | // Parser name as a C style string. |
548 | | const PARSER_NAME: &[u8] = b"quic\0"; |
549 | | |
550 | | #[no_mangle] |
551 | 34 | pub unsafe extern "C" fn rs_quic_register_parser() { |
552 | 34 | let default_port = CString::new("[443,80]").unwrap(); |
553 | 34 | let parser = RustParser { |
554 | 34 | name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
555 | 34 | default_port: default_port.as_ptr(), |
556 | 34 | ipproto: IPPROTO_UDP, |
557 | 34 | probe_ts: Some(rs_quic_probing_parser), |
558 | 34 | probe_tc: Some(rs_quic_probing_parser), |
559 | 34 | min_depth: 0, |
560 | 34 | max_depth: 16, |
561 | 34 | state_new: rs_quic_state_new, |
562 | 34 | state_free: rs_quic_state_free, |
563 | 34 | tx_free: rs_quic_state_tx_free, |
564 | 34 | parse_ts: rs_quic_parse_ts, |
565 | 34 | parse_tc: rs_quic_parse_tc, |
566 | 34 | get_tx_count: rs_quic_state_get_tx_count, |
567 | 34 | get_tx: rs_quic_state_get_tx, |
568 | 34 | tx_comp_st_ts: 1, |
569 | 34 | tx_comp_st_tc: 1, |
570 | 34 | tx_get_progress: rs_quic_tx_get_alstate_progress, |
571 | 34 | get_eventinfo: Some(QuicEvent::get_event_info), |
572 | 34 | get_eventinfo_byid: Some(QuicEvent::get_event_info_by_id), |
573 | 34 | localstorage_new: None, |
574 | 34 | localstorage_free: None, |
575 | 34 | get_tx_files: None, |
576 | 34 | get_tx_iterator: Some(rs_quic_state_get_tx_iterator), |
577 | 34 | get_tx_data: rs_quic_get_tx_data, |
578 | 34 | get_state_data: rs_quic_get_state_data, |
579 | 34 | apply_tx_config: None, |
580 | 34 | flags: 0, |
581 | 34 | truncate: None, |
582 | 34 | get_frame_id_by_name: None, |
583 | 34 | get_frame_name_by_id: None, |
584 | 34 | }; |
585 | | |
586 | 34 | let ip_proto_str = CString::new("udp").unwrap(); |
587 | | |
588 | 34 | if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
589 | 34 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
590 | 34 | ALPROTO_QUIC = alproto; |
591 | 34 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
592 | 34 | let _ = AppLayerRegisterParser(&parser, alproto); |
593 | 34 | } |
594 | | SCLogDebug!("Rust quic parser registered."); |
595 | 0 | } else { |
596 | 0 | SCLogDebug!("Protocol detector and parser disabled for quic."); |
597 | 0 | } |
598 | 34 | } |