Coverage Report

Created: 2026-11-12 06:59

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