/src/suricata8/rust/src/dns/dns.rs
Line | Count | Source |
1 | | /* Copyright (C) 2017-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 | | use std; |
19 | | use std::collections::HashMap; |
20 | | use std::collections::VecDeque; |
21 | | use std::ffi::CString; |
22 | | use std::os::raw::c_void; |
23 | | |
24 | | use crate::applayer::*; |
25 | | use crate::core::{self, *}; |
26 | | use crate::direction::Direction; |
27 | | use crate::direction::DIR_BOTH; |
28 | | use crate::dns::parser; |
29 | | use crate::flow::Flow; |
30 | | use crate::frames::Frame; |
31 | | |
32 | | use nom7::number::streaming::be_u16; |
33 | | use nom7::{Err, IResult}; |
34 | | use suricata_sys::sys::{ |
35 | | AppLayerParserState, AppProto, DetectEngineThreadCtx, SCAppLayerParserConfParserEnabled, |
36 | | SCAppLayerProtoDetectConfProtoDetectionEnabled, |
37 | | }; |
38 | | |
39 | | /// DNS record types. |
40 | | /// DNS error codes. |
41 | | #[derive(Clone, Debug, EnumStringU16)] |
42 | | pub enum DNSRecordType { |
43 | | A = 1, |
44 | | NS = 2, |
45 | | MD = 3, // Obsolete |
46 | | MF = 4, // Obsolete |
47 | | CNAME = 5, |
48 | | SOA = 6, |
49 | | MB = 7, // Experimental |
50 | | MG = 8, // Experimental |
51 | | MR = 9, // Experimental |
52 | | NULL = 10, // Experimental |
53 | | WKS = 11, |
54 | | PTR = 12, |
55 | | HINFO = 13, |
56 | | MINFO = 14, |
57 | | MX = 15, |
58 | | TXT = 16, |
59 | | RP = 17, |
60 | | AFSDB = 18, |
61 | | X25 = 19, |
62 | | ISDN = 20, |
63 | | RT = 21, |
64 | | NSAP = 22, |
65 | | NSAPPTR = 23, |
66 | | SIG = 24, |
67 | | KEY = 25, |
68 | | PX = 26, |
69 | | GPOS = 27, |
70 | | AAAA = 28, |
71 | | LOC = 29, |
72 | | NXT = 30, // Obsolete |
73 | | SRV = 33, |
74 | | ATMA = 34, |
75 | | NAPTR = 35, |
76 | | KX = 36, |
77 | | CERT = 37, |
78 | | A6 = 38, // Obsolete |
79 | | DNAME = 39, |
80 | | OPT = 41, |
81 | | APL = 42, |
82 | | DS = 43, |
83 | | SSHFP = 44, |
84 | | IPSECKEY = 45, |
85 | | RRSIG = 46, |
86 | | NSEC = 47, |
87 | | DNSKEY = 48, |
88 | | DHCID = 49, |
89 | | NSEC3 = 50, |
90 | | NSEC3PARAM = 51, |
91 | | TLSA = 52, |
92 | | HIP = 55, |
93 | | CDS = 59, |
94 | | CDNSKEY = 60, |
95 | | HTTPS = 65, |
96 | | SPF = 99, // Obsolete |
97 | | TKEY = 249, |
98 | | TSIG = 250, |
99 | | MAILA = 254, // Obsolete |
100 | | ANY = 255, |
101 | | URI = 256, |
102 | | } |
103 | | |
104 | | /// DNS error codes. |
105 | | #[derive(Clone, Debug, EnumStringU16)] |
106 | | pub enum DNSRcode { |
107 | | NOERROR = 0, |
108 | | FORMERR = 1, |
109 | | SERVFAIL = 2, |
110 | | NXDOMAIN = 3, |
111 | | NOTIMP = 4, |
112 | | REFUSED = 5, |
113 | | YXDOMAIN = 6, |
114 | | YXRRSET = 7, |
115 | | NXRRSET = 8, |
116 | | NOTAUTH = 9, |
117 | | NOTZONE = 10, |
118 | | // Support for OPT RR from RFC6891 will be needed to |
119 | | // parse RCODE values over 15 |
120 | | BADVERS = 16, |
121 | | //also pub const DNS_RCODE_BADSIG: u16 = 16; |
122 | | BADKEY = 17, |
123 | | BADTIME = 18, |
124 | | BADMODE = 19, |
125 | | BADNAME = 20, |
126 | | BADALG = 21, |
127 | | BADTRUNC = 22, |
128 | | } |
129 | | |
130 | | pub(super) static mut ALPROTO_DNS: AppProto = ALPROTO_UNKNOWN; |
131 | | |
132 | | #[derive(AppLayerFrameType)] |
133 | | pub(crate) enum DnsFrameType { |
134 | | /// DNS PDU frame. For UDP DNS this is the complete UDP payload, for TCP |
135 | | /// this is the DNS payload not including the leading length field allowing |
136 | | /// this frame to be used for UDP and TCP DNS. |
137 | | Pdu, |
138 | | } |
139 | | |
140 | | #[derive(Debug, PartialEq, Eq, AppLayerEvent)] |
141 | | pub enum DNSEvent { |
142 | | MalformedData, |
143 | | NotRequest, |
144 | | NotResponse, |
145 | | ZFlagSet, |
146 | | InvalidOpcode, |
147 | | /// A DNS resource name was exessively long and was truncated. |
148 | | NameTooLong, |
149 | | /// An infinite loop was found while parsing a name. |
150 | | InfiniteLoop, |
151 | | /// Too many labels were found. |
152 | | TooManyLabels, |
153 | | InvalidAdditionals, |
154 | | InvalidAuthorities, |
155 | | } |
156 | | |
157 | | #[derive(Debug, PartialEq, Eq)] |
158 | | #[repr(C)] |
159 | | pub struct DNSHeader { |
160 | | pub tx_id: u16, |
161 | | pub flags: u16, |
162 | | pub questions: u16, |
163 | | pub answer_rr: u16, |
164 | | pub authority_rr: u16, |
165 | | pub additional_rr: u16, |
166 | | } |
167 | | |
168 | | #[derive(Debug)] |
169 | | pub struct DNSQueryEntry { |
170 | | pub name: DNSName, |
171 | | pub rrtype: u16, |
172 | | pub rrclass: u16, |
173 | | } |
174 | | |
175 | | #[derive(Debug, PartialEq, Eq)] |
176 | | pub struct DNSRDataOPT { |
177 | | /// Option Code |
178 | | pub code: u16, |
179 | | /// Option Data |
180 | | pub data: Vec<u8>, |
181 | | } |
182 | | |
183 | | #[derive(Debug, PartialEq, Eq)] |
184 | | pub struct DNSRDataSOA { |
185 | | /// Primary name server for this zone |
186 | | pub mname: DNSName, |
187 | | /// Authority's mailbox |
188 | | pub rname: DNSName, |
189 | | /// Serial version number |
190 | | pub serial: u32, |
191 | | /// Refresh interval (seconds) |
192 | | pub refresh: u32, |
193 | | /// Retry interval (seconds) |
194 | | pub retry: u32, |
195 | | /// Upper time limit until zone is no longer authoritative (seconds) |
196 | | pub expire: u32, |
197 | | /// Minimum ttl for records in this zone (seconds) |
198 | | pub minimum: u32, |
199 | | } |
200 | | |
201 | | #[derive(Debug, PartialEq, Eq)] |
202 | | pub struct DNSRDataSSHFP { |
203 | | /// Algorithm number |
204 | | pub algo: u8, |
205 | | /// Fingerprint type |
206 | | pub fp_type: u8, |
207 | | /// Fingerprint |
208 | | pub fingerprint: Vec<u8>, |
209 | | } |
210 | | |
211 | | #[derive(Debug, PartialEq, Eq)] |
212 | | pub struct DNSRDataSRV { |
213 | | /// Priority |
214 | | pub priority: u16, |
215 | | /// Weight |
216 | | pub weight: u16, |
217 | | /// Port |
218 | | pub port: u16, |
219 | | /// Target |
220 | | pub target: DNSName, |
221 | | } |
222 | | |
223 | | bitflags! { |
224 | | #[derive(Default)] |
225 | | pub struct DNSNameFlags: u8 { |
226 | | const INFINITE_LOOP = 0b0000_0001; |
227 | | const TRUNCATED = 0b0000_0010; |
228 | | const LABEL_LIMIT = 0b0000_0100; |
229 | | } |
230 | | } |
231 | | |
232 | | #[derive(Debug, Clone, PartialEq, Eq)] |
233 | | pub struct DNSName { |
234 | | pub value: Vec<u8>, |
235 | | pub flags: DNSNameFlags, |
236 | | } |
237 | | |
238 | | /// Represents RData of various formats |
239 | | #[derive(Debug, PartialEq, Eq)] |
240 | | pub enum DNSRData { |
241 | | // RData is an address |
242 | | A(Vec<u8>), |
243 | | AAAA(Vec<u8>), |
244 | | // RData is a domain name |
245 | | CNAME(DNSName), |
246 | | PTR(DNSName), |
247 | | MX(DNSName), |
248 | | NS(DNSName), |
249 | | // TXT records are an array of TXT entries |
250 | | TXT(Vec<Vec<u8>>), |
251 | | NULL(Vec<u8>), |
252 | | // RData has several fields |
253 | | SOA(DNSRDataSOA), |
254 | | SRV(DNSRDataSRV), |
255 | | SSHFP(DNSRDataSSHFP), |
256 | | OPT(Vec<DNSRDataOPT>), |
257 | | // RData for remaining types is sometimes ignored |
258 | | Unknown(Vec<u8>), |
259 | | } |
260 | | |
261 | | #[derive(Debug, PartialEq, Eq)] |
262 | | pub struct DNSAnswerEntry { |
263 | | pub name: DNSName, |
264 | | pub rrtype: u16, |
265 | | pub rrclass: u16, |
266 | | pub ttl: u32, |
267 | | pub data: DNSRData, |
268 | | } |
269 | | |
270 | | #[derive(Debug)] |
271 | | pub struct DNSMessage { |
272 | | pub header: DNSHeader, |
273 | | pub queries: Vec<DNSQueryEntry>, |
274 | | pub answers: Vec<DNSAnswerEntry>, |
275 | | pub authorities: Vec<DNSAnswerEntry>, |
276 | | pub invalid_authorities: bool, |
277 | | pub additionals: Vec<DNSAnswerEntry>, |
278 | | pub invalid_additionals: bool, |
279 | | } |
280 | | |
281 | | #[derive(Debug, Default)] |
282 | | pub struct DNSTransaction { |
283 | | pub id: u64, |
284 | | pub request: Option<DNSMessage>, |
285 | | pub response: Option<DNSMessage>, |
286 | | pub tx_data: AppLayerTxData, |
287 | | } |
288 | | |
289 | | impl Transaction for DNSTransaction { |
290 | 960k | fn id(&self) -> u64 { |
291 | 960k | self.id |
292 | 960k | } |
293 | | } |
294 | | |
295 | | impl DNSTransaction { |
296 | 924k | pub(crate) fn new(direction: Direction) -> Self { |
297 | 924k | Self { |
298 | 924k | tx_data: AppLayerTxData::for_direction(direction), |
299 | 924k | ..Default::default() |
300 | 924k | } |
301 | 924k | } |
302 | | |
303 | | /// Get the DNS transactions ID (not the internal tracking ID). |
304 | 772 | pub fn tx_id(&self) -> u16 { |
305 | 772 | if let Some(request) = &self.request { |
306 | 334 | return request.header.tx_id; |
307 | 438 | } |
308 | 438 | if let Some(response) = &self.response { |
309 | 438 | return response.header.tx_id; |
310 | 0 | } |
311 | | |
312 | | // Shouldn't happen. |
313 | 0 | return 0; |
314 | 772 | } |
315 | | |
316 | | /// Get the reply code of the transaction. Note that this will |
317 | | /// also return 0 if there is no reply. |
318 | 0 | pub fn rcode(&self) -> u16 { |
319 | 0 | if let Some(response) = &self.response { |
320 | 0 | return response.header.flags & 0x000f; |
321 | 0 | } |
322 | 0 | return 0; |
323 | 0 | } |
324 | | |
325 | | /// Set an event. The event is set on the most recent transaction. |
326 | 2.98M | pub fn set_event(&mut self, event: DNSEvent) { |
327 | 2.98M | self.tx_data.set_event(event as u8); |
328 | 2.98M | } |
329 | | } |
330 | | |
331 | | struct ConfigTracker { |
332 | | map: HashMap<u16, AppLayerTxConfig>, |
333 | | queue: VecDeque<u16>, |
334 | | } |
335 | | |
336 | | impl ConfigTracker { |
337 | 0 | fn new() -> ConfigTracker { |
338 | 0 | ConfigTracker { |
339 | 0 | map: HashMap::new(), |
340 | 0 | queue: VecDeque::new(), |
341 | 0 | } |
342 | 0 | } |
343 | | |
344 | 0 | fn add(&mut self, id: u16, config: AppLayerTxConfig) { |
345 | | // If at size limit, remove the oldest entry. |
346 | 0 | if self.queue.len() > 499 { |
347 | 0 | if let Some(id) = self.queue.pop_front() { |
348 | 0 | self.map.remove(&id); |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 0 | self.map.insert(id, config); |
353 | 0 | self.queue.push_back(id); |
354 | 0 | } |
355 | | |
356 | 0 | fn remove(&mut self, id: &u16) -> Option<AppLayerTxConfig> { |
357 | 0 | self.map.remove(id) |
358 | 0 | } |
359 | | } |
360 | | |
361 | | pub(crate) enum DnsVariant { |
362 | | Dns, |
363 | | MulticastDns, |
364 | | } |
365 | | |
366 | | impl DnsVariant { |
367 | 645k | pub fn is_dns(&self) -> bool { |
368 | 645k | matches!(self, DnsVariant::Dns) |
369 | 645k | } |
370 | | |
371 | 563k | pub fn is_mdns(&self) -> bool { |
372 | 563k | matches!(self, DnsVariant::MulticastDns) |
373 | 563k | } |
374 | | } |
375 | | |
376 | | //#[derive(Default)] |
377 | | pub struct DNSState { |
378 | | variant: DnsVariant, |
379 | | state_data: AppLayerStateData, |
380 | | |
381 | | // Internal transaction ID. |
382 | | tx_id: u64, |
383 | | |
384 | | // Transactions. |
385 | | transactions: VecDeque<DNSTransaction>, |
386 | | |
387 | | config: Option<ConfigTracker>, |
388 | | |
389 | | gap: bool, |
390 | | } |
391 | | |
392 | | impl State<DNSTransaction> for DNSState { |
393 | 749k | fn get_transaction_count(&self) -> usize { |
394 | 749k | self.transactions.len() |
395 | 749k | } |
396 | | |
397 | 480k | fn get_transaction_by_index(&self, index: usize) -> Option<&DNSTransaction> { |
398 | 480k | self.transactions.get(index) |
399 | 480k | } |
400 | | } |
401 | | |
402 | 1.16M | fn dns_validate_header(input: &[u8]) -> Option<(&[u8], DNSHeader)> { |
403 | 1.16M | if let Ok((body, header)) = parser::dns_parse_header(input) { |
404 | 1.12M | if probe_header_validity(&header, input.len()).0 { |
405 | 1.04M | return Some((body, header)); |
406 | 78.8k | } |
407 | 48.3k | } |
408 | 127k | None |
409 | 1.16M | } |
410 | | |
411 | | #[derive(Debug, PartialEq, Eq)] |
412 | | pub(crate) enum DNSParseError { |
413 | | HeaderValidation, |
414 | | NotRequest, |
415 | | Incomplete, |
416 | | OtherError, |
417 | | } |
418 | | |
419 | 775k | pub(crate) fn dns_parse_request( |
420 | 775k | input: &[u8], variant: &DnsVariant, |
421 | 775k | ) -> Result<DNSTransaction, DNSParseError> { |
422 | 775k | let (body, header) = if let Some((body, header)) = dns_validate_header(input) { |
423 | 675k | (body, header) |
424 | | } else { |
425 | 100k | return Err(DNSParseError::HeaderValidation); |
426 | | }; |
427 | | |
428 | 675k | match parser::dns_parse_body(body, input, header) { |
429 | 645k | Ok((_, (request, parse_flags))) => { |
430 | 645k | if variant.is_dns() && request.header.flags & 0x8000 != 0 { |
431 | | SCLogDebug!("DNS message is not a request"); |
432 | 82.5k | return Err(DNSParseError::NotRequest); |
433 | 563k | } |
434 | | |
435 | 563k | let z_flag = request.header.flags & 0x0040 != 0; |
436 | 563k | let opcode = ((request.header.flags >> 11) & 0xf) as u8; |
437 | | |
438 | 563k | let mut tx = DNSTransaction::new(Direction::ToServer); |
439 | 563k | if request.invalid_additionals { |
440 | 464k | tx.set_event(DNSEvent::InvalidAdditionals); |
441 | 464k | } |
442 | 563k | if request.invalid_authorities { |
443 | 87.0k | tx.set_event(DNSEvent::InvalidAuthorities); |
444 | 476k | } |
445 | | |
446 | 563k | if variant.is_mdns() && request.header.flags & 0x8000 != 0 { |
447 | 179k | tx.response = Some(request); |
448 | 384k | } else { |
449 | 384k | tx.request = Some(request); |
450 | 384k | } |
451 | | |
452 | 563k | if z_flag { |
453 | 550k | SCLogDebug!("Z-flag set on DNS request"); |
454 | 550k | tx.set_event(DNSEvent::ZFlagSet); |
455 | 550k | } |
456 | | |
457 | 563k | if opcode >= 7 { |
458 | 546k | tx.set_event(DNSEvent::InvalidOpcode); |
459 | 546k | } |
460 | | |
461 | 563k | if parse_flags.contains(DNSNameFlags::TRUNCATED) { |
462 | 1.32k | tx.set_event(DNSEvent::NameTooLong); |
463 | 561k | } |
464 | | |
465 | 563k | if parse_flags.contains(DNSNameFlags::INFINITE_LOOP) { |
466 | 694 | tx.set_event(DNSEvent::InfiniteLoop); |
467 | 562k | } |
468 | | |
469 | 563k | if parse_flags.contains(DNSNameFlags::LABEL_LIMIT) { |
470 | 1.21k | tx.set_event(DNSEvent::TooManyLabels); |
471 | 561k | } |
472 | | |
473 | 563k | return Ok(tx); |
474 | | } |
475 | | Err(Err::Incomplete(_)) => { |
476 | | // Insufficient data. |
477 | | SCLogDebug!("Insufficient data while parsing DNS request"); |
478 | 20.7k | return Err(DNSParseError::Incomplete); |
479 | | } |
480 | | Err(_) => { |
481 | | // Error, probably malformed data. |
482 | | SCLogDebug!("An error occurred while parsing DNS request"); |
483 | 8.57k | return Err(DNSParseError::OtherError); |
484 | | } |
485 | | } |
486 | 775k | } |
487 | | |
488 | 394k | pub(crate) fn dns_parse_response(input: &[u8]) -> Result<DNSTransaction, DNSParseError> { |
489 | 394k | let (body, header) = if let Some((body, header)) = dns_validate_header(input) { |
490 | 367k | (body, header) |
491 | | } else { |
492 | 26.6k | return Err(DNSParseError::HeaderValidation); |
493 | | }; |
494 | | |
495 | 367k | match parser::dns_parse_body(body, input, header) { |
496 | 361k | Ok((_, (response, parse_flags))) => { |
497 | | SCLogDebug!("Response header flags: {}", response.header.flags); |
498 | 361k | let z_flag = response.header.flags & 0x0040 != 0; |
499 | 361k | let opcode = ((response.header.flags >> 11) & 0xf) as u8; |
500 | 361k | let flags = response.header.flags; |
501 | | |
502 | 361k | let mut tx = DNSTransaction::new(Direction::ToClient); |
503 | 361k | if response.invalid_additionals { |
504 | 272k | tx.set_event(DNSEvent::InvalidAdditionals); |
505 | 272k | } |
506 | 361k | if response.invalid_authorities { |
507 | 77.6k | tx.set_event(DNSEvent::InvalidAuthorities); |
508 | 283k | } |
509 | 361k | tx.response = Some(response); |
510 | | |
511 | 361k | if flags & 0x8000 == 0 { |
512 | 277k | SCLogDebug!("DNS message is not a response"); |
513 | 277k | tx.set_event(DNSEvent::NotResponse); |
514 | 277k | } |
515 | | |
516 | 361k | if z_flag { |
517 | 353k | SCLogDebug!("Z-flag set on DNS response"); |
518 | 353k | tx.set_event(DNSEvent::ZFlagSet); |
519 | 353k | } |
520 | | |
521 | 361k | if opcode >= 7 { |
522 | 354k | tx.set_event(DNSEvent::InvalidOpcode); |
523 | 354k | } |
524 | | |
525 | 361k | if parse_flags.contains(DNSNameFlags::TRUNCATED) { |
526 | 632 | tx.set_event(DNSEvent::NameTooLong); |
527 | 361k | } |
528 | | |
529 | 361k | if parse_flags.contains(DNSNameFlags::INFINITE_LOOP) { |
530 | 436 | tx.set_event(DNSEvent::InfiniteLoop); |
531 | 361k | } |
532 | | |
533 | 361k | if parse_flags.contains(DNSNameFlags::LABEL_LIMIT) { |
534 | 600 | tx.set_event(DNSEvent::TooManyLabels); |
535 | 361k | } |
536 | | |
537 | 361k | return Ok(tx); |
538 | | } |
539 | | Err(Err::Incomplete(_)) => { |
540 | | // Insufficient data. |
541 | | SCLogDebug!("Insufficient data while parsing DNS request"); |
542 | 3.88k | return Err(DNSParseError::Incomplete); |
543 | | } |
544 | | Err(_) => { |
545 | | // Error, probably malformed data. |
546 | | SCLogDebug!("An error occurred while parsing DNS request"); |
547 | 2.09k | return Err(DNSParseError::OtherError); |
548 | | } |
549 | | } |
550 | 394k | } |
551 | | |
552 | | impl DNSState { |
553 | 4.02k | fn new() -> Self { |
554 | 4.02k | Self { |
555 | 4.02k | variant: DnsVariant::Dns, |
556 | 4.02k | state_data: AppLayerStateData::default(), |
557 | 4.02k | tx_id: 0, |
558 | 4.02k | transactions: VecDeque::default(), |
559 | 4.02k | config: None, |
560 | 4.02k | gap: false, |
561 | 4.02k | } |
562 | 4.02k | } |
563 | | |
564 | 2.81k | pub(crate) fn new_variant(variant: DnsVariant) -> Self { |
565 | 2.81k | Self { |
566 | 2.81k | variant, |
567 | 2.81k | state_data: AppLayerStateData::default(), |
568 | 2.81k | tx_id: 0, |
569 | 2.81k | transactions: VecDeque::default(), |
570 | 2.81k | config: None, |
571 | 2.81k | gap: false, |
572 | 2.81k | } |
573 | 2.81k | } |
574 | | |
575 | 478k | fn free_tx(&mut self, tx_id: u64) { |
576 | 478k | let len = self.transactions.len(); |
577 | 478k | let mut found = false; |
578 | 478k | let mut index = 0; |
579 | 478k | for i in 0..len { |
580 | 478k | let tx = &self.transactions[i]; |
581 | 478k | if tx.id == tx_id + 1 { |
582 | 478k | found = true; |
583 | 478k | index = i; |
584 | 478k | break; |
585 | 0 | } |
586 | | } |
587 | 478k | if found { |
588 | 478k | self.transactions.remove(index); |
589 | 478k | } |
590 | 478k | } |
591 | | |
592 | 0 | fn get_tx(&mut self, tx_id: u64) -> Option<&DNSTransaction> { |
593 | 0 | return self.transactions.iter().find(|&tx| tx.id == tx_id + 1); |
594 | 0 | } |
595 | | |
596 | | /// Set an event. The event is set on the most recent transaction. |
597 | 117k | fn set_event(&mut self, event: DNSEvent) { |
598 | 117k | let len = self.transactions.len(); |
599 | 117k | if len == 0 { |
600 | 117k | return; |
601 | 84 | } |
602 | | |
603 | 84 | let tx = &mut self.transactions[len - 1]; |
604 | 84 | tx.tx_data.set_event(event as u8); |
605 | 117k | } |
606 | | |
607 | 775k | fn parse_request( |
608 | 775k | &mut self, input: &[u8], is_tcp: bool, frame: Option<Frame>, flow: *const Flow, |
609 | 775k | ) -> bool { |
610 | 775k | match dns_parse_request(input, &self.variant) { |
611 | 563k | Ok(mut tx) => { |
612 | 563k | self.tx_id += 1; |
613 | 563k | tx.id = self.tx_id; |
614 | 563k | if let Some(frame) = frame { |
615 | 27 | frame.set_tx(flow, tx.id); |
616 | 563k | } |
617 | 563k | self.transactions.push_back(tx); |
618 | 563k | return true; |
619 | | } |
620 | 212k | Err(e) => match e { |
621 | | DNSParseError::HeaderValidation => { |
622 | 100k | return !is_tcp; |
623 | | } |
624 | | DNSParseError::NotRequest => { |
625 | 82.5k | self.set_event(DNSEvent::NotRequest); |
626 | 82.5k | return false; |
627 | | } |
628 | | DNSParseError::Incomplete => { |
629 | 20.7k | self.set_event(DNSEvent::MalformedData); |
630 | 20.7k | return false; |
631 | | } |
632 | | DNSParseError::OtherError => { |
633 | 8.57k | self.set_event(DNSEvent::MalformedData); |
634 | 8.57k | return false; |
635 | | } |
636 | | }, |
637 | | } |
638 | 775k | } |
639 | | |
640 | 407k | pub(crate) fn parse_request_udp( |
641 | 407k | &mut self, flow: *const Flow, stream_slice: StreamSlice, |
642 | 407k | ) -> bool { |
643 | 407k | let input = stream_slice.as_slice(); |
644 | 407k | let frame = Frame::new( |
645 | 407k | flow, |
646 | 407k | &stream_slice, |
647 | 407k | input, |
648 | 407k | input.len() as i64, |
649 | 407k | DnsFrameType::Pdu as u8, |
650 | 407k | None, |
651 | | ); |
652 | 407k | self.parse_request(input, false, frame, flow) |
653 | 407k | } |
654 | | |
655 | 120k | fn parse_response_udp(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> bool { |
656 | 120k | let input = stream_slice.as_slice(); |
657 | 120k | let frame = Frame::new( |
658 | 120k | flow, |
659 | 120k | &stream_slice, |
660 | 120k | input, |
661 | 120k | input.len() as i64, |
662 | 120k | DnsFrameType::Pdu as u8, |
663 | 120k | None, |
664 | | ); |
665 | 120k | self.parse_response(input, false, frame, flow) |
666 | 120k | } |
667 | | |
668 | 394k | fn parse_response( |
669 | 394k | &mut self, input: &[u8], is_tcp: bool, frame: Option<Frame>, flow: *const Flow, |
670 | 394k | ) -> bool { |
671 | 394k | match dns_parse_response(input) { |
672 | 361k | Ok(mut tx) => { |
673 | 361k | self.tx_id += 1; |
674 | 361k | tx.id = self.tx_id; |
675 | 361k | if let Some(ref mut config) = &mut self.config { |
676 | 0 | if let Some(response) = &tx.response { |
677 | 0 | if let Some(config) = config.remove(&response.header.tx_id) { |
678 | 0 | tx.tx_data.config = config; |
679 | 0 | } |
680 | 0 | } |
681 | 361k | } |
682 | 361k | if let Some(frame) = frame { |
683 | 15 | frame.set_tx(flow, tx.id); |
684 | 361k | } |
685 | 361k | self.transactions.push_back(tx); |
686 | 361k | return true; |
687 | | } |
688 | 32.6k | Err(e) => match e { |
689 | | DNSParseError::HeaderValidation => { |
690 | 26.6k | return !is_tcp; |
691 | | } |
692 | | _ => { |
693 | 5.97k | self.set_event(DNSEvent::MalformedData); |
694 | 5.97k | return false; |
695 | | } |
696 | | }, |
697 | | } |
698 | 394k | } |
699 | | |
700 | | /// TCP variation of response request parser to handle the length |
701 | | /// prefix. |
702 | | /// |
703 | | /// Returns the number of messages parsed. |
704 | 11.3k | fn parse_request_tcp( |
705 | 11.3k | &mut self, flow: *mut Flow, stream_slice: StreamSlice, |
706 | 11.3k | ) -> AppLayerResult { |
707 | 11.3k | let input = stream_slice.as_slice(); |
708 | 11.3k | if self.gap { |
709 | 1.44k | let (is_dns, _, is_incomplete) = probe_tcp(input); |
710 | 1.44k | if is_dns || is_incomplete { |
711 | 22 | self.gap = false; |
712 | 22 | } else { |
713 | 1.42k | return AppLayerResult::ok(); |
714 | | } |
715 | 9.94k | } |
716 | | |
717 | 9.96k | let mut cur_i = input; |
718 | 9.96k | let mut consumed = 0; |
719 | 2.86M | while !cur_i.is_empty() { |
720 | 2.86M | if cur_i.len() == 1 { |
721 | 1.70k | return AppLayerResult::incomplete(consumed as u32, 2_u32); |
722 | 2.86M | } |
723 | 2.86M | let size = match be_u16(cur_i) as IResult<&[u8], u16> { |
724 | 2.86M | Ok((_, len)) => len, |
725 | 0 | _ => 0, |
726 | | } as usize; |
727 | | SCLogDebug!( |
728 | | "[request] Have {} bytes, need {} to parse", |
729 | | cur_i.len(), |
730 | | size + 2 |
731 | | ); |
732 | 2.86M | if size > 0 && cur_i.len() >= size + 2 { |
733 | 368k | let msg = &cur_i[2..(size + 2)]; |
734 | 368k | sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToServer as i32); |
735 | 368k | let frame = Frame::new( |
736 | 368k | flow, |
737 | 368k | &stream_slice, |
738 | 368k | msg, |
739 | 368k | msg.len() as i64, |
740 | 368k | DnsFrameType::Pdu as u8, |
741 | 368k | None, |
742 | | ); |
743 | 368k | if self.parse_request(msg, true, frame, flow) { |
744 | 368k | cur_i = &cur_i[(size + 2)..]; |
745 | 368k | consumed += size + 2; |
746 | 368k | } else { |
747 | 199 | return AppLayerResult::err(); |
748 | | } |
749 | 2.49M | } else if size == 0 { |
750 | 2.48M | cur_i = &cur_i[2..]; |
751 | 2.48M | consumed += 2; |
752 | 2.48M | } else { |
753 | | SCLogDebug!( |
754 | | "[request]Not enough DNS traffic to parse. Returning {}/{}", |
755 | | consumed as u32, |
756 | | (size + 2) as u32 |
757 | | ); |
758 | 7.09k | return AppLayerResult::incomplete(consumed as u32, (size + 2) as u32); |
759 | | } |
760 | | } |
761 | 969 | AppLayerResult::ok() |
762 | 11.3k | } |
763 | | |
764 | | /// TCP variation of the response parser to handle the length |
765 | | /// prefix. |
766 | | /// |
767 | | /// Returns the number of messages parsed. |
768 | 12.6k | fn parse_response_tcp( |
769 | 12.6k | &mut self, flow: *mut Flow, stream_slice: StreamSlice, |
770 | 12.6k | ) -> AppLayerResult { |
771 | 12.6k | let input = stream_slice.as_slice(); |
772 | 12.6k | if self.gap { |
773 | 2.86k | let (is_dns, _, is_incomplete) = probe_tcp(input); |
774 | 2.86k | if is_dns || is_incomplete { |
775 | 5 | self.gap = false; |
776 | 5 | } else { |
777 | 2.85k | return AppLayerResult::ok(); |
778 | | } |
779 | 9.77k | } |
780 | | |
781 | 9.77k | let mut cur_i = input; |
782 | 9.77k | let mut consumed = 0; |
783 | 2.47M | while !cur_i.is_empty() { |
784 | 2.46M | if cur_i.len() == 1 { |
785 | 1.71k | return AppLayerResult::incomplete(consumed as u32, 2_u32); |
786 | 2.46M | } |
787 | 2.46M | let size = match be_u16(cur_i) as IResult<&[u8], u16> { |
788 | 2.46M | Ok((_, len)) => len, |
789 | 0 | _ => 0, |
790 | | } as usize; |
791 | | SCLogDebug!( |
792 | | "[response] Have {} bytes, need {} to parse", |
793 | | cur_i.len(), |
794 | | size + 2 |
795 | | ); |
796 | 2.46M | if size > 0 && cur_i.len() >= size + 2 { |
797 | 273k | let msg = &cur_i[2..(size + 2)]; |
798 | 273k | sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32); |
799 | 273k | let frame = Frame::new( |
800 | 273k | flow, |
801 | 273k | &stream_slice, |
802 | 273k | msg, |
803 | 273k | msg.len() as i64, |
804 | 273k | DnsFrameType::Pdu as u8, |
805 | 273k | None, |
806 | | ); |
807 | 273k | if self.parse_response(msg, true, frame, flow) { |
808 | 273k | cur_i = &cur_i[(size + 2)..]; |
809 | 273k | consumed += size + 2; |
810 | 273k | } else { |
811 | 229 | return AppLayerResult::err(); |
812 | | } |
813 | 2.19M | } else if size == 0 { |
814 | 2.18M | cur_i = &cur_i[2..]; |
815 | 2.18M | consumed += 2; |
816 | 2.18M | } else { |
817 | | SCLogDebug!( |
818 | | "[response]Not enough DNS traffic to parse. Returning {}/{}", |
819 | | consumed as u32, |
820 | | (cur_i.len() - consumed) as u32 |
821 | | ); |
822 | 6.97k | return AppLayerResult::incomplete(consumed as u32, (size + 2) as u32); |
823 | | } |
824 | | } |
825 | 859 | AppLayerResult::ok() |
826 | 12.6k | } |
827 | | |
828 | | /// A gap has been seen in the request direction. Set the gap flag. |
829 | 23 | fn request_gap(&mut self, gap: u32) { |
830 | 23 | if gap > 0 { |
831 | 23 | self.gap = true; |
832 | 23 | } |
833 | 23 | } |
834 | | |
835 | | /// A gap has been seen in the response direction. Set the gap |
836 | | /// flag. |
837 | 79 | fn response_gap(&mut self, gap: u32) { |
838 | 79 | if gap > 0 { |
839 | 79 | self.gap = true; |
840 | 79 | } |
841 | 79 | } |
842 | | } |
843 | | |
844 | | const DNS_HEADER_SIZE: usize = 12; |
845 | | |
846 | 1.12M | pub(crate) fn probe_header_validity(header: &DNSHeader, rlen: usize) -> (bool, bool, bool) { |
847 | 1.12M | let nb_records = header.additional_rr as usize |
848 | 1.12M | + header.answer_rr as usize |
849 | 1.12M | + header.authority_rr as usize |
850 | 1.12M | + header.questions as usize; |
851 | | |
852 | 1.12M | let min_msg_size = 2 * nb_records; |
853 | 1.12M | if min_msg_size > rlen { |
854 | | // Not enough data for records defined in the header, or |
855 | | // impossibly large. |
856 | 72.9k | return (false, false, false); |
857 | 1.05M | } |
858 | | |
859 | 1.05M | if nb_records == 0 && rlen > DNS_HEADER_SIZE { |
860 | | // zero fields, data size should be just DNS_HEADER_SIZE |
861 | | // happens when DNS server returns format error |
862 | 7.95k | return (false, false, false); |
863 | 1.04M | } |
864 | | |
865 | 1.04M | let is_request = header.flags & 0x8000 == 0; |
866 | 1.04M | if is_request && header.questions == 0 { |
867 | 3.38k | return (false, false, false); |
868 | 1.04M | } |
869 | 1.04M | return (true, is_request, false); |
870 | 1.12M | } |
871 | | |
872 | | /// Probe input to see if it looks like DNS. |
873 | | /// |
874 | | /// Returns a tuple of booleans: (is_dns, is_request, incomplete) |
875 | 9.78k | fn probe(input: &[u8], dlen: usize) -> (bool, bool, bool) { |
876 | | // Trim input to dlen if larger. |
877 | 9.78k | let input = if input.len() <= dlen { |
878 | 8.78k | input |
879 | | } else { |
880 | 992 | &input[..dlen] |
881 | | }; |
882 | | |
883 | | // If input is less than dlen then we know we don't have enough data to |
884 | | // parse a complete message, so perform header validation only. |
885 | 9.78k | if input.len() < dlen { |
886 | 7.09k | if let Ok((_, header)) = parser::dns_parse_header(input) { |
887 | 5.13k | return probe_header_validity(&header, dlen); |
888 | | } else { |
889 | 1.95k | return (false, false, false); |
890 | | } |
891 | 2.68k | } |
892 | | |
893 | 2.68k | match parser::dns_parse_header(input) { |
894 | 2.39k | Ok((body, header)) => match parser::dns_parse_body(body, input, header) { |
895 | 1.31k | Ok((_, (request, _flags))) => probe_header_validity(&request.header, dlen), |
896 | 633 | Err(Err::Incomplete(_)) => (false, false, true), |
897 | 448 | Err(_) => (false, false, false), |
898 | | }, |
899 | 287 | Err(_) => (false, false, false), |
900 | | } |
901 | 9.78k | } |
902 | | |
903 | | /// Probe TCP input to see if it looks like DNS. |
904 | 8.10k | fn probe_tcp(input: &[u8]) -> (bool, bool, bool) { |
905 | 8.10k | match be_u16(input) as IResult<&[u8], u16> { |
906 | 8.08k | Ok((rem, dlen)) => { |
907 | 8.08k | return probe(rem, dlen as usize); |
908 | | } |
909 | | Err(Err::Incomplete(_)) => { |
910 | 16 | return (false, false, true); |
911 | | } |
912 | 0 | _ => {} |
913 | | } |
914 | 0 | return (false, false, false); |
915 | 8.10k | } |
916 | | |
917 | | /// Returns *mut DNSState |
918 | 4.02k | pub(crate) extern "C" fn state_new( |
919 | 4.02k | _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto, |
920 | 4.02k | ) -> *mut std::os::raw::c_void { |
921 | 4.02k | let state = DNSState::new(); |
922 | 4.02k | let boxed = Box::new(state); |
923 | 4.02k | return Box::into_raw(boxed) as *mut _; |
924 | 4.02k | } |
925 | | |
926 | | /// Params: |
927 | | /// - state: *mut DNSState as void pointer |
928 | 6.84k | pub(crate) extern "C" fn state_free(state: *mut std::os::raw::c_void) { |
929 | | // Just unbox... |
930 | 6.84k | std::mem::drop(unsafe { Box::from_raw(state as *mut DNSState) }); |
931 | 6.84k | } |
932 | | |
933 | 478k | pub(crate) unsafe extern "C" fn state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { |
934 | 478k | let state = cast_pointer!(state, DNSState); |
935 | 478k | state.free_tx(tx_id); |
936 | 478k | } |
937 | | |
938 | | /// C binding parse a DNS request. Returns 1 on success, -1 on failure. |
939 | 407k | pub(crate) unsafe extern "C" fn parse_request( |
940 | 407k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
941 | 407k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
942 | 407k | ) -> AppLayerResult { |
943 | 407k | let state = cast_pointer!(state, DNSState); |
944 | 407k | state.parse_request_udp(flow, stream_slice); |
945 | 407k | AppLayerResult::ok() |
946 | 407k | } |
947 | | |
948 | 120k | unsafe extern "C" fn parse_response( |
949 | 120k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
950 | 120k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
951 | 120k | ) -> AppLayerResult { |
952 | 120k | let state = cast_pointer!(state, DNSState); |
953 | 120k | state.parse_response_udp(flow, stream_slice); |
954 | 120k | AppLayerResult::ok() |
955 | 120k | } |
956 | | |
957 | | /// C binding parse a DNS request. Returns 1 on success, -1 on failure. |
958 | 11.4k | unsafe extern "C" fn parse_request_tcp( |
959 | 11.4k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
960 | 11.4k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
961 | 11.4k | ) -> AppLayerResult { |
962 | 11.4k | let state = cast_pointer!(state, DNSState); |
963 | 11.4k | if stream_slice.is_gap() { |
964 | 23 | state.request_gap(stream_slice.gap_size()); |
965 | 11.3k | } else if !stream_slice.is_empty() { |
966 | 11.3k | return state.parse_request_tcp(flow, stream_slice); |
967 | 0 | } |
968 | 23 | AppLayerResult::ok() |
969 | 11.4k | } |
970 | | |
971 | 12.7k | unsafe extern "C" fn parse_response_tcp( |
972 | 12.7k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
973 | 12.7k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
974 | 12.7k | ) -> AppLayerResult { |
975 | 12.7k | let state = cast_pointer!(state, DNSState); |
976 | 12.7k | if stream_slice.is_gap() { |
977 | 79 | state.response_gap(stream_slice.gap_size()); |
978 | 12.6k | } else if !stream_slice.is_empty() { |
979 | 12.6k | return state.parse_response_tcp(flow, stream_slice); |
980 | 0 | } |
981 | 79 | AppLayerResult::ok() |
982 | 12.7k | } |
983 | | |
984 | 959k | pub(crate) extern "C" fn tx_get_alstate_progress( |
985 | 959k | _tx: *mut std::os::raw::c_void, _direction: u8, |
986 | 959k | ) -> std::os::raw::c_int { |
987 | | // This is a stateless parser, just the existence of a transaction |
988 | | // means its complete. |
989 | | SCLogDebug!("tx_get_alstate_progress"); |
990 | 959k | return 1; |
991 | 959k | } |
992 | | |
993 | 1.66M | pub(crate) unsafe extern "C" fn state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { |
994 | 1.66M | let state = cast_pointer!(state, DNSState); |
995 | | SCLogDebug!("state_get_tx_count: returning {}", state.tx_id); |
996 | 1.66M | return state.tx_id; |
997 | 1.66M | } |
998 | | |
999 | 0 | pub(crate) unsafe extern "C" fn state_get_tx( |
1000 | 0 | state: *mut std::os::raw::c_void, tx_id: u64, |
1001 | 0 | ) -> *mut std::os::raw::c_void { |
1002 | 0 | let state = cast_pointer!(state, DNSState); |
1003 | 0 | match state.get_tx(tx_id) { |
1004 | 0 | Some(tx) => { |
1005 | 0 | return tx as *const _ as *mut _; |
1006 | | } |
1007 | | None => { |
1008 | 0 | return std::ptr::null_mut(); |
1009 | | } |
1010 | | } |
1011 | 0 | } |
1012 | | |
1013 | | #[no_mangle] |
1014 | 1.11k | pub extern "C" fn SCDnsTxIsRequest(tx: &mut DNSTransaction) -> bool { |
1015 | 1.11k | tx.request.is_some() |
1016 | 1.11k | } |
1017 | | |
1018 | | #[no_mangle] |
1019 | 468 | pub extern "C" fn SCDnsTxIsResponse(tx: &mut DNSTransaction) -> bool { |
1020 | 468 | tx.response.is_some() |
1021 | 468 | } |
1022 | | |
1023 | 480k | pub(crate) unsafe extern "C" fn state_get_tx_data( |
1024 | 480k | tx: *mut std::os::raw::c_void, |
1025 | 480k | ) -> *mut AppLayerTxData { |
1026 | 480k | let tx = cast_pointer!(tx, DNSTransaction); |
1027 | 480k | return &mut tx.tx_data; |
1028 | 480k | } |
1029 | | |
1030 | 877 | pub(crate) unsafe extern "C" fn dns_get_state_data( |
1031 | 877 | state: *mut std::os::raw::c_void, |
1032 | 877 | ) -> *mut AppLayerStateData { |
1033 | 877 | let state = cast_pointer!(state, DNSState); |
1034 | 877 | return &mut state.state_data; |
1035 | 877 | } |
1036 | | |
1037 | | /// Get the DNS query name at index i. |
1038 | | #[no_mangle] |
1039 | 0 | pub unsafe extern "C" fn SCDnsTxGetQueryName( |
1040 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, flow_flags: u8, i: u32, |
1041 | 0 | buf: *mut *const u8, len: *mut u32, |
1042 | 0 | ) -> bool { |
1043 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
1044 | 0 | let queries = if (flow_flags & STREAM_TOSERVER) == 0 { |
1045 | 0 | tx.response.as_ref().map(|response| &response.queries) |
1046 | | } else { |
1047 | 0 | tx.request.as_ref().map(|request| &request.queries) |
1048 | | }; |
1049 | 0 | let index = i as usize; |
1050 | | |
1051 | 0 | if let Some(queries) = queries { |
1052 | 0 | if let Some(query) = queries.get(index) { |
1053 | 0 | if !query.name.value.is_empty() { |
1054 | 0 | *buf = query.name.value.as_ptr(); |
1055 | 0 | *len = query.name.value.len() as u32; |
1056 | 0 | return true; |
1057 | 0 | } |
1058 | 0 | } |
1059 | 0 | } |
1060 | | |
1061 | 0 | false |
1062 | 0 | } |
1063 | | |
1064 | | /// Get the DNS response answer name and index i. |
1065 | | #[no_mangle] |
1066 | | pub unsafe extern "C" fn SCDnsTxGetAnswerName( |
1067 | | _de: *mut DetectEngineThreadCtx, tx: *const c_void, flow_flags: u8, i: u32, |
1068 | | buf: *mut *const u8, len: *mut u32, |
1069 | | ) -> bool { |
1070 | | let tx = cast_pointer!(tx, DNSTransaction); |
1071 | | let answers = if (flow_flags & STREAM_TOSERVER) == 0 { |
1072 | | tx.response.as_ref().map(|response| &response.answers) |
1073 | | } else { |
1074 | | tx.request.as_ref().map(|request| &request.answers) |
1075 | | }; |
1076 | | let index = i as usize; |
1077 | | |
1078 | | if let Some(answers) = answers { |
1079 | | if let Some(answer) = answers.get(index) { |
1080 | | if !answer.name.value.is_empty() { |
1081 | | *buf = answer.name.value.as_ptr(); |
1082 | | *len = answer.name.value.len() as u32; |
1083 | | return true; |
1084 | | } |
1085 | | } |
1086 | | } |
1087 | | |
1088 | | false |
1089 | | } |
1090 | | |
1091 | | /// Get the DNS response authority name at index i. |
1092 | | #[no_mangle] |
1093 | 0 | pub unsafe extern "C" fn SCDnsTxGetAuthorityName( |
1094 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, _flow_flags: u8, i: u32, |
1095 | 0 | buf: *mut *const u8, len: *mut u32, |
1096 | 0 | ) -> bool { |
1097 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
1098 | 0 | let index = i as usize; |
1099 | | |
1100 | 0 | if let Some(response) = &tx.response { |
1101 | 0 | if let Some(record) = response.authorities.get(index) { |
1102 | 0 | if !record.name.value.is_empty() { |
1103 | 0 | *buf = record.name.value.as_ptr(); |
1104 | 0 | *len = record.name.value.len() as u32; |
1105 | 0 | return true; |
1106 | 0 | } |
1107 | 0 | } |
1108 | 0 | } |
1109 | | |
1110 | 0 | false |
1111 | 0 | } |
1112 | | |
1113 | | /// Get the DNS response additional name at index i. |
1114 | | #[no_mangle] |
1115 | 0 | pub unsafe extern "C" fn SCDnsTxGetAdditionalName( |
1116 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, _flow_flags: u8, i: u32, |
1117 | 0 | buf: *mut *const u8, len: *mut u32, |
1118 | 0 | ) -> bool { |
1119 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
1120 | 0 | let index = i as usize; |
1121 | | |
1122 | 0 | if let Some(response) = &tx.response { |
1123 | 0 | if let Some(record) = response.additionals.get(index) { |
1124 | 0 | if !record.name.value.is_empty() { |
1125 | 0 | *buf = record.name.value.as_ptr(); |
1126 | 0 | *len = record.name.value.len() as u32; |
1127 | 0 | return true; |
1128 | 0 | } |
1129 | 0 | } |
1130 | 0 | } |
1131 | | |
1132 | 0 | false |
1133 | 0 | } |
1134 | | |
1135 | 0 | fn get_rdata_name(data: &DNSRData) -> Option<&DNSName> { |
1136 | 0 | match data { |
1137 | 0 | DNSRData::CNAME(name) | DNSRData::PTR(name) | DNSRData::MX(name) | DNSRData::NS(name) => { |
1138 | 0 | Some(name) |
1139 | | } |
1140 | 0 | DNSRData::SOA(soa) => Some(&soa.mname), |
1141 | 0 | _ => None, |
1142 | | } |
1143 | 0 | } |
1144 | | |
1145 | | /// Get the DNS response answer rdata at index i that could be a domain name. |
1146 | | #[no_mangle] |
1147 | | pub unsafe extern "C" fn SCDnsTxGetAnswerRdata( |
1148 | | tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, |
1149 | | ) -> bool { |
1150 | | let index = i as usize; |
1151 | | |
1152 | | if let Some(response) = &tx.response { |
1153 | | if let Some(record) = response.answers.get(index) { |
1154 | | if let Some(name) = get_rdata_name(&record.data) { |
1155 | | if !name.value.is_empty() { |
1156 | | *buf = name.value.as_ptr(); |
1157 | | *len = name.value.len() as u32; |
1158 | | return true; |
1159 | | } |
1160 | | } |
1161 | | } |
1162 | | } |
1163 | | |
1164 | | false |
1165 | | } |
1166 | | |
1167 | | /// Get the DNS response authority rdata at index i that could be a domain name. |
1168 | | #[no_mangle] |
1169 | 0 | pub unsafe extern "C" fn SCDnsTxGetAuthorityRdata( |
1170 | 0 | tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, |
1171 | 0 | ) -> bool { |
1172 | 0 | let index = i as usize; |
1173 | | |
1174 | 0 | if let Some(response) = &tx.response { |
1175 | 0 | if let Some(record) = response.authorities.get(index) { |
1176 | 0 | if let Some(name) = get_rdata_name(&record.data) { |
1177 | 0 | if !name.value.is_empty() { |
1178 | 0 | *buf = name.value.as_ptr(); |
1179 | 0 | *len = name.value.len() as u32; |
1180 | 0 | return true; |
1181 | 0 | } |
1182 | 0 | } |
1183 | 0 | } |
1184 | 0 | } |
1185 | | |
1186 | 0 | false |
1187 | 0 | } |
1188 | | |
1189 | | /// Get the DNS response additional rdata at index i that could be a domain name. |
1190 | | #[no_mangle] |
1191 | 0 | pub unsafe extern "C" fn SCDnsTxGetAdditionalRdata( |
1192 | 0 | tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, |
1193 | 0 | ) -> bool { |
1194 | 0 | let index = i as usize; |
1195 | | |
1196 | 0 | if let Some(response) = &tx.response { |
1197 | 0 | if let Some(record) = response.additionals.get(index) { |
1198 | 0 | if let Some(name) = get_rdata_name(&record.data) { |
1199 | 0 | if !name.value.is_empty() { |
1200 | 0 | *buf = name.value.as_ptr(); |
1201 | 0 | *len = name.value.len() as u32; |
1202 | 0 | return true; |
1203 | 0 | } |
1204 | 0 | } |
1205 | 0 | } |
1206 | 0 | } |
1207 | | |
1208 | 0 | false |
1209 | 0 | } |
1210 | | |
1211 | | /// Get the DNS response flags for a transaction. |
1212 | | #[no_mangle] |
1213 | 0 | pub extern "C" fn SCDnsTxGetResponseFlags(tx: &mut DNSTransaction) -> u16 { |
1214 | 0 | return tx.rcode(); |
1215 | 0 | } |
1216 | | |
1217 | 1.74k | pub(crate) unsafe extern "C" fn probe_udp( |
1218 | 1.74k | _flow: *const Flow, _dir: u8, input: *const u8, len: u32, rdir: *mut u8, |
1219 | 1.74k | ) -> AppProto { |
1220 | 1.74k | if input.is_null() || len < std::mem::size_of::<DNSHeader>() as u32 { |
1221 | 54 | return core::ALPROTO_UNKNOWN; |
1222 | 1.69k | } |
1223 | 1.69k | let slice: &[u8] = std::slice::from_raw_parts(input as *mut u8, len as usize); |
1224 | 1.69k | let (is_dns, is_request, _) = probe(slice, slice.len()); |
1225 | 1.69k | if is_dns { |
1226 | 487 | let dir = if is_request { |
1227 | 301 | Direction::ToServer |
1228 | | } else { |
1229 | 186 | Direction::ToClient |
1230 | | }; |
1231 | 487 | *rdir = dir as u8; |
1232 | 487 | return ALPROTO_DNS; |
1233 | 1.20k | } |
1234 | 1.20k | return 0; |
1235 | 1.74k | } |
1236 | | |
1237 | 31.3k | unsafe extern "C" fn c_probe_tcp( |
1238 | 31.3k | _flow: *const Flow, direction: u8, input: *const u8, len: u32, rdir: *mut u8, |
1239 | 31.3k | ) -> AppProto { |
1240 | 31.3k | if input.is_null() || len < std::mem::size_of::<DNSHeader>() as u32 + 2 { |
1241 | 27.5k | return core::ALPROTO_UNKNOWN; |
1242 | 3.79k | } |
1243 | 3.79k | let slice: &[u8] = std::slice::from_raw_parts(input as *mut u8, len as usize); |
1244 | | //is_incomplete is checked by caller |
1245 | 3.79k | let (is_dns, is_request, _) = probe_tcp(slice); |
1246 | 3.79k | if is_dns { |
1247 | 537 | let dir = if is_request { |
1248 | 410 | Direction::ToServer |
1249 | | } else { |
1250 | 127 | Direction::ToClient |
1251 | | }; |
1252 | 537 | if (direction & DIR_BOTH) != u8::from(dir) { |
1253 | 367 | *rdir = dir as u8; |
1254 | 367 | } |
1255 | 537 | return ALPROTO_DNS; |
1256 | 3.26k | } |
1257 | 3.26k | return 0; |
1258 | 31.3k | } |
1259 | | |
1260 | 0 | unsafe extern "C" fn apply_tx_config( |
1261 | 0 | _state: *mut std::os::raw::c_void, _tx: *mut std::os::raw::c_void, _mode: std::os::raw::c_int, |
1262 | 0 | config: AppLayerTxConfig, |
1263 | 0 | ) { |
1264 | 0 | let tx = cast_pointer!(_tx, DNSTransaction); |
1265 | 0 | let state = cast_pointer!(_state, DNSState); |
1266 | 0 | if let Some(request) = &tx.request { |
1267 | 0 | if state.config.is_none() { |
1268 | 0 | state.config = Some(ConfigTracker::new()); |
1269 | 0 | } |
1270 | 0 | if let Some(ref mut tracker) = &mut state.config { |
1271 | 0 | tracker.add(request.header.tx_id, config); |
1272 | 0 | } |
1273 | 0 | } |
1274 | 0 | } |
1275 | | |
1276 | | #[no_mangle] |
1277 | 40 | pub unsafe extern "C" fn SCRegisterDnsUdpParser() { |
1278 | 40 | let default_port = std::ffi::CString::new("[53]").unwrap(); |
1279 | 40 | let parser = RustParser { |
1280 | 40 | name: b"dns\0".as_ptr() as *const std::os::raw::c_char, |
1281 | 40 | default_port: default_port.as_ptr(), |
1282 | 40 | ipproto: IPPROTO_UDP, |
1283 | 40 | probe_ts: Some(probe_udp), |
1284 | 40 | probe_tc: Some(probe_udp), |
1285 | 40 | min_depth: 0, |
1286 | 40 | max_depth: std::mem::size_of::<DNSHeader>() as u16, |
1287 | 40 | state_new, |
1288 | 40 | state_free, |
1289 | 40 | tx_free: state_tx_free, |
1290 | 40 | parse_ts: parse_request, |
1291 | 40 | parse_tc: parse_response, |
1292 | 40 | get_tx_count: state_get_tx_count, |
1293 | 40 | get_tx: state_get_tx, |
1294 | 40 | tx_comp_st_ts: 1, |
1295 | 40 | tx_comp_st_tc: 1, |
1296 | 40 | tx_get_progress: tx_get_alstate_progress, |
1297 | 40 | get_eventinfo: Some(DNSEvent::get_event_info), |
1298 | 40 | get_eventinfo_byid: Some(DNSEvent::get_event_info_by_id), |
1299 | 40 | localstorage_new: None, |
1300 | 40 | localstorage_free: None, |
1301 | 40 | get_tx_files: None, |
1302 | 40 | get_tx_iterator: Some(crate::applayer::state_get_tx_iterator::<DNSState, DNSTransaction>), |
1303 | 40 | get_tx_data: state_get_tx_data, |
1304 | 40 | get_state_data: dns_get_state_data, |
1305 | 40 | apply_tx_config: Some(apply_tx_config), |
1306 | 40 | flags: 0, |
1307 | 40 | get_frame_id_by_name: Some(DnsFrameType::ffi_id_from_name), |
1308 | 40 | get_frame_name_by_id: Some(DnsFrameType::ffi_name_from_id), |
1309 | 40 | get_state_id_by_name: None, |
1310 | 40 | get_state_name_by_id: None, |
1311 | 40 | }; |
1312 | | |
1313 | 40 | let ip_proto_str = CString::new("udp").unwrap(); |
1314 | 40 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1315 | 40 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
1316 | 40 | ALPROTO_DNS = alproto; |
1317 | 40 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1318 | 40 | let _ = AppLayerRegisterParser(&parser, alproto); |
1319 | 40 | } |
1320 | 0 | } |
1321 | 40 | } |
1322 | | |
1323 | | #[no_mangle] |
1324 | 40 | pub unsafe extern "C" fn SCRegisterDnsTcpParser() { |
1325 | 40 | let default_port = std::ffi::CString::new("53").unwrap(); |
1326 | 40 | let parser = RustParser { |
1327 | 40 | name: b"dns\0".as_ptr() as *const std::os::raw::c_char, |
1328 | 40 | default_port: default_port.as_ptr(), |
1329 | 40 | ipproto: IPPROTO_TCP, |
1330 | 40 | probe_ts: Some(c_probe_tcp), |
1331 | 40 | probe_tc: Some(c_probe_tcp), |
1332 | 40 | min_depth: 0, |
1333 | 40 | max_depth: std::mem::size_of::<DNSHeader>() as u16 + 2, |
1334 | 40 | state_new, |
1335 | 40 | state_free, |
1336 | 40 | tx_free: state_tx_free, |
1337 | 40 | parse_ts: parse_request_tcp, |
1338 | 40 | parse_tc: parse_response_tcp, |
1339 | 40 | get_tx_count: state_get_tx_count, |
1340 | 40 | get_tx: state_get_tx, |
1341 | 40 | tx_comp_st_ts: 1, |
1342 | 40 | tx_comp_st_tc: 1, |
1343 | 40 | tx_get_progress: tx_get_alstate_progress, |
1344 | 40 | get_eventinfo: Some(DNSEvent::get_event_info), |
1345 | 40 | get_eventinfo_byid: Some(DNSEvent::get_event_info_by_id), |
1346 | 40 | localstorage_new: None, |
1347 | 40 | localstorage_free: None, |
1348 | 40 | get_tx_files: None, |
1349 | 40 | get_tx_iterator: Some(crate::applayer::state_get_tx_iterator::<DNSState, DNSTransaction>), |
1350 | 40 | get_tx_data: state_get_tx_data, |
1351 | 40 | get_state_data: dns_get_state_data, |
1352 | 40 | apply_tx_config: Some(apply_tx_config), |
1353 | 40 | flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS, |
1354 | 40 | get_frame_id_by_name: Some(DnsFrameType::ffi_id_from_name), |
1355 | 40 | get_frame_name_by_id: Some(DnsFrameType::ffi_name_from_id), |
1356 | 40 | get_state_id_by_name: None, |
1357 | 40 | get_state_name_by_id: None, |
1358 | 40 | }; |
1359 | | |
1360 | 40 | let ip_proto_str = CString::new("tcp").unwrap(); |
1361 | 40 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1362 | 40 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
1363 | 40 | ALPROTO_DNS = alproto; |
1364 | 40 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1365 | 40 | let _ = AppLayerRegisterParser(&parser, alproto); |
1366 | 40 | } |
1367 | 0 | } |
1368 | 40 | } |
1369 | | |
1370 | | #[cfg(test)] |
1371 | | mod tests { |
1372 | | |
1373 | | use super::*; |
1374 | | |
1375 | | #[test] |
1376 | | fn test_dns_parse_request_tcp_valid() { |
1377 | | // A UDP DNS request with the DNS payload starting at byte 42. |
1378 | | // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap |
1379 | | #[rustfmt::skip] |
1380 | | let buf: &[u8] = &[ |
1381 | | 0x00, 0x15, 0x17, 0x0d, 0x06, 0xf7, 0xd8, 0xcb, /* ........ */ |
1382 | | 0x8a, 0xed, 0xa1, 0x46, 0x08, 0x00, 0x45, 0x00, /* ...F..E. */ |
1383 | | 0x00, 0x4d, 0x23, 0x11, 0x00, 0x00, 0x40, 0x11, /* .M#...@. */ |
1384 | | 0x41, 0x64, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10, /* Ad...... */ |
1385 | | 0x01, 0x01, 0xa3, 0x4d, 0x00, 0x35, 0x00, 0x39, /* ...M.5.9 */ |
1386 | | 0xb2, 0xb3, 0x8d, 0x32, 0x01, 0x20, 0x00, 0x01, /* ...2. .. */ |
1387 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, /* .......w */ |
1388 | | 0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */ |
1389 | | 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */ |
1390 | | 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */ |
1391 | | 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, /* ..)..... */ |
1392 | | 0x00, 0x00, 0x00 /* ... */ |
1393 | | ]; |
1394 | | |
1395 | | // The DNS payload starts at offset 42. |
1396 | | let dns_payload = &buf[42..]; |
1397 | | |
1398 | | // Make a TCP DNS request payload. |
1399 | | let mut request = Vec::new(); |
1400 | | request.push(((dns_payload.len() as u16) >> 8) as u8); |
1401 | | request.push(((dns_payload.len() as u16) & 0xff) as u8); |
1402 | | request.extend(dns_payload); |
1403 | | |
1404 | | let mut state = DNSState::new(); |
1405 | | assert_eq!( |
1406 | | AppLayerResult::ok(), |
1407 | | state.parse_request_tcp( |
1408 | | std::ptr::null_mut(), |
1409 | | StreamSlice::from_slice(&request, STREAM_TOSERVER, 0) |
1410 | | ) |
1411 | | ); |
1412 | | } |
1413 | | |
1414 | | #[test] |
1415 | | fn test_dns_parse_request_tcp_short_payload() { |
1416 | | // A UDP DNS request with the DNS payload starting at byte 42. |
1417 | | // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap |
1418 | | #[rustfmt::skip] |
1419 | | let buf: &[u8] = &[ |
1420 | | 0x00, 0x15, 0x17, 0x0d, 0x06, 0xf7, 0xd8, 0xcb, /* ........ */ |
1421 | | 0x8a, 0xed, 0xa1, 0x46, 0x08, 0x00, 0x45, 0x00, /* ...F..E. */ |
1422 | | 0x00, 0x4d, 0x23, 0x11, 0x00, 0x00, 0x40, 0x11, /* .M#...@. */ |
1423 | | 0x41, 0x64, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10, /* Ad...... */ |
1424 | | 0x01, 0x01, 0xa3, 0x4d, 0x00, 0x35, 0x00, 0x39, /* ...M.5.9 */ |
1425 | | 0xb2, 0xb3, 0x8d, 0x32, 0x01, 0x20, 0x00, 0x01, /* ...2. .. */ |
1426 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, /* .......w */ |
1427 | | 0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */ |
1428 | | 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */ |
1429 | | 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */ |
1430 | | 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, /* ..)..... */ |
1431 | | 0x00, 0x00, 0x00 /* ... */ |
1432 | | ]; |
1433 | | |
1434 | | // The DNS payload starts at offset 42. |
1435 | | let dns_payload = &buf[42..]; |
1436 | | |
1437 | | // Make a TCP DNS request payload but with the length 1 larger |
1438 | | // than the available data. |
1439 | | let mut request = Vec::new(); |
1440 | | request.push(((dns_payload.len() as u16) >> 8) as u8); |
1441 | | request.push(((dns_payload.len() as u16) & 0xff) as u8 + 1); |
1442 | | request.extend(dns_payload); |
1443 | | |
1444 | | let mut state = DNSState::new(); |
1445 | | assert_eq!( |
1446 | | AppLayerResult::incomplete(0, 52), |
1447 | | state.parse_request_tcp( |
1448 | | std::ptr::null_mut(), |
1449 | | StreamSlice::from_slice(&request, STREAM_TOSERVER, 0) |
1450 | | ) |
1451 | | ); |
1452 | | } |
1453 | | |
1454 | | #[test] |
1455 | | fn test_dns_parse_response_tcp_valid() { |
1456 | | // A UDP DNS response with the DNS payload starting at byte 42. |
1457 | | // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap |
1458 | | #[rustfmt::skip] |
1459 | | let buf: &[u8] = &[ |
1460 | | 0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15, /* .....F.. */ |
1461 | | 0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00, /* ......E. */ |
1462 | | 0x00, 0x80, 0x65, 0x4e, 0x40, 0x00, 0x40, 0x11, /* ..eN@.@. */ |
1463 | | 0xbe, 0xf3, 0x0a, 0x10, 0x01, 0x01, 0x0a, 0x10, /* ........ */ |
1464 | | 0x01, 0x0b, 0x00, 0x35, 0xa3, 0x4d, 0x00, 0x6c, /* ...5.M.l */ |
1465 | | 0x8d, 0x8c, 0x8d, 0x32, 0x81, 0xa0, 0x00, 0x01, /* ...2.... */ |
1466 | | 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, /* .......w */ |
1467 | | 0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */ |
1468 | | 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */ |
1469 | | 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */ |
1470 | | 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, /* ........ */ |
1471 | | 0x0d, 0xd8, 0x00, 0x12, 0x0c, 0x73, 0x75, 0x72, /* .....sur */ |
1472 | | 0x69, 0x63, 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, /* icata-id */ |
1473 | | 0x73, 0x03, 0x6f, 0x72, 0x67, 0x00, 0xc0, 0x32, /* s.org..2 */ |
1474 | | 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */ |
1475 | | 0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0xc0, 0x32, /* ....N..2 */ |
1476 | | 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */ |
1477 | | 0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19 /* ....N. */ |
1478 | | ]; |
1479 | | |
1480 | | // The DNS payload starts at offset 42. |
1481 | | let dns_payload = &buf[42..]; |
1482 | | |
1483 | | // Make a TCP DNS response payload. |
1484 | | let mut request = Vec::new(); |
1485 | | request.push(((dns_payload.len() as u16) >> 8) as u8); |
1486 | | request.push(((dns_payload.len() as u16) & 0xff) as u8); |
1487 | | request.extend(dns_payload); |
1488 | | |
1489 | | let mut state = DNSState::new(); |
1490 | | assert_eq!( |
1491 | | AppLayerResult::ok(), |
1492 | | state.parse_response_tcp( |
1493 | | std::ptr::null_mut(), |
1494 | | StreamSlice::from_slice(&request, STREAM_TOCLIENT, 0) |
1495 | | ) |
1496 | | ); |
1497 | | } |
1498 | | |
1499 | | // Test that a TCP DNS payload won't be parsed if there is not |
1500 | | // enough data. |
1501 | | #[test] |
1502 | | fn test_dns_parse_response_tcp_short_payload() { |
1503 | | // A UDP DNS response with the DNS payload starting at byte 42. |
1504 | | // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap |
1505 | | #[rustfmt::skip] |
1506 | | let buf: &[u8] = &[ |
1507 | | 0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15, /* .....F.. */ |
1508 | | 0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00, /* ......E. */ |
1509 | | 0x00, 0x80, 0x65, 0x4e, 0x40, 0x00, 0x40, 0x11, /* ..eN@.@. */ |
1510 | | 0xbe, 0xf3, 0x0a, 0x10, 0x01, 0x01, 0x0a, 0x10, /* ........ */ |
1511 | | 0x01, 0x0b, 0x00, 0x35, 0xa3, 0x4d, 0x00, 0x6c, /* ...5.M.l */ |
1512 | | 0x8d, 0x8c, 0x8d, 0x32, 0x81, 0xa0, 0x00, 0x01, /* ...2.... */ |
1513 | | 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, /* .......w */ |
1514 | | 0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */ |
1515 | | 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */ |
1516 | | 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */ |
1517 | | 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, /* ........ */ |
1518 | | 0x0d, 0xd8, 0x00, 0x12, 0x0c, 0x73, 0x75, 0x72, /* .....sur */ |
1519 | | 0x69, 0x63, 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, /* icata-id */ |
1520 | | 0x73, 0x03, 0x6f, 0x72, 0x67, 0x00, 0xc0, 0x32, /* s.org..2 */ |
1521 | | 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */ |
1522 | | 0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0xc0, 0x32, /* ....N..2 */ |
1523 | | 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */ |
1524 | | 0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19 /* ....N. */ |
1525 | | ]; |
1526 | | |
1527 | | // The DNS payload starts at offset 42. |
1528 | | let dns_payload = &buf[42..]; |
1529 | | |
1530 | | // Make a TCP DNS response payload, but make the length 1 byte |
1531 | | // larger than the actual size. |
1532 | | let mut request = Vec::new(); |
1533 | | request.push(((dns_payload.len() as u16) >> 8) as u8); |
1534 | | request.push((((dns_payload.len() as u16) & 0xff) + 1) as u8); |
1535 | | request.extend(dns_payload); |
1536 | | |
1537 | | let mut state = DNSState::new(); |
1538 | | assert_eq!( |
1539 | | AppLayerResult::incomplete(0, 103), |
1540 | | state.parse_response_tcp( |
1541 | | std::ptr::null_mut(), |
1542 | | StreamSlice::from_slice(&request, STREAM_TOCLIENT, 0) |
1543 | | ) |
1544 | | ); |
1545 | | } |
1546 | | |
1547 | | // Port of the C RustDNSUDPParserTest02 unit test. |
1548 | | #[test] |
1549 | | fn test_dns_udp_parser_test_01() { |
1550 | | /* query: abcdefghijk.com |
1551 | | * TTL: 86400 |
1552 | | * serial 20130422 refresh 28800 retry 7200 exp 604800 min ttl 86400 |
1553 | | * ns, hostmaster */ |
1554 | | #[rustfmt::skip] |
1555 | | let buf: &[u8] = &[ |
1556 | | 0x00, 0x3c, 0x85, 0x00, 0x00, 0x01, 0x00, 0x00, |
1557 | | 0x00, 0x01, 0x00, 0x00, 0x0b, 0x61, 0x62, 0x63, |
1558 | | 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, |
1559 | | 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x0f, 0x00, |
1560 | | 0x01, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01, |
1561 | | 0x51, 0x80, 0x00, 0x25, 0x02, 0x6e, 0x73, 0x00, |
1562 | | 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x73, |
1563 | | 0x74, 0x65, 0x72, 0xc0, 0x2f, 0x01, 0x33, 0x2a, |
1564 | | 0x76, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x1c, |
1565 | | 0x20, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01, 0x51, |
1566 | | 0x80, |
1567 | | ]; |
1568 | | let mut state = DNSState::new(); |
1569 | | assert!(state.parse_response(buf, false, None, std::ptr::null())); |
1570 | | } |
1571 | | |
1572 | | // Port of the C RustDNSUDPParserTest02 unit test. |
1573 | | #[test] |
1574 | | fn test_dns_udp_parser_test_02() { |
1575 | | #[rustfmt::skip] |
1576 | | let buf: &[u8] = &[ |
1577 | | 0x6D,0x08,0x84,0x80,0x00,0x01,0x00,0x08,0x00,0x00,0x00,0x01,0x03,0x57,0x57,0x57, |
1578 | | 0x04,0x54,0x54,0x54,0x54,0x03,0x56,0x56,0x56,0x03,0x63,0x6F,0x6D,0x02,0x79,0x79, |
1579 | | 0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00, |
1580 | | 0x02,0xC0,0x0C,0xC0,0x31,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0, |
1581 | | 0x31,0xC0,0x3F,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x3F,0xC0, |
1582 | | 0x4D,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x4D,0xC0,0x5B,0x00, |
1583 | | 0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x5B,0xC0,0x69,0x00,0x05,0x00, |
1584 | | 0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x69,0xC0,0x77,0x00,0x05,0x00,0x01,0x00, |
1585 | | 0x00,0x0E,0x10,0x00,0x02,0xC0,0x77,0xC0,0x85,0x00,0x05,0x00,0x01,0x00,0x00,0x0E, |
1586 | | 0x10,0x00,0x02,0xC0,0x85,0x00,0x00,0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
1587 | | ]; |
1588 | | let mut state = DNSState::new(); |
1589 | | assert!(state.parse_response(buf, false, None, std::ptr::null())); |
1590 | | } |
1591 | | |
1592 | | // Port of the C RustDNSUDPParserTest03 unit test. |
1593 | | #[test] |
1594 | | fn test_dns_udp_parser_test_03() { |
1595 | | #[rustfmt::skip] |
1596 | | let buf: &[u8] = &[ |
1597 | | 0x6F,0xB4,0x84,0x80,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x03,0x03,0x57,0x57,0x77, |
1598 | | 0x0B,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x03,0x55,0x55,0x55, |
1599 | | 0x02,0x79,0x79,0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00, |
1600 | | 0x0E,0x10,0x00,0x02,0xC0,0x10,0xC0,0x34,0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10, |
1601 | | 0x00,0x04,0xC3,0xEA,0x04,0x19,0xC0,0x34,0x00,0x02,0x00,0x01,0x00,0x00,0x0E,0x10, |
1602 | | 0x00,0x0A,0x03,0x6E,0x73,0x31,0x03,0x61,0x67,0x62,0xC0,0x20,0xC0,0x46,0x00,0x02, |
1603 | | 0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x06,0x03,0x6E,0x73,0x32,0xC0,0x56,0xC0,0x52, |
1604 | | 0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x04,0x0A,0xC0,0x68, |
1605 | | 0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x05,0x14,0x00,0x00, |
1606 | | 0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00 |
1607 | | ]; |
1608 | | let mut state = DNSState::new(); |
1609 | | assert!(state.parse_response(buf, false, None, std::ptr::null())); |
1610 | | } |
1611 | | |
1612 | | // Port of the C RustDNSUDPParserTest04 unit test. |
1613 | | // |
1614 | | // Test the TXT records in an answer. |
1615 | | #[test] |
1616 | | fn test_dns_udp_parser_test_04() { |
1617 | | #[rustfmt::skip] |
1618 | | let buf: &[u8] = &[ |
1619 | | 0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41, |
1620 | | 0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72, |
1621 | | 0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00, |
1622 | | 0x01, |
1623 | | /* answer record start */ |
1624 | | 0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22, |
1625 | | /* txt record starts: */ |
1626 | | 0x20, /* <txt len 32 */ 0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46, |
1627 | | 0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31, |
1628 | | 0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */ 0xc0,0x1d,0x00,0x02,0x00,0x01, |
1629 | | 0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0, |
1630 | | 0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f |
1631 | | ]; |
1632 | | let mut state = DNSState::new(); |
1633 | | assert!(state.parse_response(buf, false, None, std::ptr::null())); |
1634 | | } |
1635 | | |
1636 | | // Port of the C RustDNSUDPParserTest05 unit test. |
1637 | | // |
1638 | | // Test TXT records in answer with a bad length. |
1639 | | #[test] |
1640 | | fn test_dns_udp_parser_test_05() { |
1641 | | #[rustfmt::skip] |
1642 | | let buf: &[u8] = &[ |
1643 | | 0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41, |
1644 | | 0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72, |
1645 | | 0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00, |
1646 | | 0x01, |
1647 | | /* answer record start */ |
1648 | | 0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22, |
1649 | | /* txt record starts: */ |
1650 | | 0x40, /* <txt len 64 */ 0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46, |
1651 | | 0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31, |
1652 | | 0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */ 0xc0,0x1d,0x00,0x02,0x00,0x01, |
1653 | | 0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0, |
1654 | | 0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f |
1655 | | ]; |
1656 | | let mut state = DNSState::new(); |
1657 | | assert!(!state.parse_response(buf, false, None, std::ptr::null())); |
1658 | | } |
1659 | | |
1660 | | // Port of the C RustDNSTCPParserTestMultiRecord unit test. |
1661 | | #[test] |
1662 | | fn test_dns_tcp_parser_multi_record() { |
1663 | | #[rustfmt::skip] |
1664 | | let buf: &[u8] = &[ |
1665 | | 0x00, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, |
1666 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30, |
1667 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1668 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1669 | | 0x00, 0x1e, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, |
1670 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31, |
1671 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1672 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1673 | | 0x00, 0x1e, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, |
1674 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, |
1675 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1676 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1677 | | 0x00, 0x1e, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, |
1678 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x33, |
1679 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1680 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1681 | | 0x00, 0x1e, 0x00, 0x04, 0x01, 0x00, 0x00, 0x01, |
1682 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x34, |
1683 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1684 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1685 | | 0x00, 0x1e, 0x00, 0x05, 0x01, 0x00, 0x00, 0x01, |
1686 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x35, |
1687 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1688 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1689 | | 0x00, 0x1e, 0x00, 0x06, 0x01, 0x00, 0x00, 0x01, |
1690 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x36, |
1691 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1692 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1693 | | 0x00, 0x1e, 0x00, 0x07, 0x01, 0x00, 0x00, 0x01, |
1694 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x37, |
1695 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1696 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1697 | | 0x00, 0x1e, 0x00, 0x08, 0x01, 0x00, 0x00, 0x01, |
1698 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, |
1699 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1700 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1701 | | 0x00, 0x1e, 0x00, 0x09, 0x01, 0x00, 0x00, 0x01, |
1702 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39, |
1703 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1704 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1705 | | 0x00, 0x1f, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x01, |
1706 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, |
1707 | | 0x30, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, |
1708 | | 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, |
1709 | | 0x01, 0x00, 0x1f, 0x00, 0x0b, 0x01, 0x00, 0x00, |
1710 | | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
1711 | | 0x31, 0x31, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, |
1712 | | 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, |
1713 | | 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0c, 0x01, 0x00, |
1714 | | 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
1715 | | 0x02, 0x31, 0x32, 0x06, 0x67, 0x6f, 0x6f, 0x67, |
1716 | | 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, |
1717 | | 0x01, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0d, 0x01, |
1718 | | 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, |
1719 | | 0x00, 0x02, 0x31, 0x33, 0x06, 0x67, 0x6f, 0x6f, |
1720 | | 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, |
1721 | | 0x00, 0x01, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0e, |
1722 | | 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, |
1723 | | 0x00, 0x00, 0x02, 0x31, 0x34, 0x06, 0x67, 0x6f, |
1724 | | 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, |
1725 | | 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1f, 0x00, |
1726 | | 0x0f, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, |
1727 | | 0x00, 0x00, 0x00, 0x02, 0x31, 0x35, 0x06, 0x67, |
1728 | | 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, |
1729 | | 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1f, |
1730 | | 0x00, 0x10, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, |
1731 | | 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x36, 0x06, |
1732 | | 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, |
1733 | | 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, |
1734 | | 0x1f, 0x00, 0x11, 0x01, 0x00, 0x00, 0x01, 0x00, |
1735 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x37, |
1736 | | 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, |
1737 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, |
1738 | | 0x00, 0x1f, 0x00, 0x12, 0x01, 0x00, 0x00, 0x01, |
1739 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, |
1740 | | 0x38, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, |
1741 | | 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, |
1742 | | 0x01, 0x00, 0x1f, 0x00, 0x13, 0x01, 0x00, 0x00, |
1743 | | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
1744 | | 0x31, 0x39, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, |
1745 | | 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, |
1746 | | 0x00, 0x01 |
1747 | | ]; |
1748 | | |
1749 | | // A NULL flow. |
1750 | | let flow = std::ptr::null_mut(); |
1751 | | |
1752 | | let mut state = DNSState::new(); |
1753 | | assert_eq!( |
1754 | | AppLayerResult::ok(), |
1755 | | state.parse_request_tcp(flow, StreamSlice::from_slice(buf, STREAM_TOSERVER, 0)) |
1756 | | ); |
1757 | | } |
1758 | | |
1759 | | #[test] |
1760 | | fn test_dns_tcp_parser_split_payload() { |
1761 | | // A NULL flow. |
1762 | | let flow = std::ptr::null_mut(); |
1763 | | |
1764 | | /* incomplete payload */ |
1765 | | #[rustfmt::skip] |
1766 | | let buf1: &[u8] = &[ |
1767 | | 0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01, |
1768 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
1769 | | ]; |
1770 | | /* complete payload plus the start of a new payload */ |
1771 | | #[rustfmt::skip] |
1772 | | let buf2: &[u8] = &[ |
1773 | | 0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01, |
1774 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
1775 | | 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x03, |
1776 | | 0x63, 0x6F, 0x6D, 0x00, 0x00, 0x10, 0x00, 0x01, |
1777 | | |
1778 | | // next. |
1779 | | 0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01, |
1780 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
1781 | | ]; |
1782 | | |
1783 | | /* and the complete payload again with no trailing data. */ |
1784 | | #[rustfmt::skip] |
1785 | | let buf3: &[u8] = &[ |
1786 | | 0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01, |
1787 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
1788 | | 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x03, |
1789 | | 0x63, 0x6F, 0x6D, 0x00, 0x00, 0x10, 0x00, 0x01, |
1790 | | ]; |
1791 | | |
1792 | | let mut state = DNSState::new(); |
1793 | | assert_eq!( |
1794 | | AppLayerResult::incomplete(0, 30), |
1795 | | state.parse_request_tcp(flow, StreamSlice::from_slice(buf1, STREAM_TOSERVER, 0)) |
1796 | | ); |
1797 | | assert_eq!( |
1798 | | AppLayerResult::incomplete(30, 30), |
1799 | | state.parse_request_tcp(flow, StreamSlice::from_slice(buf2, STREAM_TOSERVER, 0)) |
1800 | | ); |
1801 | | assert_eq!( |
1802 | | AppLayerResult::ok(), |
1803 | | state.parse_request_tcp(flow, StreamSlice::from_slice(buf3, STREAM_TOSERVER, 0)) |
1804 | | ); |
1805 | | } |
1806 | | |
1807 | | #[test] |
1808 | | fn test_dns_event_from_id() { |
1809 | | assert_eq!(DNSEvent::from_id(0), Some(DNSEvent::MalformedData)); |
1810 | | assert_eq!(DNSEvent::from_id(3), Some(DNSEvent::ZFlagSet)); |
1811 | | assert_eq!(DNSEvent::from_id(99), None); |
1812 | | } |
1813 | | |
1814 | | #[test] |
1815 | | fn test_dns_event_to_cstring() { |
1816 | | assert_eq!(DNSEvent::MalformedData.to_cstring(), "malformed_data\0"); |
1817 | | } |
1818 | | |
1819 | | #[test] |
1820 | | fn test_dns_event_from_string() { |
1821 | | let name = "malformed_data"; |
1822 | | let event = DNSEvent::from_string(name).unwrap(); |
1823 | | assert_eq!(event, DNSEvent::MalformedData); |
1824 | | assert_eq!(event.to_cstring(), format!("{}\0", name)); |
1825 | | } |
1826 | | } |