Coverage Report

Created: 2026-08-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/dns/detect.rs
Line
Count
Source
1
/* Copyright (C) 2019 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::DNSTransaction;
19
use crate::core::*;
20
use std::ffi::CStr;
21
use std::os::raw::{c_char, c_void};
22
23
#[derive(Debug, PartialEq, Eq)]
24
pub struct DetectDnsOpcode {
25
    negate: bool,
26
    opcode: u8,
27
}
28
29
/// Parse a DNS opcode argument returning the code and if it is to be
30
/// negated or not.
31
///
32
/// For now only an indication that an error occurred is returned, not
33
/// the details of the error.
34
156
fn parse_opcode(opcode: &str) -> Result<DetectDnsOpcode, ()> {
35
156
    let mut negated = false;
36
291
    for (i, c) in opcode.char_indices() {
37
291
        match c {
38
            ' ' | '\t' => {
39
0
                continue;
40
            }
41
135
            '!' => {
42
135
                negated = true;
43
135
            }
44
            _ => {
45
156
                let code: u8 = opcode[i..].parse().map_err(|_| ())?;
46
136
                return Ok(DetectDnsOpcode {
47
136
                    negate: negated,
48
136
                    opcode: code,
49
136
                });
50
            }
51
        }
52
    }
53
0
    Err(())
54
156
}
55
56
/// Perform the DNS opcode match.
57
///
58
/// 1 will be returned on match, otherwise 0 will be returned.
59
#[no_mangle]
60
3
pub extern "C" fn rs_dns_opcode_match(
61
3
    tx: &mut DNSTransaction, detect: &mut DetectDnsOpcode, flags: u8,
62
3
) -> u8 {
63
3
    let header_flags = if flags & Direction::ToServer as u8 != 0 {
64
3
        if let Some(request) = &tx.request {
65
3
            request.header.flags
66
        } else {
67
0
            return 0;
68
        }
69
0
    } else if flags & Direction::ToClient as u8 != 0 {
70
0
        if let Some(response) = &tx.response {
71
0
            response.header.flags
72
        } else {
73
0
            return 0;
74
        }
75
    } else {
76
        // Not to server or to client??
77
0
        return 0;
78
    };
79
80
3
    match_opcode(detect, header_flags).into()
81
3
}
82
83
3
fn match_opcode(detect: &DetectDnsOpcode, flags: u16) -> bool {
84
3
    let opcode = ((flags >> 11) & 0xf) as u8;
85
3
    if detect.negate {
86
3
        detect.opcode != opcode
87
    } else {
88
0
        detect.opcode == opcode
89
    }
90
3
}
91
92
#[no_mangle]
93
156
pub unsafe extern "C" fn rs_detect_dns_opcode_parse(carg: *const c_char) -> *mut c_void {
94
156
    if carg.is_null() {
95
0
        return std::ptr::null_mut();
96
156
    }
97
156
    let arg = match CStr::from_ptr(carg).to_str() {
98
156
        Ok(arg) => arg,
99
        _ => {
100
0
            return std::ptr::null_mut();
101
        }
102
    };
103
104
156
    match parse_opcode(arg) {
105
136
        Ok(detect) => Box::into_raw(Box::new(detect)) as *mut _,
106
20
        Err(_) => std::ptr::null_mut(),
107
    }
108
156
}
109
110
#[no_mangle]
111
136
pub unsafe extern "C" fn rs_dns_detect_opcode_free(ptr: *mut c_void) {
112
136
    if !ptr.is_null() {
113
136
        std::mem::drop(Box::from_raw(ptr as *mut DetectDnsOpcode));
114
136
    }
115
136
}
116
117
#[cfg(test)]
118
mod test {
119
    use super::*;
120
121
    #[test]
122
    fn parse_opcode_good() {
123
        assert_eq!(
124
            parse_opcode("1"),
125
            Ok(DetectDnsOpcode {
126
                negate: false,
127
                opcode: 1
128
            })
129
        );
130
        assert_eq!(
131
            parse_opcode("123"),
132
            Ok(DetectDnsOpcode {
133
                negate: false,
134
                opcode: 123
135
            })
136
        );
137
        assert_eq!(
138
            parse_opcode("!123"),
139
            Ok(DetectDnsOpcode {
140
                negate: true,
141
                opcode: 123
142
            })
143
        );
144
        assert_eq!(
145
            parse_opcode("!123"),
146
            Ok(DetectDnsOpcode {
147
                negate: true,
148
                opcode: 123
149
            })
150
        );
151
        assert_eq!(parse_opcode(""), Err(()));
152
        assert_eq!(parse_opcode("!"), Err(()));
153
        assert_eq!(parse_opcode("!   "), Err(()));
154
        assert_eq!(parse_opcode("!asdf"), Err(()));
155
    }
156
157
    #[test]
158
    fn test_match_opcode() {
159
        assert!(
160
            match_opcode(
161
                &DetectDnsOpcode {
162
                    negate: false,
163
                    opcode: 0,
164
                },
165
                0b0000_0000_0000_0000,
166
            )
167
        );
168
169
        assert!(
170
            !match_opcode(
171
                &DetectDnsOpcode {
172
                    negate: true,
173
                    opcode: 0,
174
                },
175
                0b0000_0000_0000_0000,
176
            )
177
        );
178
179
        assert!(
180
            match_opcode(
181
                &DetectDnsOpcode {
182
                    negate: false,
183
                    opcode: 4,
184
                },
185
                0b0010_0000_0000_0000,
186
            )
187
        );
188
189
        assert!(
190
            !match_opcode(
191
                &DetectDnsOpcode {
192
                    negate: true,
193
                    opcode: 4,
194
                },
195
                0b0010_0000_0000_0000,
196
            )
197
        );
198
    }
199
}