• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23255204113

18 Mar 2026 04:23PM UTC coverage: 20.951% (-58.4%) from 79.315%
23255204113

Pull #15058

github

web-flow
Merge 2aa43ba22 into 6587e363a
Pull Request #15058: Dataset match subdomain/v1

5 of 53 new or added lines in 1 file covered. (9.43%)

113965 existing lines in 851 files now uncovered.

45775 of 218488 relevant lines covered (20.95%)

1848014.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.54
/rust/src/pgsql/pgsql.rs
1
/* Copyright (C) 2022-2025 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
// Author: Juliana Fajardini <jufajardini@oisf.net>
19

20
//! PostgreSQL parser
21

22
use super::parser::PgsqlParseError;
23
use super::parser::{self, ConsolidatedDataRowPacket, PgsqlBEMessage, PgsqlFEMessage};
24
use crate::applayer::*;
25
use crate::conf::*;
26
use crate::core::{ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_TCP, *};
27
use crate::direction::Direction;
28
use crate::flow::Flow;
29
use nom8::{Err, IResult};
30
use std;
31
use std::collections::VecDeque;
32
use std::ffi::CString;
33
use suricata_sys::sys::{
34
    AppLayerParserState, AppProto, SCAppLayerParserConfParserEnabled,
35
    SCAppLayerParserSetStreamDepth, SCAppLayerParserStateIssetFlag,
36
    SCAppLayerProtoDetectConfProtoDetectionEnabled, SCAppLayerRequestProtocolTLSUpgrade,
37
};
38

39
const PGSQL_CONFIG_DEFAULT_STREAM_DEPTH: u32 = 0;
40

41
pub(crate) static mut ALPROTO_PGSQL: AppProto = ALPROTO_UNKNOWN;
42

43
static mut PGSQL_MAX_TX: usize = 1024;
44

UNCOV
45
#[derive(AppLayerEvent, Debug, PartialEq, Eq)]
×
46
enum PgsqlEvent {
47
    InvalidLength,     // Can't parse the length field
48
    MalformedRequest,  // Enough data, but unexpected request format
49
    MalformedResponse, // Enough data, but unexpected response format
50
    TooManyTransactions,
51
}
52

53
#[repr(u8)]
54
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
55
pub(crate) enum PgsqlTxProgress {
56
    Init = 0,
57
    Received,
58
    Done,
59
    FlushedOut,
60
}
61

62
#[derive(Debug)]
63
pub(crate) struct PgsqlTransaction {
64
    pub tx_id: u64,
65
    pub tx_req_state: PgsqlTxProgress,
66
    pub tx_res_state: PgsqlTxProgress,
67
    pub requests: Vec<PgsqlFEMessage>,
68
    pub responses: Vec<PgsqlBEMessage>,
69

70
    pub data_row_cnt: u64,
71
    pub data_size: u64,
72

73
    tx_data: AppLayerTxData,
74
}
75

76
impl Transaction for PgsqlTransaction {
UNCOV
77
    fn id(&self) -> u64 {
×
UNCOV
78
        self.tx_id
×
UNCOV
79
    }
×
80
}
81

82
impl Default for PgsqlTransaction {
83
    fn default() -> Self {
×
84
        Self::new()
×
85
    }
×
86
}
87

88
impl PgsqlTransaction {
UNCOV
89
    fn new() -> Self {
×
UNCOV
90
        Self {
×
UNCOV
91
            tx_id: 0,
×
UNCOV
92
            tx_req_state: PgsqlTxProgress::Init,
×
UNCOV
93
            tx_res_state: PgsqlTxProgress::Init,
×
UNCOV
94
            requests: Vec::<PgsqlFEMessage>::new(),
×
UNCOV
95
            responses: Vec::<PgsqlBEMessage>::new(),
×
UNCOV
96
            data_row_cnt: 0,
×
UNCOV
97
            data_size: 0,
×
UNCOV
98
            tx_data: AppLayerTxData::new(),
×
UNCOV
99
        }
×
UNCOV
100
    }
×
101

UNCOV
102
    fn incr_row_cnt(&mut self) {
×
UNCOV
103
        self.data_row_cnt = self.data_row_cnt.saturating_add(1);
×
UNCOV
104
    }
×
105

UNCOV
106
    fn get_row_cnt(&self) -> u64 {
×
UNCOV
107
        self.data_row_cnt
×
UNCOV
108
    }
×
109

UNCOV
110
    fn sum_data_size(&mut self, row_size: u64) {
×
UNCOV
111
        self.data_size += row_size;
×
UNCOV
112
    }
×
113
}
114

115
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116
enum PgsqlStateProgress {
117
    IdleState,
118
    // Related to Frontend-received messages //
119
    SSLRequestReceived,
120
    StartupMessageReceived,
121
    SASLInitialResponseReceived,
122
    SASLResponseReceived,
123
    PasswordMessageReceived,
124
    SimpleQueryReceived,
125
    CancelRequestReceived,
126
    ConnectionTerminated,
127
    // Related to Backend-received messages //
128
    CopyDoneReceived, // BE and FE
129
    CopyFailReceived, // BE and FE
130
    CopyOutResponseReceived,
131
    CopyDataOutReceived,
132
    CopyInResponseReceived,
133
    FirstCopyDataInReceived,
134
    ConsolidatingCopyDataIn,
135
    SSLRejectedReceived,
136
    // SSPIAuthenticationReceived, // TODO implement
137
    SASLAuthenticationReceived,
138
    SASLAuthenticationContinueReceived,
139
    SASLAuthenticationFinalReceived,
140
    SimpleAuthenticationReceived,
141
    AuthenticationOkReceived,
142
    ParameterSetup,
143
    BackendKeyReceived,
144
    ReadyForQueryReceived,
145
    RowDescriptionReceived,
146
    DataRowReceived,
147
    CommandCompletedReceived,
148
    ErrorMessageReceived,
149
    #[cfg(test)]
150
    UnknownState,
151
    Finished,
152
}
153

154
#[derive(Debug)]
155
struct PgsqlState {
156
    state_data: AppLayerStateData,
157
    tx_id: u64,
158
    transactions: VecDeque<PgsqlTransaction>,
159
    request_gap: bool,
160
    response_gap: bool,
161
    backend_secret_key: u32,
162
    backend_pid: u32,
163
    state_progress: PgsqlStateProgress,
164
    tx_index_completed: usize,
165
}
166

167
impl State<PgsqlTransaction> for PgsqlState {
UNCOV
168
    fn get_transaction_count(&self) -> usize {
×
UNCOV
169
        self.transactions.len()
×
UNCOV
170
    }
×
171

UNCOV
172
    fn get_transaction_by_index(&self, index: usize) -> Option<&PgsqlTransaction> {
×
UNCOV
173
        self.transactions.get(index)
×
UNCOV
174
    }
×
175
}
176

177
impl Default for PgsqlState {
178
    fn default() -> Self {
×
179
        Self::new()
×
180
    }
×
181
}
182

183
impl PgsqlState {
UNCOV
184
    fn new() -> Self {
×
UNCOV
185
        Self {
×
UNCOV
186
            state_data: AppLayerStateData::default(),
×
UNCOV
187
            tx_id: 0,
×
UNCOV
188
            transactions: VecDeque::new(),
×
UNCOV
189
            request_gap: false,
×
UNCOV
190
            response_gap: false,
×
UNCOV
191
            backend_secret_key: 0,
×
UNCOV
192
            backend_pid: 0,
×
UNCOV
193
            state_progress: PgsqlStateProgress::IdleState,
×
UNCOV
194
            tx_index_completed: 0,
×
UNCOV
195
        }
×
UNCOV
196
    }
×
197

198
    // Free a transaction by ID.
UNCOV
199
    fn free_tx(&mut self, tx_id: u64) {
×
UNCOV
200
        let len = self.transactions.len();
×
UNCOV
201
        let mut found = false;
×
UNCOV
202
        let mut index = 0;
×
UNCOV
203
        for i in 0..len {
×
UNCOV
204
            let tx = &self.transactions[i];
×
UNCOV
205
            if tx.tx_id == tx_id + 1 {
×
UNCOV
206
                found = true;
×
UNCOV
207
                index = i;
×
UNCOV
208
                break;
×
209
            }
×
210
        }
UNCOV
211
        if found {
×
UNCOV
212
            self.tx_index_completed = 0;
×
UNCOV
213
            self.transactions.remove(index);
×
UNCOV
214
        }
×
UNCOV
215
    }
×
216

UNCOV
217
    fn get_tx(&mut self, tx_id: u64) -> Option<&PgsqlTransaction> {
×
UNCOV
218
        self.transactions.iter().find(|tx| tx.tx_id == tx_id + 1)
×
UNCOV
219
    }
×
220

UNCOV
221
    fn new_tx(&mut self) -> PgsqlTransaction {
×
UNCOV
222
        let mut tx = PgsqlTransaction::new();
×
UNCOV
223
        self.tx_id += 1;
×
UNCOV
224
        tx.tx_id = self.tx_id;
×
UNCOV
225
        SCLogDebug!("Creating new transaction. tx_id: {}", tx.tx_id);
×
UNCOV
226
        if self.transactions.len() > unsafe { PGSQL_MAX_TX } + self.tx_index_completed {
×
227
            // If there are too many open transactions,
228
            // mark the earliest ones as completed, and take care
229
            // to avoid quadratic complexity
230
            let mut index = self.tx_index_completed;
×
231
            for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) {
×
232
                index += 1;
×
233
                if tx_old.tx_res_state < PgsqlTxProgress::Done {
×
234
                    tx_old.tx_data.0.updated_tc = true;
×
235
                    tx_old.tx_data.0.updated_ts = true;
×
236
                    // we don't check for TxReqDone for the majority of requests are basically completed
×
237
                    // when they're parsed, as of now
×
238
                    tx_old.tx_req_state = PgsqlTxProgress::FlushedOut;
×
239
                    tx_old.tx_res_state = PgsqlTxProgress::FlushedOut;
×
240
                    tx_old
×
241
                        .tx_data
×
242
                        .set_event(PgsqlEvent::TooManyTransactions as u8);
×
243
                    break;
×
244
                }
×
245
            }
246
            self.tx_index_completed = index;
×
UNCOV
247
        }
×
UNCOV
248
        return tx;
×
UNCOV
249
    }
×
250

251
    /// Find or create a new transaction
252
    ///
253
    /// If a new transaction is created, push that into state.transactions before returning &mut to last tx
254
    /// If we can't find a transaction and we should not create one, we return None
255
    /// The moment when this is called will may impact the logic of transaction tracking (e.g. when a tx is considered completed)
256
    // TODO A future, improved version may be based on current message type and dir, too
UNCOV
257
    fn find_or_create_tx(&mut self) -> Option<&mut PgsqlTransaction> {
×
UNCOV
258
        // First, check if we should create a new tx (in case the other was completed or there's no tx yet)
×
UNCOV
259
        if self.state_progress == PgsqlStateProgress::IdleState
×
UNCOV
260
            || self.state_progress == PgsqlStateProgress::StartupMessageReceived
×
UNCOV
261
            || self.state_progress == PgsqlStateProgress::PasswordMessageReceived
×
UNCOV
262
            || self.state_progress == PgsqlStateProgress::SASLInitialResponseReceived
×
UNCOV
263
            || self.state_progress == PgsqlStateProgress::SASLResponseReceived
×
UNCOV
264
            || self.state_progress == PgsqlStateProgress::SimpleQueryReceived
×
UNCOV
265
            || self.state_progress == PgsqlStateProgress::SSLRequestReceived
×
UNCOV
266
            || self.state_progress == PgsqlStateProgress::ConnectionTerminated
×
UNCOV
267
            || self.state_progress == PgsqlStateProgress::CancelRequestReceived
×
UNCOV
268
            || self.state_progress == PgsqlStateProgress::FirstCopyDataInReceived
×
UNCOV
269
        {
×
UNCOV
270
            let tx = self.new_tx();
×
UNCOV
271
            self.transactions.push_back(tx);
×
UNCOV
272
        }
×
273
        // If we don't need a new transaction, just return the current one
274
        SCLogDebug!("find_or_create state is {:?}", &self.state_progress);
UNCOV
275
        return self.transactions.back_mut();
×
UNCOV
276
    }
×
277

UNCOV
278
    fn get_curr_state(&mut self) -> PgsqlStateProgress {
×
UNCOV
279
        self.state_progress
×
UNCOV
280
    }
×
281

282
    /// Define PgsqlState progression, based on the request received
283
    ///
284
    /// As PostgreSQL transactions can have multiple messages, State progression
285
    /// is what helps us keep track of the PgsqlTransactions - when one finished
286
    /// when the other starts.
287
    /// State isn't directly updated to avoid reference borrowing conflicts.
UNCOV
288
    fn request_next_state(&mut self, request: &PgsqlFEMessage) -> Option<PgsqlStateProgress> {
×
UNCOV
289
        match request {
×
UNCOV
290
            PgsqlFEMessage::SSLRequest(_) => Some(PgsqlStateProgress::SSLRequestReceived),
×
UNCOV
291
            PgsqlFEMessage::StartupMessage(_) => Some(PgsqlStateProgress::StartupMessageReceived),
×
UNCOV
292
            PgsqlFEMessage::PasswordMessage(_) => Some(PgsqlStateProgress::PasswordMessageReceived),
×
293
            PgsqlFEMessage::SASLInitialResponse(_) => {
UNCOV
294
                Some(PgsqlStateProgress::SASLInitialResponseReceived)
×
295
            }
UNCOV
296
            PgsqlFEMessage::SASLResponse(_) => Some(PgsqlStateProgress::SASLResponseReceived),
×
297
            PgsqlFEMessage::SimpleQuery(_) => {
298
                SCLogDebug!("Match: SimpleQuery");
UNCOV
299
                Some(PgsqlStateProgress::SimpleQueryReceived)
×
300
                // TODO here we may want to save the command that was received, to compare that later on when we receive command completed?
301

302
                // Important to keep in mind that: "In simple Query mode, the format of retrieved values is always text, except when the given command is a FETCH from a cursor declared with the BINARY option. In that case, the retrieved values are in binary format. The format codes given in the RowDescription message tell which format is being used." (from pgsql official documentation)
303
            }
304
            PgsqlFEMessage::ConsolidatedCopyDataIn(_) => {
UNCOV
305
                match self.get_curr_state() {
×
306
                    PgsqlStateProgress::CopyInResponseReceived => {
UNCOV
307
                        return Some(PgsqlStateProgress::FirstCopyDataInReceived);
×
308
                    }
309
                    PgsqlStateProgress::FirstCopyDataInReceived
310
                    | PgsqlStateProgress::ConsolidatingCopyDataIn => {
311
                        // We are in CopyInResponseReceived state, and we received a CopyDataIn message
312
                        // We can either be in the first CopyDataIn message or in the middle
313
                        // of consolidating CopyDataIn messages
314
                        return Some(PgsqlStateProgress::ConsolidatingCopyDataIn);
×
315
                    }
316
                    _ => {
317
                        return None;
×
318
                    }
319
                }
320
            }
UNCOV
321
            PgsqlFEMessage::CopyDone(_) => Some(PgsqlStateProgress::CopyDoneReceived),
×
322
            PgsqlFEMessage::CopyFail(_) => Some(PgsqlStateProgress::CopyFailReceived),
×
UNCOV
323
            PgsqlFEMessage::CancelRequest(_) => Some(PgsqlStateProgress::CancelRequestReceived),
×
324
            PgsqlFEMessage::Terminate(_) => {
325
                SCLogDebug!("Match: Terminate message");
UNCOV
326
                Some(PgsqlStateProgress::ConnectionTerminated)
×
327
            }
328
            PgsqlFEMessage::UnknownMessageType(_) => {
329
                SCLogDebug!("Match: Unknown request message type");
330
                // Not changing state when we don't know the message
UNCOV
331
                None
×
332
            }
333
        }
UNCOV
334
    }
×
335

UNCOV
336
    fn state_based_req_parsing(
×
UNCOV
337
        state: PgsqlStateProgress, input: &[u8],
×
UNCOV
338
    ) -> IResult<&[u8], parser::PgsqlFEMessage, PgsqlParseError<&[u8]>> {
×
UNCOV
339
        match state {
×
340
            PgsqlStateProgress::SASLAuthenticationReceived => {
UNCOV
341
                parser::parse_sasl_initial_response(input)
×
342
            }
343
            PgsqlStateProgress::SASLInitialResponseReceived
344
            | PgsqlStateProgress::SASLAuthenticationContinueReceived => {
UNCOV
345
                parser::parse_sasl_response(input)
×
346
            }
347
            PgsqlStateProgress::SimpleAuthenticationReceived => {
UNCOV
348
                parser::parse_password_message(input)
×
349
            }
UNCOV
350
            _ => parser::parse_request(input),
×
351
        }
UNCOV
352
    }
×
353

354
    /// Process State progress to decide if request is finished
355
    ///
UNCOV
356
    fn request_is_complete(state: PgsqlStateProgress) -> bool {
×
UNCOV
357
        match state {
×
358
            PgsqlStateProgress::SSLRequestReceived
359
            | PgsqlStateProgress::StartupMessageReceived
360
            | PgsqlStateProgress::SimpleQueryReceived
361
            | PgsqlStateProgress::PasswordMessageReceived
362
            | PgsqlStateProgress::SASLInitialResponseReceived
363
            | PgsqlStateProgress::SASLResponseReceived
364
            | PgsqlStateProgress::CancelRequestReceived
365
            | PgsqlStateProgress::CopyDoneReceived
366
            | PgsqlStateProgress::CopyFailReceived
UNCOV
367
            | PgsqlStateProgress::ConnectionTerminated => true,
×
UNCOV
368
            _ => false,
×
369
        }
UNCOV
370
    }
×
371

UNCOV
372
    fn parse_request(&mut self, flow: *mut Flow, input: &[u8]) -> AppLayerResult {
×
UNCOV
373
        // We're not interested in empty requests.
×
UNCOV
374
        if input.is_empty() {
×
UNCOV
375
            return AppLayerResult::ok();
×
UNCOV
376
        }
×
UNCOV
377

×
UNCOV
378
        // If there was gap, check we can sync up again.
×
UNCOV
379
        if self.request_gap {
×
380
            if parser::parse_request(input).is_ok() {
×
381
                // The parser now needs to decide what to do as we are not in sync.
382
                // For now, we'll just try again next time.
383
                SCLogDebug!("Suricata interprets there's a gap in the request");
384
                return AppLayerResult::ok();
×
385
            }
×
386

×
387
            // It looks like we're in sync with the message header
×
388
            // clear gap state and keep parsing.
×
389
            self.request_gap = false;
×
UNCOV
390
        }
×
391

UNCOV
392
        let mut start = input;
×
UNCOV
393
        while !start.is_empty() {
×
394
            SCLogDebug!(
395
                "In 'parse_request' State Progress is: {:?}",
396
                &self.state_progress
397
            );
UNCOV
398
            match PgsqlState::state_based_req_parsing(self.state_progress, start) {
×
UNCOV
399
                Ok((rem, request)) => {
×
UNCOV
400
                    start = rem;
×
UNCOV
401
                    let new_state = self.request_next_state(&request);
×
402

UNCOV
403
                    if let Some(state) = new_state {
×
UNCOV
404
                        self.state_progress = state;
×
UNCOV
405
                    };
×
406
                    // PostreSQL progress states can be represented as a finite state machine
407
                    // After the connection phase, the backend/ server will be mostly waiting in a state of `ReadyForQuery`, unless
408
                    // it's processing some request.
409
                    // When the frontend wants to cancel a request, it will send a CancelRequest message over a new connection - to
410
                    // which there won't be any responses.
411
                    // If the frontend wants to terminate the connection, the backend won't send any confirmation after receiving a
412
                    // Terminate request.
413
                    // A simplified finite state machine for PostgreSQL v3 can be found at:
414
                    // https://samadhiweb.com/blog/2013.04.28.graphviz.postgresv3.html
UNCOV
415
                    if let Some(tx) = self.find_or_create_tx() {
×
UNCOV
416
                        tx.tx_data.0.updated_ts = true;
×
UNCOV
417
                        if let Some(state) = new_state {
×
UNCOV
418
                            if state == PgsqlStateProgress::FirstCopyDataInReceived
×
UNCOV
419
                            || state == PgsqlStateProgress::ConsolidatingCopyDataIn {
×
420
                                // here we're actually only counting how many messages were received.
421
                                // frontends are not forced to send one row per message
UNCOV
422
                                if let PgsqlFEMessage::ConsolidatedCopyDataIn(ref msg) = request {
×
UNCOV
423
                                    tx.sum_data_size(msg.data_size);
×
UNCOV
424
                                    tx.incr_row_cnt();
×
UNCOV
425
                                }
×
UNCOV
426
                            } else if (state == PgsqlStateProgress::CopyDoneReceived || state == PgsqlStateProgress::CopyFailReceived) && tx.get_row_cnt() > 0 {
×
UNCOV
427
                                let consolidated_copy_data = PgsqlFEMessage::ConsolidatedCopyDataIn(
×
UNCOV
428
                                    ConsolidatedDataRowPacket {
×
UNCOV
429
                                        identifier: b'd',
×
UNCOV
430
                                        row_cnt: tx.get_row_cnt(),
×
UNCOV
431
                                        data_size: tx.data_size, // total byte count of all copy_data messages combined
×
UNCOV
432
                                    },
×
UNCOV
433
                                );
×
UNCOV
434
                                tx.requests.push(consolidated_copy_data);
×
UNCOV
435
                            }
×
436

UNCOV
437
                            if Self::request_is_complete(state) {
×
UNCOV
438
                                tx.requests.push(request);
×
UNCOV
439
                                // The request is complete at this point
×
UNCOV
440
                                tx.tx_req_state = PgsqlTxProgress::Done;
×
UNCOV
441
                                if state == PgsqlStateProgress::ConnectionTerminated
×
UNCOV
442
                                    || state == PgsqlStateProgress::CancelRequestReceived
×
UNCOV
443
                                {
×
UNCOV
444
                                    /* The server won't send any responses to such requests, so transaction should be over */
×
UNCOV
445
                                    tx.tx_res_state = PgsqlTxProgress::Done;
×
UNCOV
446
                                }
×
UNCOV
447
                                sc_app_layer_parser_trigger_raw_stream_inspection(
×
UNCOV
448
                                    flow,
×
UNCOV
449
                                    Direction::ToServer as i32,
×
UNCOV
450
                                );
×
UNCOV
451
                            }
×
452
                        }
×
453
                    } else {
454
                        // If there isn't a transaction, we'll consider Suri should move on
UNCOV
455
                        return AppLayerResult::ok();
×
456
                    };
457
                }
UNCOV
458
                Err(Err::Incomplete(_needed)) => {
×
UNCOV
459
                    let consumed = input.len() - start.len();
×
UNCOV
460
                    let needed_estimation = start.len() + 1;
×
UNCOV
461
                    SCLogDebug!(
×
UNCOV
462
                        "Needed: {:?}, estimated needed: {:?}",
×
UNCOV
463
                        _needed,
×
UNCOV
464
                        needed_estimation
×
UNCOV
465
                    );
×
UNCOV
466
                    return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32);
×
467
                }
UNCOV
468
                Err(Err::Error(err)) => {
×
UNCOV
469
                    let mut tx = self.new_tx();
×
UNCOV
470
                    match err {
×
471
                        PgsqlParseError::InvalidLength => {
472
                            tx.tx_data.set_event(PgsqlEvent::InvalidLength as u8);
×
473
                            self.transactions.push_back(tx);
×
474
                            // If we don't get a valid length, we can't know how to proceed
×
475
                            return AppLayerResult::err();
×
476
                        }
UNCOV
477
                        PgsqlParseError::NomError(_i, error_kind) => {
×
UNCOV
478
                            if error_kind == nom8::error::ErrorKind::Switch {
×
UNCOV
479
                                tx.tx_data.set_event(PgsqlEvent::MalformedRequest as u8);
×
UNCOV
480
                                self.transactions.push_back(tx);
×
UNCOV
481
                            }
×
482
                            SCLogDebug!("Parsing error: {:?}", error_kind);
483
                        }
484
                    }
485
                    // If we have parsed the message length, let's assume we can
486
                    // move onto the next PDU even if we can't parse the current message
UNCOV
487
                    return AppLayerResult::ok();
×
488
                }
489
                Err(_) => {
490
                    SCLogDebug!("Error while parsing PGSQL request");
491
                    return AppLayerResult::err();
×
492
                }
493
            }
494
        }
495

496
        // Input was fully consumed.
UNCOV
497
        return AppLayerResult::ok();
×
UNCOV
498
    }
×
499

500
    /// When the state changes based on a specific response, there are other actions we may need to perform
501
    ///
502
    /// If there is data from the backend message that Suri should store separately in the State or
503
    /// Transaction, that is also done here
UNCOV
504
    fn response_process_next_state(
×
UNCOV
505
        &mut self, response: &PgsqlBEMessage, f: *mut Flow,
×
UNCOV
506
    ) -> Option<PgsqlStateProgress> {
×
UNCOV
507
        match response {
×
508
            PgsqlBEMessage::SSLResponse(parser::SSLResponseMessage::SSLAccepted) => {
509
                SCLogDebug!("SSL Request accepted");
UNCOV
510
                unsafe {
×
UNCOV
511
                    SCAppLayerRequestProtocolTLSUpgrade(f);
×
UNCOV
512
                }
×
UNCOV
513
                Some(PgsqlStateProgress::Finished)
×
514
            }
515
            PgsqlBEMessage::SSLResponse(parser::SSLResponseMessage::SSLRejected) => {
516
                SCLogDebug!("SSL Request rejected");
UNCOV
517
                Some(PgsqlStateProgress::SSLRejectedReceived)
×
518
            }
519
            PgsqlBEMessage::AuthenticationSASL(_) => {
UNCOV
520
                Some(PgsqlStateProgress::SASLAuthenticationReceived)
×
521
            }
522
            PgsqlBEMessage::AuthenticationSASLContinue(_) => {
UNCOV
523
                Some(PgsqlStateProgress::SASLAuthenticationContinueReceived)
×
524
            }
525
            PgsqlBEMessage::AuthenticationSASLFinal(_) => {
UNCOV
526
                Some(PgsqlStateProgress::SASLAuthenticationFinalReceived)
×
527
            }
528
            PgsqlBEMessage::AuthenticationOk(_) => {
UNCOV
529
                Some(PgsqlStateProgress::AuthenticationOkReceived)
×
530
            }
UNCOV
531
            PgsqlBEMessage::ParameterStatus(_) => Some(PgsqlStateProgress::ParameterSetup),
×
532
            PgsqlBEMessage::BackendKeyData(_) => {
UNCOV
533
                let backend_info = response.get_backendkey_info();
×
UNCOV
534
                self.backend_pid = backend_info.0;
×
UNCOV
535
                self.backend_secret_key = backend_info.1;
×
UNCOV
536
                Some(PgsqlStateProgress::BackendKeyReceived)
×
537
            }
UNCOV
538
            PgsqlBEMessage::ReadyForQuery(_) => Some(PgsqlStateProgress::ReadyForQueryReceived),
×
539
            // TODO should we store any Parameter Status in PgsqlState?
540
            // TODO -- For CopyBoth mode, parameterstatus may be important (replication parameter)
541
            PgsqlBEMessage::AuthenticationMD5Password(_)
542
            | PgsqlBEMessage::AuthenticationCleartextPassword(_) => {
UNCOV
543
                Some(PgsqlStateProgress::SimpleAuthenticationReceived)
×
544
            }
UNCOV
545
            PgsqlBEMessage::RowDescription(_) => Some(PgsqlStateProgress::RowDescriptionReceived),
×
UNCOV
546
            PgsqlBEMessage::CopyOutResponse(_) => Some(PgsqlStateProgress::CopyOutResponseReceived),
×
UNCOV
547
            PgsqlBEMessage::CopyInResponse(_) => Some(PgsqlStateProgress::CopyInResponseReceived),
×
UNCOV
548
            PgsqlBEMessage::ConsolidatedDataRow(msg) => {
×
UNCOV
549
                // Increment tx.data_size here, since we know msg type, so that we can later on log that info
×
UNCOV
550
                self.transactions.back_mut()?.sum_data_size(msg.data_size);
×
UNCOV
551
                Some(PgsqlStateProgress::DataRowReceived)
×
552
            }
UNCOV
553
            PgsqlBEMessage::ConsolidatedCopyDataOut(msg) => {
×
UNCOV
554
                // Increment tx.data_size here, since we know msg type, so that we can later on log that info
×
UNCOV
555
                self.transactions.back_mut()?.sum_data_size(msg.data_size);
×
UNCOV
556
                Some(PgsqlStateProgress::CopyDataOutReceived)
×
557
            }
UNCOV
558
            PgsqlBEMessage::CopyDone(_) => Some(PgsqlStateProgress::CopyDoneReceived),
×
559
            PgsqlBEMessage::CommandComplete(_) => {
560
                // TODO Do we want to compare the command that was stored when
561
                // query was sent with what we received here?
UNCOV
562
                Some(PgsqlStateProgress::CommandCompletedReceived)
×
563
            }
564
            PgsqlBEMessage::UnknownMessageType(_) => {
565
                SCLogDebug!("Match: Unknown response message type");
566
                // Not changing state when we don't know the message
UNCOV
567
                None
×
568
            }
UNCOV
569
            PgsqlBEMessage::ErrorResponse(_) => Some(PgsqlStateProgress::ErrorMessageReceived),
×
570
            _ => {
571
                // We don't always have to change current state when we see a response...
572
                // NotificationResponse and NoticeResponse fall here
UNCOV
573
                None
×
574
            }
575
        }
UNCOV
576
    }
×
577

UNCOV
578
    fn state_based_resp_parsing(
×
UNCOV
579
        state: PgsqlStateProgress, input: &[u8],
×
UNCOV
580
    ) -> IResult<&[u8], parser::PgsqlBEMessage, PgsqlParseError<&[u8]>> {
×
UNCOV
581
        if state == PgsqlStateProgress::SSLRequestReceived {
×
UNCOV
582
            parser::parse_ssl_response(input)
×
583
        } else {
UNCOV
584
            parser::pgsql_parse_response(input)
×
585
        }
UNCOV
586
    }
×
587

588
    /// Process State progress to decide if response is finished
589
    ///
UNCOV
590
    fn response_is_complete(state: PgsqlStateProgress) -> bool {
×
UNCOV
591
        match state {
×
592
            PgsqlStateProgress::ReadyForQueryReceived
593
            | PgsqlStateProgress::SSLRejectedReceived
594
            | PgsqlStateProgress::SimpleAuthenticationReceived
595
            | PgsqlStateProgress::SASLAuthenticationReceived
596
            | PgsqlStateProgress::SASLAuthenticationContinueReceived
597
            | PgsqlStateProgress::SASLAuthenticationFinalReceived
598
            | PgsqlStateProgress::CopyInResponseReceived
UNCOV
599
            | PgsqlStateProgress::Finished => true,
×
UNCOV
600
            _ => false,
×
601
        }
UNCOV
602
    }
×
603

UNCOV
604
    fn parse_response(&mut self, flow: *mut Flow, input: &[u8]) -> AppLayerResult {
×
UNCOV
605
        // We're not interested in empty responses.
×
UNCOV
606
        if input.is_empty() {
×
607
            return AppLayerResult::ok();
×
UNCOV
608
        }
×
UNCOV
609

×
UNCOV
610
        if self.response_gap {
×
611
            if !probe_tc(input) {
×
612
                // Out of sync, we'll just try again next time.
613
                SCLogDebug!("Suricata interprets there's a gap in the response");
614
                return AppLayerResult::ok();
×
615
            }
×
616

×
617
            // It seems we're in sync with a message header, clear gap state and keep parsing.
×
618
            self.response_gap = false;
×
UNCOV
619
        }
×
620

UNCOV
621
        let mut start = input;
×
UNCOV
622
        while !start.is_empty() {
×
UNCOV
623
            match PgsqlState::state_based_resp_parsing(self.state_progress, start) {
×
UNCOV
624
                Ok((rem, response)) => {
×
UNCOV
625
                    start = rem;
×
UNCOV
626
                    SCLogDebug!("Response is {:?}", &response);
×
UNCOV
627
                    let new_state = self.response_process_next_state(&response, flow);
×
UNCOV
628
                    if let Some(state) = new_state {
×
UNCOV
629
                        self.state_progress = state;
×
UNCOV
630
                    }
×
UNCOV
631
                    if let Some(tx) = self.find_or_create_tx() {
×
UNCOV
632
                        tx.tx_data.0.updated_tc = true;
×
UNCOV
633
                        if tx.tx_res_state == PgsqlTxProgress::Init {
×
UNCOV
634
                            tx.tx_res_state = PgsqlTxProgress::Received;
×
UNCOV
635
                        }
×
UNCOV
636
                        if let Some(state) = new_state {
×
UNCOV
637
                            if state == PgsqlStateProgress::DataRowReceived {
×
UNCOV
638
                                tx.incr_row_cnt();
×
UNCOV
639
                            } else if state == PgsqlStateProgress::CommandCompletedReceived
×
UNCOV
640
                                && tx.get_row_cnt() > 0
×
UNCOV
641
                            {
×
UNCOV
642
                                // let's summarize the info from the data_rows in one response
×
UNCOV
643
                                let consolidated_data_row = PgsqlBEMessage::ConsolidatedDataRow(
×
UNCOV
644
                                    ConsolidatedDataRowPacket {
×
UNCOV
645
                                        identifier: b'D',
×
UNCOV
646
                                        row_cnt: tx.get_row_cnt(),
×
UNCOV
647
                                        data_size: tx.data_size, // total byte count of all data_row messages combined
×
UNCOV
648
                                    },
×
UNCOV
649
                                );
×
UNCOV
650
                                tx.responses.push(consolidated_data_row);
×
UNCOV
651
                                tx.responses.push(response);
×
UNCOV
652
                                // reset values
×
UNCOV
653
                                tx.data_row_cnt = 0;
×
UNCOV
654
                                tx.data_size = 0;
×
UNCOV
655
                            } else if state == PgsqlStateProgress::CopyDataOutReceived {
×
UNCOV
656
                                tx.incr_row_cnt();
×
UNCOV
657
                            } else if state == PgsqlStateProgress::CopyDoneReceived
×
UNCOV
658
                                && tx.get_row_cnt() > 0
×
UNCOV
659
                            {
×
UNCOV
660
                                // let's summarize the info from the data_rows in one response
×
UNCOV
661
                                let consolidated_copy_data = PgsqlBEMessage::ConsolidatedCopyDataOut(
×
UNCOV
662
                                    ConsolidatedDataRowPacket {
×
UNCOV
663
                                        identifier: b'd',
×
UNCOV
664
                                        row_cnt: tx.get_row_cnt(),
×
UNCOV
665
                                        data_size: tx.data_size, // total byte count of all data_row messages combined
×
UNCOV
666
                                    },
×
UNCOV
667
                                );
×
UNCOV
668
                                tx.responses.push(consolidated_copy_data);
×
UNCOV
669
                                tx.responses.push(response);
×
UNCOV
670
                                // reset values
×
UNCOV
671
                                tx.data_row_cnt = 0;
×
UNCOV
672
                                tx.data_size = 0;
×
UNCOV
673
                            } else {
×
UNCOV
674
                                tx.responses.push(response);
×
UNCOV
675
                                if Self::response_is_complete(state) {
×
UNCOV
676
                                    tx.tx_req_state = PgsqlTxProgress::Done;
×
UNCOV
677
                                    tx.tx_res_state = PgsqlTxProgress::Done;
×
UNCOV
678
                                    sc_app_layer_parser_trigger_raw_stream_inspection(
×
UNCOV
679
                                        flow,
×
UNCOV
680
                                        Direction::ToClient as i32,
×
UNCOV
681
                                    );
×
UNCOV
682
                                }
×
683
                            }
684
                        }
×
685
                    } else {
686
                        // If there isn't a transaction, we'll consider Suri should move on
UNCOV
687
                        return AppLayerResult::ok();
×
688
                    };
689
                }
UNCOV
690
                Err(Err::Incomplete(_needed)) => {
×
UNCOV
691
                    let consumed = input.len() - start.len();
×
UNCOV
692
                    let needed_estimation = start.len() + 1;
×
UNCOV
693
                    SCLogDebug!(
×
UNCOV
694
                        "Needed: {:?}, estimated needed: {:?}, start is {:?}",
×
UNCOV
695
                        _needed,
×
UNCOV
696
                        needed_estimation,
×
UNCOV
697
                        &start
×
UNCOV
698
                    );
×
UNCOV
699
                    return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32);
×
700
                }
701
                Err(Err::Error(err)) => {
×
702
                    let mut tx = self.new_tx();
×
703
                    match err {
×
704
                        PgsqlParseError::InvalidLength => {
705
                            tx.tx_data.set_event(PgsqlEvent::InvalidLength as u8);
×
706
                            self.transactions.push_back(tx);
×
707
                            // If we don't get a valid length, we can't know how to proceed
×
708
                            return AppLayerResult::err();
×
709
                        }
710
                        PgsqlParseError::NomError(_i, error_kind) => {
×
711
                            if error_kind == nom8::error::ErrorKind::Switch {
×
712
                                tx.tx_data.set_event(PgsqlEvent::MalformedResponse as u8);
×
713
                                self.transactions.push_back(tx);
×
714
                            }
×
715
                            SCLogDebug!("Parsing error: {:?}", error_kind);
716
                        }
717
                    }
718
                    // If we have parsed the message length, let's assume we can
719
                    // move onto the next PDU even if we can't parse the current message
720
                    return AppLayerResult::ok();
×
721
                }
722
                Err(_) => {
723
                    SCLogDebug!("Error while parsing PGSQL response");
724
                    return AppLayerResult::err();
×
725
                }
726
            }
727
        }
728

729
        // All input was fully consumed.
UNCOV
730
        return AppLayerResult::ok();
×
UNCOV
731
    }
×
732

733
    fn on_request_gap(&mut self, _size: u32) {
×
734
        self.request_gap = true;
×
735
    }
×
736

737
    fn on_response_gap(&mut self, _size: u32) {
×
738
        self.response_gap = true;
×
739
    }
×
740
}
741

742
/// Probe for a valid PostgreSQL response
743
///
744
/// Currently, for parser usage only. We have a bit more logic in the function
745
/// used by the engine.
746
/// PGSQL messages don't have a header per se, so we parse the slice for an ok()
UNCOV
747
fn probe_tc(input: &[u8]) -> bool {
×
UNCOV
748
    if parser::pgsql_parse_response(input).is_ok() || parser::parse_ssl_response(input).is_ok() {
×
UNCOV
749
        return true;
×
750
    }
×
751
    SCLogDebug!("probe_tc is false");
×
752
    false
×
UNCOV
753
}
×
754

UNCOV
755
fn pgsql_tx_get_req_state(tx: *mut std::os::raw::c_void) -> PgsqlTxProgress {
×
UNCOV
756
    let tx_safe: &mut PgsqlTransaction;
×
UNCOV
757
    unsafe {
×
UNCOV
758
        tx_safe = cast_pointer!(tx, PgsqlTransaction);
×
UNCOV
759
    }
×
UNCOV
760
    tx_safe.tx_req_state
×
UNCOV
761
}
×
762

UNCOV
763
fn pgsql_tx_get_res_state(tx: *mut std::os::raw::c_void) -> PgsqlTxProgress {
×
UNCOV
764
    let tx_safe: &mut PgsqlTransaction;
×
UNCOV
765
    unsafe {
×
UNCOV
766
        tx_safe = cast_pointer!(tx, PgsqlTransaction);
×
UNCOV
767
    }
×
UNCOV
768
    tx_safe.tx_res_state
×
UNCOV
769
}
×
770

771
// C exports.
772

773
/// C entry point for a probing parser.
UNCOV
774
unsafe extern "C" fn probing_parser_ts(
×
UNCOV
775
    _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
×
UNCOV
776
) -> AppProto {
×
UNCOV
777
    if input_len >= 1 && !input.is_null() {
×
UNCOV
778
        let slice: &[u8] = build_slice!(input, input_len as usize);
×
UNCOV
779

×
UNCOV
780
        match parser::parse_request(slice) {
×
781
            Ok((_, _)) => {
UNCOV
782
                return ALPROTO_PGSQL;
×
783
            }
784
            Err(Err::Incomplete(_)) => {
UNCOV
785
                return ALPROTO_UNKNOWN;
×
786
            }
UNCOV
787
            Err(_e) => {
×
UNCOV
788
                return ALPROTO_FAILED;
×
789
            }
790
        }
UNCOV
791
    }
×
UNCOV
792
    return ALPROTO_UNKNOWN;
×
UNCOV
793
}
×
794

795
/// C entry point for a probing parser.
UNCOV
796
unsafe extern "C" fn probing_parser_tc(
×
UNCOV
797
    _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
×
UNCOV
798
) -> AppProto {
×
UNCOV
799
    if input_len >= 1 && !input.is_null() {
×
UNCOV
800
        let slice: &[u8] = build_slice!(input, input_len as usize);
×
UNCOV
801

×
UNCOV
802
        if parser::parse_ssl_response(slice).is_ok() {
×
UNCOV
803
            return ALPROTO_PGSQL;
×
UNCOV
804
        }
×
UNCOV
805

×
UNCOV
806
        match parser::pgsql_parse_response(slice) {
×
807
            Ok((_, _)) => {
UNCOV
808
                return ALPROTO_PGSQL;
×
809
            }
810
            Err(Err::Incomplete(_)) => {
UNCOV
811
                return ALPROTO_UNKNOWN;
×
812
            }
UNCOV
813
            Err(_e) => {
×
UNCOV
814
                return ALPROTO_FAILED;
×
815
            }
816
        }
UNCOV
817
    }
×
UNCOV
818
    return ALPROTO_UNKNOWN;
×
UNCOV
819
}
×
820

UNCOV
821
extern "C" fn state_new(
×
UNCOV
822
    _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
×
UNCOV
823
) -> *mut std::os::raw::c_void {
×
UNCOV
824
    let state = PgsqlState::new();
×
UNCOV
825
    let boxed = Box::new(state);
×
UNCOV
826
    return Box::into_raw(boxed) as *mut _;
×
UNCOV
827
}
×
828

UNCOV
829
extern "C" fn state_free(state: *mut std::os::raw::c_void) {
×
UNCOV
830
    // Just unbox...
×
UNCOV
831
    std::mem::drop(unsafe { Box::from_raw(state as *mut PgsqlState) });
×
UNCOV
832
}
×
833

UNCOV
834
unsafe extern "C" fn state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
×
UNCOV
835
    let state_safe: &mut PgsqlState = cast_pointer!(state, PgsqlState);
×
UNCOV
836
    state_safe.free_tx(tx_id);
×
UNCOV
837
}
×
838

UNCOV
839
unsafe extern "C" fn parse_request(
×
UNCOV
840
    flow: *mut Flow, state: *mut std::os::raw::c_void, pstate: *mut AppLayerParserState,
×
UNCOV
841
    stream_slice: StreamSlice, _data: *mut std::os::raw::c_void,
×
UNCOV
842
) -> AppLayerResult {
×
UNCOV
843
    if stream_slice.is_empty() {
×
UNCOV
844
        if SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 {
×
845
            SCLogDebug!(" Suricata reached `eof`");
UNCOV
846
            return AppLayerResult::ok();
×
847
        } else {
848
            return AppLayerResult::err();
×
849
        }
UNCOV
850
    }
×
UNCOV
851

×
UNCOV
852
    let state_safe: &mut PgsqlState = cast_pointer!(state, PgsqlState);
×
UNCOV
853

×
UNCOV
854
    if stream_slice.is_gap() {
×
855
        state_safe.on_request_gap(stream_slice.gap_size());
×
UNCOV
856
    } else if !stream_slice.is_empty() {
×
UNCOV
857
        return state_safe.parse_request(flow, stream_slice.as_slice());
×
858
    }
×
859
    AppLayerResult::ok()
×
UNCOV
860
}
×
861

UNCOV
862
unsafe extern "C" fn parse_response(
×
UNCOV
863
    flow: *mut Flow, state: *mut std::os::raw::c_void, pstate: *mut AppLayerParserState,
×
UNCOV
864
    stream_slice: StreamSlice, _data: *mut std::os::raw::c_void,
×
UNCOV
865
) -> AppLayerResult {
×
UNCOV
866
    if stream_slice.is_empty() {
×
UNCOV
867
        if SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 {
×
UNCOV
868
            return AppLayerResult::ok();
×
869
        } else {
870
            return AppLayerResult::err();
×
871
        }
UNCOV
872
    }
×
UNCOV
873

×
UNCOV
874
    let state_safe: &mut PgsqlState = cast_pointer!(state, PgsqlState);
×
UNCOV
875

×
UNCOV
876
    if stream_slice.is_gap() {
×
877
        state_safe.on_response_gap(stream_slice.gap_size());
×
UNCOV
878
    } else if !stream_slice.is_empty() {
×
UNCOV
879
        return state_safe.parse_response(flow, stream_slice.as_slice());
×
880
    }
×
881
    AppLayerResult::ok()
×
UNCOV
882
}
×
883

UNCOV
884
unsafe extern "C" fn state_get_tx(
×
UNCOV
885
    state: *mut std::os::raw::c_void, tx_id: u64,
×
UNCOV
886
) -> *mut std::os::raw::c_void {
×
UNCOV
887
    let state_safe: &mut PgsqlState = cast_pointer!(state, PgsqlState);
×
UNCOV
888
    match state_safe.get_tx(tx_id) {
×
UNCOV
889
        Some(tx) => {
×
UNCOV
890
            return tx as *const _ as *mut _;
×
891
        }
892
        None => {
893
            return std::ptr::null_mut();
×
894
        }
895
    }
UNCOV
896
}
×
897

UNCOV
898
unsafe extern "C" fn state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
×
UNCOV
899
    let state_safe: &mut PgsqlState = cast_pointer!(state, PgsqlState);
×
UNCOV
900
    return state_safe.tx_id;
×
UNCOV
901
}
×
902

UNCOV
903
unsafe extern "C" fn tx_get_al_state_progress(
×
UNCOV
904
    tx: *mut std::os::raw::c_void, direction: u8,
×
UNCOV
905
) -> std::os::raw::c_int {
×
UNCOV
906
    if direction == Direction::ToServer as u8 {
×
UNCOV
907
        return pgsql_tx_get_req_state(tx) as i32;
×
UNCOV
908
    }
×
UNCOV
909

×
UNCOV
910
    // Direction has only two possible values, so we don't need to check for the other one
×
UNCOV
911
    pgsql_tx_get_res_state(tx) as i32
×
UNCOV
912
}
×
913

914
export_tx_data_get!(pgsql_get_tx_data, PgsqlTransaction);
915
export_state_data_get!(pgsql_get_state_data, PgsqlState);
916

917
// Parser name as a C style string.
918
const PARSER_NAME: &[u8] = b"pgsql\0";
919

920
#[no_mangle]
921
pub unsafe extern "C" fn SCRegisterPgsqlParser() {
33✔
922
    let default_port = CString::new("[5432]").unwrap();
33✔
923
    let mut stream_depth = PGSQL_CONFIG_DEFAULT_STREAM_DEPTH;
33✔
924
    let parser = RustParser {
33✔
925
        name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
33✔
926
        default_port: default_port.as_ptr(),
33✔
927
        ipproto: IPPROTO_TCP,
33✔
928
        probe_ts: Some(probing_parser_ts),
33✔
929
        probe_tc: Some(probing_parser_tc),
33✔
930
        min_depth: 0,
33✔
931
        max_depth: 16,
33✔
932
        state_new,
33✔
933
        state_free,
33✔
934
        tx_free: state_tx_free,
33✔
935
        parse_ts: parse_request,
33✔
936
        parse_tc: parse_response,
33✔
937
        get_tx_count: state_get_tx_count,
33✔
938
        get_tx: state_get_tx,
33✔
939
        tx_comp_st_ts: PgsqlTxProgress::Done as i32,
33✔
940
        tx_comp_st_tc: PgsqlTxProgress::Done as i32,
33✔
941
        tx_get_progress: tx_get_al_state_progress,
33✔
942
        get_eventinfo: Some(PgsqlEvent::get_event_info),
33✔
943
        get_eventinfo_byid: Some(PgsqlEvent::get_event_info_by_id),
33✔
944
        localstorage_new: None,
33✔
945
        localstorage_free: None,
33✔
946
        get_tx_files: None,
33✔
947
        get_tx_iterator: Some(
33✔
948
            crate::applayer::state_get_tx_iterator::<PgsqlState, PgsqlTransaction>,
33✔
949
        ),
33✔
950
        get_tx_data: pgsql_get_tx_data,
33✔
951
        get_state_data: pgsql_get_state_data,
33✔
952
        apply_tx_config: None,
33✔
953
        flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS,
33✔
954
        get_frame_id_by_name: None,
33✔
955
        get_frame_name_by_id: None,
33✔
956
        get_state_id_by_name: None,
33✔
957
        get_state_name_by_id: None,
33✔
958
    };
33✔
959

33✔
960
    let ip_proto_str = CString::new("tcp").unwrap();
33✔
961

33✔
962
    if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
33✔
963
        let alproto = applayer_register_protocol_detection(&parser, 1);
26✔
964
        ALPROTO_PGSQL = alproto;
26✔
965
        if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
26✔
966
            let _ = AppLayerRegisterParser(&parser, alproto);
26✔
967
        }
26✔
968
        SCLogDebug!("Rust pgsql parser registered.");
969
        let retval = conf_get("app-layer.protocols.pgsql.stream-depth");
26✔
970
        if let Some(val) = retval {
26✔
UNCOV
971
            match get_memval(val) {
×
UNCOV
972
                Ok(retval) => {
×
UNCOV
973
                    stream_depth = retval as u32;
×
UNCOV
974
                }
×
975
                Err(_) => {
976
                    SCLogError!("Invalid depth value");
×
977
                }
978
            }
UNCOV
979
            SCAppLayerParserSetStreamDepth(IPPROTO_TCP, ALPROTO_PGSQL, stream_depth)
×
980
        }
26✔
981
        if let Some(val) = conf_get("app-layer.protocols.pgsql.max-tx") {
26✔
982
            if let Ok(v) = val.parse::<usize>() {
×
983
                PGSQL_MAX_TX = v;
×
984
            } else {
×
985
                SCLogError!("Invalid value for pgsql.max-tx");
×
986
            }
987
        }
26✔
988
    } else {
7✔
989
        SCLogDebug!("Protocol detector and parser disabled for PGSQL.");
7✔
990
    }
7✔
991
}
33✔
992

993
#[cfg(test)]
994
mod test {
995
    use super::*;
996

997
    #[test]
998
    fn test_response_probe() {
999
        /* Authentication Request MD5 password salt value f211a3ed */
1000
        let buf: &[u8] = &[
1001
            0x52, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x05, 0xf2, 0x11, 0xa3, 0xed,
1002
        ];
1003
        assert!(probe_tc(buf));
1004

1005
        /* R  8 -- Authentication Cleartext */
1006
        let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03];
1007
        assert!(probe_tc(buf));
1008

1009
        let buf: &[u8] = &[
1010
            /* R */ 0x52, /* 54 */ 0x00, 0x00, 0x00, 0x36, /* 12 */ 0x00, 0x00,
1011
            0x00, 0x0c, /* signature */ 0x76, 0x3d, 0x64, 0x31, 0x50, 0x58, 0x61, 0x38, 0x54,
1012
            0x4b, 0x46, 0x50, 0x5a, 0x72, 0x52, 0x33, 0x4d, 0x42, 0x52, 0x6a, 0x4c, 0x79, 0x33,
1013
            0x2b, 0x4a, 0x36, 0x79, 0x78, 0x72, 0x66, 0x77, 0x2f, 0x7a, 0x7a, 0x70, 0x38, 0x59,
1014
            0x54, 0x39, 0x65, 0x78, 0x56, 0x37, 0x73, 0x38, 0x3d,
1015
        ];
1016
        assert!(probe_tc(buf));
1017

1018
        /* S   26 -- parameter status application_name psql*/
1019
        let buf: &[u8] = &[
1020
            0x53, 0x00, 0x00, 0x00, 0x1a, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,
1021
            0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x70, 0x73, 0x71, 0x6c, 0x00,
1022
        ];
1023
        assert!(probe_tc(buf));
1024
    }
1025

1026
    #[test]
1027
    fn test_request_events() {
1028
        let mut state = PgsqlState::new();
1029
        // an SSL Request
1030
        let buf: &[u8] = &[0x00, 0x00, 0x00, 0x08, 0x04, 0xd2, 0x16, 0x2f];
1031
        // We can pass null here as the only place that uses flow in the parse_request fn isn't run for unittests
1032
        state.parse_request(std::ptr::null_mut(), buf);
1033
        let ok_state = PgsqlStateProgress::SSLRequestReceived;
1034

1035
        assert_eq!(state.state_progress, ok_state);
1036

1037
        // TODO add test for startup request
1038
    }
1039

1040
    #[test]
1041
    fn test_incomplete_request() {
1042
        let mut state = PgsqlState::new();
1043
        // An SSL Request
1044
        let buf: &[u8] = &[0x00, 0x00, 0x00, 0x08, 0x04, 0xd2, 0x16, 0x2f];
1045

1046
        // We can pass null here as the only place that uses flow in the parse_request fn isn't run for unittests
1047
        let r = state.parse_request(std::ptr::null_mut(), &buf[0..0]);
1048
        assert_eq!(
1049
            r,
1050
            AppLayerResult {
1051
                status: 0,
1052
                consumed: 0,
1053
                needed: 0
1054
            }
1055
        );
1056

1057
        let r = state.parse_request(std::ptr::null_mut(), &buf[0..1]);
1058
        assert_eq!(
1059
            r,
1060
            AppLayerResult {
1061
                status: 1,
1062
                consumed: 0,
1063
                needed: 2
1064
            }
1065
        );
1066

1067
        let r = state.parse_request(std::ptr::null_mut(), &buf[0..2]);
1068
        assert_eq!(
1069
            r,
1070
            AppLayerResult {
1071
                status: 1,
1072
                consumed: 0,
1073
                needed: 3
1074
            }
1075
        );
1076
    }
1077

1078
    #[test]
1079
    fn test_find_or_create_tx() {
1080
        let mut state = PgsqlState::new();
1081
        state.state_progress = PgsqlStateProgress::UnknownState;
1082
        let tx = state.find_or_create_tx();
1083
        assert!(tx.is_none());
1084

1085
        state.state_progress = PgsqlStateProgress::IdleState;
1086
        let tx = state.find_or_create_tx();
1087
        assert!(tx.is_some());
1088

1089
        // Now, even though there isn't a new transaction created, the previous one is available
1090
        state.state_progress = PgsqlStateProgress::SSLRejectedReceived;
1091
        let tx = state.find_or_create_tx();
1092
        assert!(tx.is_some());
1093
        assert_eq!(tx.unwrap().tx_id, 1);
1094
    }
1095

1096
    #[test]
1097
    fn test_row_cnt() {
1098
        let mut tx = PgsqlTransaction::new();
1099
        assert_eq!(tx.get_row_cnt(), 0);
1100

1101
        tx.incr_row_cnt();
1102
        assert_eq!(tx.get_row_cnt(), 1);
1103
    }
1104
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc