/src/suricata8/rust/src/dns/detect.rs
Line | Count | Source |
1 | | /* Copyright (C) 2019-2024 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::dns::{DNSRcode, DNSRecordType, DNSTransaction, ALPROTO_DNS}; |
19 | | use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; |
20 | | use crate::detect::uint::{ |
21 | | detect_match_uint, detect_parse_uint_enum, DetectUintData, SCDetectU16Free, SCDetectU8Free, |
22 | | SCDetectU8Parse, |
23 | | }; |
24 | | use crate::detect::{helper_keyword_register_sticky_buffer, SigTableElmtStickyBuffer}; |
25 | | use crate::direction::Direction; |
26 | | use std::ffi::CStr; |
27 | | use std::os::raw::{c_int, c_void}; |
28 | | use suricata_sys::sys::{ |
29 | | DetectEngineCtx, DetectEngineThreadCtx, Flow, SCDetectBufferSetActiveList, |
30 | | SCDetectHelperBufferRegister, SCDetectHelperKeywordAliasRegister, |
31 | | SCDetectHelperKeywordRegister, SCDetectHelperMultiBufferProgressMpmRegister, |
32 | | SCDetectSignatureSetAppProto, SCSigMatchAppendSMToList, SCSigTableAppLiteElmt, SigMatchCtx, |
33 | | Signature, |
34 | | }; |
35 | | |
36 | | /// Perform the DNS opcode match. |
37 | | /// |
38 | | /// 1 will be returned on match, otherwise 0 will be returned. |
39 | 0 | unsafe extern "C" fn dns_opcode_match( |
40 | 0 | _de: *mut DetectEngineThreadCtx, _f: *mut Flow, flags: u8, _state: *mut c_void, |
41 | 0 | tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx, |
42 | 0 | ) -> c_int { |
43 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
44 | 0 | let ctx = cast_pointer!(ctx, DetectUintData<u8>); |
45 | 0 | let header_flags = if flags & Direction::ToServer as u8 != 0 { |
46 | 0 | if let Some(request) = &tx.request { |
47 | 0 | request.header.flags |
48 | | } else { |
49 | 0 | return 0; |
50 | | } |
51 | 0 | } else if flags & Direction::ToClient as u8 != 0 { |
52 | 0 | if let Some(response) = &tx.response { |
53 | 0 | response.header.flags |
54 | | } else { |
55 | 0 | return 0; |
56 | | } |
57 | | } else { |
58 | | // Not to server or to client?? |
59 | 0 | return 0; |
60 | | }; |
61 | 0 | let opcode = ((header_flags >> 11) & 0xf) as u8; |
62 | | |
63 | 0 | if detect_match_uint(ctx, opcode) { |
64 | 0 | return 1; |
65 | 0 | } |
66 | 0 | return 0; |
67 | 0 | } |
68 | | |
69 | | /// Perform the DNS rcode match. |
70 | | /// |
71 | | /// 1 will be returned on match, otherwise 0 will be returned. |
72 | 0 | unsafe extern "C" fn dns_rcode_match( |
73 | 0 | _de: *mut DetectEngineThreadCtx, _f: *mut Flow, flags: u8, _state: *mut c_void, |
74 | 0 | tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx, |
75 | 0 | ) -> c_int { |
76 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
77 | 0 | let ctx = cast_pointer!(ctx, DetectUintData<u16>); |
78 | 0 | let header_flags = if flags & Direction::ToServer as u8 != 0 { |
79 | 0 | if let Some(request) = &tx.request { |
80 | 0 | request.header.flags |
81 | | } else { |
82 | 0 | return 0; |
83 | | } |
84 | 0 | } else if let Some(response) = &tx.response { |
85 | 0 | response.header.flags |
86 | | } else { |
87 | 0 | return 0; |
88 | | }; |
89 | | |
90 | 0 | let rcode = header_flags & 0xf; |
91 | | |
92 | 0 | if detect_match_uint(ctx, rcode) { |
93 | 0 | return 1; |
94 | 0 | } |
95 | 0 | return 0; |
96 | 0 | } |
97 | | |
98 | | /// Perform the DNS rrtype match. |
99 | | /// 1 will be returned on match, otherwise 0 will be returned. |
100 | 0 | unsafe extern "C" fn dns_rrtype_match( |
101 | 0 | _de: *mut DetectEngineThreadCtx, _f: *mut Flow, flags: u8, _state: *mut c_void, |
102 | 0 | tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx, |
103 | 0 | ) -> c_int { |
104 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
105 | 0 | let ctx = cast_pointer!(ctx, DetectUintData<u16>); |
106 | | |
107 | 0 | if flags & Direction::ToServer as u8 != 0 { |
108 | 0 | if let Some(request) = &tx.request { |
109 | 0 | for i in 0..request.queries.len() { |
110 | 0 | if detect_match_uint(ctx, request.queries[i].rrtype) { |
111 | 0 | return 1; |
112 | 0 | } |
113 | | } |
114 | 0 | } |
115 | 0 | } else if flags & Direction::ToClient as u8 != 0 { |
116 | 0 | if let Some(response) = &tx.response { |
117 | 0 | for i in 0..response.answers.len() { |
118 | 0 | if detect_match_uint(ctx, response.answers[i].rrtype) { |
119 | 0 | return 1; |
120 | 0 | } |
121 | | } |
122 | 0 | } |
123 | 0 | } |
124 | 0 | return 0; |
125 | 0 | } |
126 | | |
127 | | static mut G_DNS_ANSWER_NAME_BUFFER_ID: c_int = 0; |
128 | | static mut G_DNS_QUERY_NAME_BUFFER_ID: c_int = 0; |
129 | | static mut G_DNS_QUERY_BUFFER_ID: c_int = 0; |
130 | | static mut G_DNS_OPCODE_KW_ID: u16 = 0; |
131 | | static mut G_DNS_OPCODE_BUFFER_ID: c_int = 0; |
132 | | static mut G_DNS_RCODE_KW_ID: u16 = 0; |
133 | | static mut G_DNS_RCODE_BUFFER_ID: c_int = 0; |
134 | | static mut G_DNS_RRTYPE_KW_ID: u16 = 0; |
135 | | static mut G_DNS_RRTYPE_BUFFER_ID: c_int = 0; |
136 | | |
137 | 37 | unsafe extern "C" fn dns_opcode_setup( |
138 | 37 | de: *mut DetectEngineCtx, s: *mut Signature, raw: *const libc::c_char, |
139 | 37 | ) -> c_int { |
140 | 37 | if SCDetectSignatureSetAppProto(s, ALPROTO_DNS) != 0 { |
141 | 2 | return -1; |
142 | 35 | } |
143 | 35 | let ctx = SCDetectU8Parse(raw) as *mut c_void; |
144 | 35 | if ctx.is_null() { |
145 | 8 | return -1; |
146 | 27 | } |
147 | 27 | if SCSigMatchAppendSMToList( |
148 | 27 | de, |
149 | 27 | s, |
150 | 27 | G_DNS_OPCODE_KW_ID, |
151 | 27 | ctx as *mut SigMatchCtx, |
152 | 27 | G_DNS_OPCODE_BUFFER_ID, |
153 | | ) |
154 | 27 | .is_null() |
155 | | { |
156 | 0 | dns_opcode_free(std::ptr::null_mut(), ctx); |
157 | 0 | return -1; |
158 | 27 | } |
159 | 27 | return 0; |
160 | 37 | } |
161 | | |
162 | 27 | unsafe extern "C" fn dns_opcode_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { |
163 | | // Just unbox... |
164 | 27 | let ctx = cast_pointer!(ctx, DetectUintData<u8>); |
165 | 27 | SCDetectU8Free(ctx); |
166 | 27 | } |
167 | | |
168 | 29 | unsafe extern "C" fn dns_rcode_parse(ustr: *const std::os::raw::c_char) -> *mut DetectUintData<u8> { |
169 | 29 | let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe |
170 | 29 | if let Ok(s) = ft_name.to_str() { |
171 | 29 | if let Some(ctx) = detect_parse_uint_enum::<u16, DNSRcode>(s) { |
172 | 1 | let boxed = Box::new(ctx); |
173 | 1 | return Box::into_raw(boxed) as *mut _; |
174 | 28 | } |
175 | 0 | } |
176 | 28 | return std::ptr::null_mut(); |
177 | 29 | } |
178 | | |
179 | 30 | unsafe extern "C" fn dns_rcode_setup( |
180 | 30 | de: *mut DetectEngineCtx, s: *mut Signature, raw: *const libc::c_char, |
181 | 30 | ) -> c_int { |
182 | 30 | if SCDetectSignatureSetAppProto(s, ALPROTO_DNS) != 0 { |
183 | 1 | return -1; |
184 | 29 | } |
185 | 29 | let ctx = dns_rcode_parse(raw) as *mut c_void; |
186 | 29 | if ctx.is_null() { |
187 | 28 | return -1; |
188 | 1 | } |
189 | 1 | if SCSigMatchAppendSMToList( |
190 | 1 | de, |
191 | 1 | s, |
192 | 1 | G_DNS_RCODE_KW_ID, |
193 | 1 | ctx as *mut SigMatchCtx, |
194 | 1 | G_DNS_RCODE_BUFFER_ID, |
195 | | ) |
196 | 1 | .is_null() |
197 | | { |
198 | 0 | dns_rcode_free(std::ptr::null_mut(), ctx); |
199 | 0 | return -1; |
200 | 1 | } |
201 | 1 | return 0; |
202 | 30 | } |
203 | | |
204 | 1 | unsafe extern "C" fn dns_rcode_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { |
205 | | // Just unbox... |
206 | 1 | let ctx = cast_pointer!(ctx, DetectUintData<u16>); |
207 | 1 | SCDetectU16Free(ctx); |
208 | 1 | } |
209 | | |
210 | 92 | unsafe extern "C" fn dns_rrtype_parse( |
211 | 92 | ustr: *const std::os::raw::c_char, |
212 | 92 | ) -> *mut DetectUintData<u8> { |
213 | 92 | let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe |
214 | 92 | if let Ok(s) = ft_name.to_str() { |
215 | 92 | if let Some(ctx) = detect_parse_uint_enum::<u16, DNSRecordType>(s) { |
216 | 21 | let boxed = Box::new(ctx); |
217 | 21 | return Box::into_raw(boxed) as *mut _; |
218 | 71 | } |
219 | 0 | } |
220 | 71 | return std::ptr::null_mut(); |
221 | 92 | } |
222 | | |
223 | 94 | unsafe extern "C" fn dns_rrtype_setup( |
224 | 94 | de: *mut DetectEngineCtx, s: *mut Signature, raw: *const libc::c_char, |
225 | 94 | ) -> c_int { |
226 | 94 | if SCDetectSignatureSetAppProto(s, ALPROTO_DNS) != 0 { |
227 | 2 | return -1; |
228 | 92 | } |
229 | 92 | let ctx = dns_rrtype_parse(raw) as *mut c_void; |
230 | 92 | if ctx.is_null() { |
231 | 71 | return -1; |
232 | 21 | } |
233 | 21 | if SCSigMatchAppendSMToList( |
234 | 21 | de, |
235 | 21 | s, |
236 | 21 | G_DNS_RRTYPE_KW_ID, |
237 | 21 | ctx as *mut SigMatchCtx, |
238 | 21 | G_DNS_RRTYPE_BUFFER_ID, |
239 | | ) |
240 | 21 | .is_null() |
241 | | { |
242 | 0 | dns_rrtype_free(std::ptr::null_mut(), ctx); |
243 | 0 | return -1; |
244 | 21 | } |
245 | 21 | return 0; |
246 | 94 | } |
247 | | |
248 | 21 | unsafe extern "C" fn dns_rrtype_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { |
249 | | // Just unbox... |
250 | 21 | let ctx = cast_pointer!(ctx, DetectUintData<u16>); |
251 | 21 | SCDetectU16Free(ctx); |
252 | 21 | } |
253 | | |
254 | 3 | unsafe extern "C" fn dns_detect_answer_name_setup( |
255 | 3 | de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char, |
256 | 3 | ) -> c_int { |
257 | 3 | if SCDetectSignatureSetAppProto(s, ALPROTO_DNS) != 0 { |
258 | 2 | return -1; |
259 | 1 | } |
260 | 1 | if SCDetectBufferSetActiveList(de, s, G_DNS_ANSWER_NAME_BUFFER_ID) < 0 { |
261 | 0 | return -1; |
262 | 1 | } |
263 | 1 | return 0; |
264 | 3 | } |
265 | | |
266 | | /// Get the DNS response answer name and index i. |
267 | 0 | pub(crate) unsafe extern "C" fn dns_tx_get_answer_name( |
268 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, flags: u8, i: u32, buf: *mut *const u8, |
269 | 0 | len: *mut u32, |
270 | 0 | ) -> bool { |
271 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
272 | 0 | let answers = if flags & Direction::ToClient as u8 != 0 { |
273 | 0 | tx.response.as_ref().map(|response| &response.answers) |
274 | | } else { |
275 | 0 | tx.request.as_ref().map(|request| &request.answers) |
276 | | }; |
277 | 0 | let index = i as usize; |
278 | | |
279 | 0 | if let Some(answers) = answers { |
280 | 0 | if let Some(answer) = answers.get(index) { |
281 | 0 | if !answer.name.value.is_empty() { |
282 | 0 | *buf = answer.name.value.as_ptr(); |
283 | 0 | *len = answer.name.value.len() as u32; |
284 | 0 | return true; |
285 | 0 | } |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | 0 | false |
290 | 0 | } |
291 | | |
292 | 2 | unsafe extern "C" fn dns_detect_query_name_setup( |
293 | 2 | de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char, |
294 | 2 | ) -> c_int { |
295 | 2 | if SCDetectSignatureSetAppProto(s, ALPROTO_DNS) != 0 { |
296 | 1 | return -1; |
297 | 1 | } |
298 | 1 | if SCDetectBufferSetActiveList(de, s, G_DNS_QUERY_NAME_BUFFER_ID) < 0 { |
299 | 0 | return -1; |
300 | 1 | } |
301 | 1 | return 0; |
302 | 2 | } |
303 | | |
304 | | /// Get the DNS response answer name and index i. |
305 | 0 | pub(crate) unsafe extern "C" fn dns_tx_get_query_name( |
306 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, flags: u8, i: u32, buf: *mut *const u8, |
307 | 0 | len: *mut u32, |
308 | 0 | ) -> bool { |
309 | 0 | let tx = cast_pointer!(tx, DNSTransaction); |
310 | 0 | let queries = if flags & Direction::ToClient as u8 != 0 { |
311 | 0 | tx.response.as_ref().map(|response| &response.queries) |
312 | | } else { |
313 | 0 | tx.request.as_ref().map(|request| &request.queries) |
314 | | }; |
315 | 0 | let index = i as usize; |
316 | | |
317 | 0 | if let Some(queries) = queries { |
318 | 0 | if let Some(query) = queries.get(index) { |
319 | 0 | if !query.name.value.is_empty() { |
320 | 0 | *buf = query.name.value.as_ptr(); |
321 | 0 | *len = query.name.value.len() as u32; |
322 | 0 | return true; |
323 | 0 | } |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | 0 | false |
328 | 0 | } |
329 | | |
330 | 0 | unsafe extern "C" fn dns_tx_get_query( |
331 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, _flags: u8, i: u32, buf: *mut *const u8, |
332 | 0 | len: *mut u32, |
333 | 0 | ) -> bool { |
334 | 0 | return dns_tx_get_query_name(_de, tx, Direction::ToServer as u8, i, buf, len); |
335 | 0 | } |
336 | | |
337 | 16.8k | unsafe extern "C" fn dns_detect_query_setup( |
338 | 16.8k | de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char, |
339 | 16.8k | ) -> c_int { |
340 | 16.8k | if SCDetectSignatureSetAppProto(s, ALPROTO_DNS) != 0 { |
341 | 63 | return -1; |
342 | 16.7k | } |
343 | 16.7k | if SCDetectBufferSetActiveList(de, s, G_DNS_QUERY_BUFFER_ID) < 0 { |
344 | 0 | return -1; |
345 | 16.7k | } |
346 | 16.7k | return 0; |
347 | 16.8k | } |
348 | | |
349 | | #[no_mangle] |
350 | 39 | pub unsafe extern "C" fn SCDetectDNSRegister() { |
351 | 39 | let kw = SigTableElmtStickyBuffer { |
352 | 39 | name: String::from("dns.answer.name"), |
353 | 39 | desc: String::from("DNS answer name sticky buffer"), |
354 | 39 | url: String::from("/rules/dns-keywords.html#dns-answer-name"), |
355 | 39 | setup: dns_detect_answer_name_setup, |
356 | 39 | }; |
357 | 39 | let _g_dns_answer_name_kw_id = helper_keyword_register_sticky_buffer(&kw); |
358 | 39 | G_DNS_ANSWER_NAME_BUFFER_ID = SCDetectHelperMultiBufferProgressMpmRegister( |
359 | 39 | b"dns.answer.name\0".as_ptr() as *const libc::c_char, |
360 | 39 | b"dns answer name\0".as_ptr() as *const libc::c_char, |
361 | 39 | ALPROTO_DNS, |
362 | 39 | STREAM_TOSERVER | STREAM_TOCLIENT, |
363 | 39 | /* Register also in the TO_SERVER direction, even though this is not |
364 | 39 | normal, it could be provided as part of a request. */ |
365 | 39 | Some(dns_tx_get_answer_name), |
366 | 39 | 1, // response complete |
367 | 39 | ); |
368 | 39 | let kw = SCSigTableAppLiteElmt { |
369 | 39 | name: b"dns.opcode\0".as_ptr() as *const libc::c_char, |
370 | 39 | desc: b"Match the DNS header opcode flag.\0".as_ptr() as *const libc::c_char, |
371 | 39 | url: b"rules/dns-keywords.html#dns-opcode\0".as_ptr() as *const libc::c_char, |
372 | 39 | AppLayerTxMatch: Some(dns_opcode_match), |
373 | 39 | Setup: Some(dns_opcode_setup), |
374 | 39 | Free: Some(dns_opcode_free), |
375 | 39 | flags: 0, |
376 | 39 | }; |
377 | 39 | G_DNS_OPCODE_KW_ID = SCDetectHelperKeywordRegister(&kw); |
378 | 39 | G_DNS_OPCODE_BUFFER_ID = SCDetectHelperBufferRegister( |
379 | 39 | b"dns.opcode\0".as_ptr() as *const libc::c_char, |
380 | 39 | ALPROTO_DNS, |
381 | 39 | STREAM_TOSERVER | STREAM_TOCLIENT, |
382 | 39 | ); |
383 | 39 | let kw = SigTableElmtStickyBuffer { |
384 | 39 | name: String::from("dns.query.name"), |
385 | 39 | desc: String::from("DNS query name sticky buffer"), |
386 | 39 | url: String::from("/rules/dns-keywords.html#dns-query-name"), |
387 | 39 | setup: dns_detect_query_name_setup, |
388 | 39 | }; |
389 | 39 | let _g_dns_query_name_kw_id = helper_keyword_register_sticky_buffer(&kw); |
390 | 39 | G_DNS_QUERY_NAME_BUFFER_ID = SCDetectHelperMultiBufferProgressMpmRegister( |
391 | 39 | b"dns.query.name\0".as_ptr() as *const libc::c_char, |
392 | 39 | b"dns query name\0".as_ptr() as *const libc::c_char, |
393 | 39 | ALPROTO_DNS, |
394 | 39 | STREAM_TOSERVER | STREAM_TOCLIENT, |
395 | 39 | /* Register in both directions as the query is usually echoed back |
396 | 39 | in the response. */ |
397 | 39 | Some(dns_tx_get_query_name), |
398 | 39 | 1, // request or response complete |
399 | 39 | ); |
400 | 39 | let kw = SCSigTableAppLiteElmt { |
401 | 39 | name: b"dns.rcode\0".as_ptr() as *const libc::c_char, |
402 | 39 | desc: b"Match the DNS header rcode flag.\0".as_ptr() as *const libc::c_char, |
403 | 39 | url: b"rules/dns-keywords.html#dns-rcode\0".as_ptr() as *const libc::c_char, |
404 | 39 | AppLayerTxMatch: Some(dns_rcode_match), |
405 | 39 | Setup: Some(dns_rcode_setup), |
406 | 39 | Free: Some(dns_rcode_free), |
407 | 39 | flags: 0, |
408 | 39 | }; |
409 | 39 | G_DNS_RCODE_KW_ID = SCDetectHelperKeywordRegister(&kw); |
410 | 39 | G_DNS_RCODE_BUFFER_ID = SCDetectHelperBufferRegister( |
411 | 39 | b"dns.rcode\0".as_ptr() as *const libc::c_char, |
412 | 39 | ALPROTO_DNS, |
413 | 39 | STREAM_TOSERVER | STREAM_TOCLIENT, |
414 | 39 | ); |
415 | 39 | let kw = SCSigTableAppLiteElmt { |
416 | 39 | name: b"dns.rrtype\0".as_ptr() as *const libc::c_char, |
417 | 39 | desc: b"Match the DNS rrtype in message body.\0".as_ptr() as *const libc::c_char, |
418 | 39 | url: b"rules/dns-keywords.html#dns-rrtype\0".as_ptr() as *const libc::c_char, |
419 | 39 | AppLayerTxMatch: Some(dns_rrtype_match), |
420 | 39 | Setup: Some(dns_rrtype_setup), |
421 | 39 | Free: Some(dns_rrtype_free), |
422 | 39 | flags: 0, |
423 | 39 | }; |
424 | 39 | G_DNS_RRTYPE_KW_ID = SCDetectHelperKeywordRegister(&kw); |
425 | 39 | G_DNS_RRTYPE_BUFFER_ID = SCDetectHelperBufferRegister( |
426 | 39 | b"dns.rrtype\0".as_ptr() as *const libc::c_char, |
427 | 39 | ALPROTO_DNS, |
428 | 39 | STREAM_TOSERVER | STREAM_TOCLIENT, |
429 | 39 | ); |
430 | 39 | let kw = SigTableElmtStickyBuffer { |
431 | 39 | name: String::from("dns.query"), |
432 | 39 | desc: String::from("sticky buffer to match DNS query-buffer"), |
433 | 39 | url: String::from("/rules/dns-keywords.html#dns-query"), |
434 | 39 | setup: dns_detect_query_setup, |
435 | 39 | }; |
436 | 39 | let g_dns_query_name_kw_id = helper_keyword_register_sticky_buffer(&kw); |
437 | 39 | SCDetectHelperKeywordAliasRegister( |
438 | 39 | g_dns_query_name_kw_id, |
439 | 39 | b"dns_query\0".as_ptr() as *const libc::c_char, |
440 | | ); |
441 | 39 | G_DNS_QUERY_BUFFER_ID = SCDetectHelperMultiBufferProgressMpmRegister( |
442 | 39 | b"dns_query\0".as_ptr() as *const libc::c_char, |
443 | 39 | b"dns request query\0".as_ptr() as *const libc::c_char, |
444 | 39 | ALPROTO_DNS, |
445 | 39 | STREAM_TOSERVER, |
446 | 39 | Some(dns_tx_get_query), // reuse, will be called only toserver |
447 | 39 | 1, // request complete |
448 | 39 | ); |
449 | 39 | } |
450 | | |
451 | | #[cfg(test)] |
452 | | mod test { |
453 | | use super::*; |
454 | | use crate::detect::uint::{detect_parse_uint, DetectUintMode}; |
455 | | |
456 | | #[test] |
457 | | fn parse_opcode_good() { |
458 | | assert_eq!( |
459 | | detect_parse_uint::<u8>("1").unwrap().1, |
460 | | DetectUintData { |
461 | | mode: DetectUintMode::DetectUintModeEqual, |
462 | | arg1: 1, |
463 | | arg2: 0, |
464 | | } |
465 | | ); |
466 | | assert_eq!( |
467 | | detect_parse_uint::<u8>("123").unwrap().1, |
468 | | DetectUintData { |
469 | | mode: DetectUintMode::DetectUintModeEqual, |
470 | | arg1: 123, |
471 | | arg2: 0, |
472 | | } |
473 | | ); |
474 | | assert_eq!( |
475 | | detect_parse_uint::<u8>("!123").unwrap().1, |
476 | | DetectUintData { |
477 | | mode: DetectUintMode::DetectUintModeNe, |
478 | | arg1: 123, |
479 | | arg2: 0, |
480 | | } |
481 | | ); |
482 | | assert!(detect_parse_uint::<u8>("").is_err()); |
483 | | assert!(detect_parse_uint::<u8>("!").is_err()); |
484 | | assert!(detect_parse_uint::<u8>("! ").is_err()); |
485 | | assert!(detect_parse_uint::<u8>("!asdf").is_err()); |
486 | | } |
487 | | |
488 | | #[test] |
489 | | fn test_match_opcode() { |
490 | | assert!(detect_match_uint( |
491 | | &DetectUintData { |
492 | | mode: DetectUintMode::DetectUintModeEqual, |
493 | | arg1: 0, |
494 | | arg2: 0, |
495 | | }, |
496 | | 0b0000_0000_0000_0000, |
497 | | )); |
498 | | |
499 | | assert!(!detect_match_uint( |
500 | | &DetectUintData { |
501 | | mode: DetectUintMode::DetectUintModeNe, |
502 | | arg1: 0, |
503 | | arg2: 0, |
504 | | }, |
505 | | 0b0000_0000_0000_0000, |
506 | | )); |
507 | | |
508 | | assert!(detect_match_uint( |
509 | | &DetectUintData { |
510 | | mode: DetectUintMode::DetectUintModeEqual, |
511 | | arg1: 4, |
512 | | arg2: 0, |
513 | | }, |
514 | | ((0b0010_0000_0000_0000 >> 11) & 0xf) as u8, |
515 | | )); |
516 | | |
517 | | assert!(!detect_match_uint( |
518 | | &DetectUintData { |
519 | | mode: DetectUintMode::DetectUintModeNe, |
520 | | arg1: 4, |
521 | | arg2: 0, |
522 | | }, |
523 | | ((0b0010_0000_0000_0000 >> 11) & 0xf) as u8, |
524 | | )); |
525 | | } |
526 | | |
527 | | #[test] |
528 | | fn parse_rcode_good() { |
529 | | assert_eq!( |
530 | | detect_parse_uint_enum::<u16, DNSRcode>("1").unwrap(), |
531 | | DetectUintData { |
532 | | mode: DetectUintMode::DetectUintModeEqual, |
533 | | arg1: 1, |
534 | | arg2: 0, |
535 | | } |
536 | | ); |
537 | | assert_eq!( |
538 | | detect_parse_uint_enum::<u16, DNSRcode>("123").unwrap(), |
539 | | DetectUintData { |
540 | | mode: DetectUintMode::DetectUintModeEqual, |
541 | | arg1: 123, |
542 | | arg2: 0, |
543 | | } |
544 | | ); |
545 | | assert_eq!( |
546 | | detect_parse_uint_enum::<u16, DNSRcode>("!123").unwrap(), |
547 | | DetectUintData { |
548 | | mode: DetectUintMode::DetectUintModeNe, |
549 | | arg1: 123, |
550 | | arg2: 0, |
551 | | } |
552 | | ); |
553 | | assert_eq!( |
554 | | detect_parse_uint_enum::<u16, DNSRcode>("7-15").unwrap(), |
555 | | DetectUintData { |
556 | | mode: DetectUintMode::DetectUintModeRange, |
557 | | arg1: 7, |
558 | | arg2: 15, |
559 | | } |
560 | | ); |
561 | | assert_eq!( |
562 | | detect_parse_uint_enum::<u16, DNSRcode>("nxdomain").unwrap(), |
563 | | DetectUintData { |
564 | | mode: DetectUintMode::DetectUintModeEqual, |
565 | | arg1: DNSRcode::NXDOMAIN as u16, |
566 | | arg2: 0, |
567 | | } |
568 | | ); |
569 | | assert!(detect_parse_uint_enum::<u16, DNSRcode>("").is_none()); |
570 | | assert!(detect_parse_uint_enum::<u16, DNSRcode>("!").is_none()); |
571 | | assert!(detect_parse_uint_enum::<u16, DNSRcode>("! ").is_none()); |
572 | | assert!(detect_parse_uint_enum::<u16, DNSRcode>("!asdf").is_none()); |
573 | | } |
574 | | |
575 | | #[test] |
576 | | fn test_match_rcode() { |
577 | | assert!(detect_match_uint( |
578 | | &DetectUintData { |
579 | | mode: DetectUintMode::DetectUintModeEqual, |
580 | | arg1: 0, |
581 | | arg2: 0, |
582 | | }, |
583 | | 0b0000_0000_0000_0000, |
584 | | )); |
585 | | |
586 | | assert!(!detect_match_uint( |
587 | | &DetectUintData { |
588 | | mode: DetectUintMode::DetectUintModeNe, |
589 | | arg1: 0, |
590 | | arg2: 0, |
591 | | }, |
592 | | 0b0000_0000_0000_0000, |
593 | | )); |
594 | | |
595 | | assert!(detect_match_uint( |
596 | | &DetectUintData { |
597 | | mode: DetectUintMode::DetectUintModeEqual, |
598 | | arg1: 4, |
599 | | arg2: 0, |
600 | | }, |
601 | | 4u8, |
602 | | )); |
603 | | |
604 | | assert!(!detect_match_uint( |
605 | | &DetectUintData { |
606 | | mode: DetectUintMode::DetectUintModeNe, |
607 | | arg1: 4, |
608 | | arg2: 0, |
609 | | }, |
610 | | 4u8, |
611 | | )); |
612 | | } |
613 | | |
614 | | #[test] |
615 | | fn parse_rrtype_good() { |
616 | | assert_eq!( |
617 | | detect_parse_uint_enum::<u16, DNSRecordType>("1").unwrap(), |
618 | | DetectUintData { |
619 | | mode: DetectUintMode::DetectUintModeEqual, |
620 | | arg1: 1, |
621 | | arg2: 0, |
622 | | } |
623 | | ); |
624 | | assert_eq!( |
625 | | detect_parse_uint_enum::<u16, DNSRecordType>("123").unwrap(), |
626 | | DetectUintData { |
627 | | mode: DetectUintMode::DetectUintModeEqual, |
628 | | arg1: 123, |
629 | | arg2: 0, |
630 | | } |
631 | | ); |
632 | | assert_eq!( |
633 | | detect_parse_uint_enum::<u16, DNSRecordType>("!123").unwrap(), |
634 | | DetectUintData { |
635 | | mode: DetectUintMode::DetectUintModeNe, |
636 | | arg1: 123, |
637 | | arg2: 0, |
638 | | } |
639 | | ); |
640 | | assert_eq!( |
641 | | detect_parse_uint_enum::<u16, DNSRecordType>("7-15").unwrap(), |
642 | | DetectUintData { |
643 | | mode: DetectUintMode::DetectUintModeRange, |
644 | | arg1: 7, |
645 | | arg2: 15, |
646 | | } |
647 | | ); |
648 | | assert_eq!( |
649 | | detect_parse_uint_enum::<u16, DNSRecordType>("a").unwrap(), |
650 | | DetectUintData { |
651 | | mode: DetectUintMode::DetectUintModeEqual, |
652 | | arg1: DNSRecordType::A as u16, |
653 | | arg2: 0, |
654 | | } |
655 | | ); |
656 | | assert!(detect_parse_uint_enum::<u16, DNSRecordType>("").is_none()); |
657 | | assert!(detect_parse_uint_enum::<u16, DNSRecordType>("!").is_none()); |
658 | | assert!(detect_parse_uint_enum::<u16, DNSRecordType>("! ").is_none()); |
659 | | assert!(detect_parse_uint_enum::<u16, DNSRecordType>("!asdf").is_none()); |
660 | | } |
661 | | |
662 | | #[test] |
663 | | fn test_match_rrtype() { |
664 | | assert!(detect_match_uint( |
665 | | &DetectUintData { |
666 | | mode: DetectUintMode::DetectUintModeEqual, |
667 | | arg1: 0, |
668 | | arg2: 0, |
669 | | }, |
670 | | 0b0000_0000_0000_0000, |
671 | | )); |
672 | | |
673 | | assert!(!detect_match_uint( |
674 | | &DetectUintData { |
675 | | mode: DetectUintMode::DetectUintModeNe, |
676 | | arg1: 0, |
677 | | arg2: 0, |
678 | | }, |
679 | | 0b0000_0000_0000_0000, |
680 | | )); |
681 | | |
682 | | assert!(detect_match_uint( |
683 | | &DetectUintData { |
684 | | mode: DetectUintMode::DetectUintModeEqual, |
685 | | arg1: 4, |
686 | | arg2: 0, |
687 | | }, |
688 | | 4u16, |
689 | | )); |
690 | | |
691 | | assert!(!detect_match_uint( |
692 | | &DetectUintData { |
693 | | mode: DetectUintMode::DetectUintModeNe, |
694 | | arg1: 4, |
695 | | arg2: 0, |
696 | | }, |
697 | | 4u16, |
698 | | )); |
699 | | } |
700 | | } |