/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 | | } |