Coverage Report

Created: 2026-09-28 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}