/src/suricata8/rust/htp/src/log.rs
Line | Count | Source |
1 | | use std::cell::RefCell; |
2 | | use std::collections::VecDeque; |
3 | | use std::rc::Rc; |
4 | | |
5 | | /// Different codes used for logging. |
6 | | #[repr(C)] |
7 | | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
8 | | pub enum HtpLogCode { |
9 | | /// Default |
10 | | UNKNOWN = 0, |
11 | | /// Gzip Decompression Failed |
12 | | GZIP_DECOMPRESSION_FAILED, |
13 | | /// Request field missing a colon. |
14 | | REQUEST_FIELD_MISSING_COLON, |
15 | | /// Response field missing a colon. |
16 | | RESPONSE_FIELD_MISSING_COLON, |
17 | | /// Request chunk length parsing failed. |
18 | | INVALID_REQUEST_CHUNK_LEN, |
19 | | /// Response chunked-length parsing failed. |
20 | | INVALID_RESPONSE_CHUNK_LEN, |
21 | | /// Response chunk exension. |
22 | | REQUEST_CHUNK_EXTENSION, |
23 | | /// Response chunk exension. |
24 | | RESPONSE_CHUNK_EXTENSION, |
25 | | /// Request has too many headers. |
26 | | REQUEST_TOO_MANY_HEADERS, |
27 | | /// Response has too many headers. |
28 | | RESPONSE_TOO_MANY_HEADERS, |
29 | | /// Request transfer-encoding invalid. |
30 | | INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST, |
31 | | /// Response transfer-encoding invalid. |
32 | | INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE, |
33 | | /// Request content-length parsing failed. |
34 | | INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST, |
35 | | /// Response content-length parsing failed. |
36 | | INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE, |
37 | | /// Request has a duplicate content-length field. |
38 | | DUPLICATE_CONTENT_LENGTH_FIELD_IN_REQUEST, |
39 | | /// Response has a duplicate content-length field. |
40 | | DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE, |
41 | | /// 100 Continue response status already seen. |
42 | | CONTINUE_ALREADY_SEEN, |
43 | | /// Unable to match response to a request. |
44 | | UNABLE_TO_MATCH_RESPONSE_TO_REQUEST, |
45 | | /// Request server port is invalid. |
46 | | INVALID_SERVER_PORT_IN_REQUEST, |
47 | | /// Authority port is invalid. |
48 | | INVALID_AUTHORITY_PORT, |
49 | | /// Request header name is incorrectly formed. |
50 | | REQUEST_HEADER_INVALID, |
51 | | /// Response header name is incorrectly formed. |
52 | | RESPONSE_HEADER_INVALID, |
53 | | /// Host header is missing. |
54 | | MISSING_HOST_HEADER, |
55 | | /// Host header is ambiguous. |
56 | | HOST_HEADER_AMBIGUOUS, |
57 | | /// Request has invalid line folding. |
58 | | INVALID_REQUEST_FIELD_FOLDING, |
59 | | /// Response has invalid line folding. |
60 | | INVALID_RESPONSE_FIELD_FOLDING, |
61 | | /// Request buffer field is over the limit. |
62 | | REQUEST_FIELD_TOO_LONG, |
63 | | /// Response buffer field is over the limit. |
64 | | RESPONSE_FIELD_TOO_LONG, |
65 | | /// Mismatch between request server port and tcp port. |
66 | | REQUEST_SERVER_PORT_TCP_PORT_MISMATCH, |
67 | | /// Uri hostname is invalid. |
68 | | URI_HOST_INVALID, |
69 | | /// Header hostname is invalid. |
70 | | HEADER_HOST_INVALID, |
71 | | /// Non compliant delimiter between method and URI in request line. |
72 | | METHOD_DELIM_NON_COMPLIANT, |
73 | | /// Parsed request-uri contains a non compliant delimiter. |
74 | | URI_DELIM_NON_COMPLIANT, |
75 | | /// Request line has leading whitespace. |
76 | | REQUEST_LINE_LEADING_WHITESPACE, |
77 | | /// Response content encoding lzma layers is greater than limit. |
78 | | RESPONSE_TOO_MANY_LZMA_LAYERS, |
79 | | /// Request content encoding lzma layers is greater than limit. |
80 | | REQUEST_TOO_MANY_LZMA_LAYERS, |
81 | | /// Too many request or response encoding layers |
82 | | TOO_MANY_ENCODING_LAYERS, |
83 | | /// Response header content-encoding header is invalid |
84 | | ABNORMAL_CE_HEADER, |
85 | | /// Request authorization header unrecognized |
86 | | AUTH_UNRECOGNIZED, |
87 | | /// Request header has been seen more than once. |
88 | | REQUEST_HEADER_REPETITION, |
89 | | /// response header has been seen more than once. |
90 | | RESPONSE_HEADER_REPETITION, |
91 | | /// Response content-type is multipart-byteranges (unsupported). |
92 | | RESPONSE_MULTIPART_BYTERANGES, |
93 | | /// Response transfer-encoding has an abnormal chunked value. |
94 | | RESPONSE_ABNORMAL_TRANSFER_ENCODING, |
95 | | /// Response chunked transfer-encoding on HTTP/0.9 or HTTP/1.0. |
96 | | RESPONSE_CHUNKED_OLD_PROTO, |
97 | | /// Response protocol invalid. |
98 | | RESPONSE_INVALID_PROTOCOL, |
99 | | /// Response status invalid. |
100 | | RESPONSE_INVALID_STATUS, |
101 | | /// Response line is incomplete. |
102 | | REQUEST_LINE_INCOMPLETE, |
103 | | /// Request uri has double encoding. |
104 | | DOUBLE_ENCODED_URI, |
105 | | /// Request line is invalid. |
106 | | REQUEST_LINE_INVALID, |
107 | | /// Unexpected request body present. |
108 | | REQUEST_BODY_UNEXPECTED, |
109 | | /// Reached LZMA memory limit. |
110 | | LZMA_MEMLIMIT_REACHED, |
111 | | /// Reached configured time limit for decompression or reached bomb limit. |
112 | | COMPRESSION_BOMB, |
113 | | /// Reached configured time limit for decompression or reached bomb limit. |
114 | | COMPRESSION_BOMB_LIMIT_REACHED, |
115 | | /// Unexpected response body present. |
116 | | RESPONSE_BODY_UNEXPECTED, |
117 | | /// Content-length parsing contains extra leading characters. |
118 | | CONTENT_LENGTH_EXTRA_DATA_START, |
119 | | /// Content-length parsing contains extra trailing characters |
120 | | CONTENT_LENGTH_EXTRA_DATA_END, |
121 | | /// 101 Switching Protocol seen with a content-length. |
122 | | SWITCHING_PROTO_WITH_CONTENT_LENGTH, |
123 | | /// End of line is deformed. |
124 | | DEFORMED_EOL, |
125 | | /// Parsing error encountered in request or response. |
126 | | PARSER_STATE_ERROR, |
127 | | /// Missing outbound transaction while state is not idle. |
128 | | MISSING_OUTBOUND_TRANSACTION_DATA, |
129 | | /// Missing inbound transaction while state is not idle. |
130 | | MISSING_INBOUND_TRANSACTION_DATA, |
131 | | /// Supplied data chunk has a length of zero. |
132 | | ZERO_LENGTH_DATA_CHUNKS, |
133 | | /// Request Line method is unknown. |
134 | | REQUEST_LINE_UNKNOWN_METHOD, |
135 | | /// Request line method is unknown and no protocol information was found. |
136 | | REQUEST_LINE_UNKNOWN_METHOD_NO_PROTOCOL, |
137 | | /// Request line method is unknown and protocol is invalid. |
138 | | REQUEST_LINE_UNKNOWN_METHOD_INVALID_PROTOCOL, |
139 | | /// Request line protocol information was not found. |
140 | | REQUEST_LINE_NO_PROTOCOL, |
141 | | /// Response line protocol is invalid. |
142 | | RESPONSE_LINE_INVALID_PROTOCOL, |
143 | | /// Response line status number is out of range. |
144 | | RESPONSE_LINE_INVALID_RESPONSE_STATUS, |
145 | | /// Response parsing progress is at an invalid state. |
146 | | RESPONSE_BODY_INTERNAL_ERROR, |
147 | | /// Request body data callback produced a error. |
148 | | REQUEST_BODY_DATA_CALLBACK_ERROR, |
149 | | /// Response header name is empty. |
150 | | RESPONSE_INVALID_EMPTY_NAME, |
151 | | /// Request header name is empty. |
152 | | REQUEST_INVALID_EMPTY_NAME, |
153 | | /// Response header name has extra whitespace after name. |
154 | | RESPONSE_INVALID_LWS_AFTER_NAME, |
155 | | /// Response header name is not a valid token. |
156 | | RESPONSE_HEADER_NAME_NOT_TOKEN, |
157 | | /// Request header name has extra whitespace after name. |
158 | | REQUEST_INVALID_LWS_AFTER_NAME, |
159 | | /// LZMA decompression is disabled. |
160 | | LZMA_DECOMPRESSION_DISABLED, |
161 | | /// Tried to open a connection that is already open. |
162 | | CONNECTION_ALREADY_OPEN, |
163 | | /// Protocol parsing detected leading or trailing data. |
164 | | PROTOCOL_CONTAINS_EXTRA_DATA, |
165 | | /// Invalid gap detected. |
166 | | INVALID_GAP, |
167 | | /// Compression bomb due to double lzma encoding. |
168 | | COMPRESSION_BOMB_DOUBLE_LZMA, |
169 | | /// Invalid content-encoding detected. |
170 | | INVALID_CONTENT_ENCODING, |
171 | | /// Error retrieving a log message's code |
172 | | ERROR, |
173 | | } |
174 | | |
175 | | /// Enumerates all log levels. |
176 | | #[repr(C)] |
177 | | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] |
178 | | pub(crate) enum HtpLogLevel { |
179 | | /// Designates fatal error. |
180 | | Error, |
181 | | /// Designates hazardous situations. |
182 | | Warning, |
183 | | /// Default log level value. |
184 | | Notice, |
185 | | /// Designates useful information, |
186 | | Info, |
187 | | } |
188 | | #[derive(Clone)] |
189 | | /// Logger struct |
190 | | pub(crate) struct Logger { |
191 | | /// The sender half of a logging channel |
192 | | pub(crate) sender: Rc<RefCell<VecDeque<Log>>>, |
193 | | /// Log level used when deciding whether to store or |
194 | | /// ignore the messages issued by the parser. |
195 | | pub(crate) level: HtpLogLevel, |
196 | | } |
197 | | |
198 | | impl Logger { |
199 | | /// Returns a new logger instance |
200 | 17.7k | pub(crate) fn new(sender: &Rc<RefCell<VecDeque<Log>>>) -> Logger { |
201 | 17.7k | Self { |
202 | 17.7k | sender: sender.clone(), |
203 | 17.7k | level: HtpLogLevel::Notice, |
204 | 17.7k | } |
205 | 17.7k | } |
206 | | /// Logs a message to the logger channel. |
207 | 2.92M | pub(crate) fn log( |
208 | 2.92M | &mut self, _file: &str, _line: u32, level: HtpLogLevel, code: HtpLogCode, msg: String, |
209 | 2.92M | ) { |
210 | | // Ignore messages below our log level. |
211 | 2.92M | if level <= self.level { |
212 | 2.92M | let mut sender = self.sender.borrow_mut(); |
213 | 2.92M | sender.push_back(Log::new(Message::new(code, msg))); |
214 | 2.92M | } |
215 | 2.92M | } |
216 | | } |
217 | | |
218 | | #[derive(Clone)] |
219 | | /// Represents a single Message entry for a log |
220 | | pub(crate) struct Message { |
221 | | /// Log message string. |
222 | | pub(crate) msg: String, |
223 | | //level: HtpLogLevel, |
224 | | /// Message code. |
225 | | pub(crate) code: HtpLogCode, |
226 | | //pub(crate) file: String, |
227 | | //line: u32, |
228 | | } |
229 | | |
230 | | impl Message { |
231 | | /// Returns a new Message instance |
232 | 2.92M | pub(crate) fn new(code: HtpLogCode, msg: String) -> Message { |
233 | 2.92M | Self { code, msg } |
234 | 2.92M | } |
235 | | } |
236 | | |
237 | | /// Represents a single log entry. |
238 | | #[derive(Clone)] |
239 | | pub struct Log { |
240 | | /// Log message. |
241 | | pub(crate) msg: Message, |
242 | | } |
243 | | |
244 | | impl Log { |
245 | | /// Returns a new Log instance. |
246 | 2.92M | pub(crate) fn new(msg: Message) -> Log { |
247 | 2.92M | Self { msg } |
248 | 2.92M | } |
249 | | } |
250 | | |
251 | | /// Logs a message at the given level. |
252 | | #[macro_export] |
253 | | macro_rules! htp_log { |
254 | | ($logger:expr, $level:expr, $code:expr, $msg:expr) => {{ |
255 | | use $crate::log::{HtpLogCode, HtpLogLevel}; |
256 | | $logger.log(file!(), line!(), $level, $code, $msg.to_string()); |
257 | | }}; |
258 | | } |
259 | | |
260 | | /// Logs a message at the info level. |
261 | | #[macro_export] |
262 | | macro_rules! htp_info { |
263 | | ($logger:expr, $code:expr, $msg:expr) => { |
264 | | htp_log!($logger, HtpLogLevel::Info, $code, $msg); |
265 | | }; |
266 | | } |
267 | | |
268 | | /// Logs a message at the warning level. |
269 | | #[macro_export] |
270 | | macro_rules! htp_warn { |
271 | | ($logger:expr, $code:expr, $msg:expr) => { |
272 | | htp_log!($logger, HtpLogLevel::Warning, $code, $msg); |
273 | | }; |
274 | | } |
275 | | |
276 | | /// Logs a message at the error level. |
277 | | #[macro_export] |
278 | | macro_rules! htp_error { |
279 | | ($logger:expr, $code:expr, $msg:expr) => { |
280 | | htp_log!($logger, HtpLogLevel::Error, $code, $msg); |
281 | | }; |
282 | | } |
283 | | |
284 | | /// Logs a message at the warning level, ensuring that it ones logs the message once. |
285 | | #[macro_export] |
286 | | macro_rules! htp_warn_once { |
287 | | ($logger:expr, $code:expr, $msg:expr, $tx_flags:expr, $flags:expr, $flag:expr) => { |
288 | | // Log only once per transaction. |
289 | | if !$tx_flags.is_set($flag) { |
290 | | htp_warn!($logger, $code, $msg); |
291 | | } |
292 | | $tx_flags.set($flag); |
293 | | $flags.set($flag); |
294 | | }; |
295 | | } |