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

OISF / suricata / 22553492142

01 Mar 2026 09:48PM UTC coverage: 70.74% (-2.9%) from 73.687%
22553492142

Pull #14920

github

web-flow
Merge e15a765bc into 90823fa90
Pull Request #14920: draft: rust based configuration file parser and loader - v4

38209 of 77306 branches covered (49.43%)

Branch coverage included in aggregate %.

533 of 779 new or added lines in 5 files covered. (68.42%)

11924 existing lines in 491 files now uncovered.

252429 of 333548 relevant lines covered (75.68%)

2403268.06 hits per line

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

94.33
/rust/src/pgsql/parser.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 nom parsers
21

22
use crate::common::nom8::take_until_and_consume;
23
use nom8::branch::alt;
24
use nom8::bytes::streaming::{tag, take, take_until, take_until1};
25
use nom8::character::streaming::{alphanumeric1, char};
26
use nom8::combinator::{all_consuming, cond, eof, map_parser, opt, peek, verify};
27
use nom8::error::{make_error, ErrorKind, ParseError};
28
use nom8::multi::{many1, many_m_n, many_till};
29
use nom8::number::streaming::{be_i16, be_i32};
30
use nom8::number::streaming::{be_u16, be_u32, be_u8};
31
use nom8::sequence::terminated;
32
use nom8::{Err, IResult, Parser, ToUsize};
33

34
const PGSQL_LENGTH_FIELD: u32 = 4;
35

36
const PGSQL_DUMMY_PROTO_MAJOR: u16 = 1234; // 0x04d2
37
const PGSQL_DUMMY_PROTO_CANCEL_REQUEST: u16 = 5678; // 0x162e
38
const PGSQL_DUMMY_PROTO_MINOR_SSL: u16 = 5679; //0x162f
39
const _PGSQL_DUMMY_PROTO_MINOR_GSSAPI: u16 = 5680; // 0x1630
40

41
#[derive(Debug, PartialEq, Eq)]
42
pub(crate) enum PgsqlParseError<I> {
43
    InvalidLength,
44

45
    NomError(I, ErrorKind),
46
}
47

48
impl<I> ParseError<I> for PgsqlParseError<I> {
49
    fn from_error_kind(input: I, kind: ErrorKind) -> Self {
203✔
50
        PgsqlParseError::NomError(input, kind)
203✔
51
    }
203✔
52

53
    fn append(input: I, kind: ErrorKind, _other: Self) -> Self {
5✔
54
        PgsqlParseError::NomError(input, kind)
5✔
55
    }
5✔
56
}
57

58
fn parse_gte_length(i: &[u8], expected_length: u32) -> IResult<&[u8], u32, PgsqlParseError<&[u8]>> {
5,961✔
59
    let res = verify(be_u32::<&[u8], nom8::error::Error<_>>, |&x| {
5,961✔
60
        x >= expected_length
5,951✔
61
    }).parse(i);
5,961✔
62
    match res {
13✔
63
        Ok(result) => Ok((result.0, result.1)),
5,948✔
64
        Err(nom8::Err::Incomplete(needed)) => Err(Err::Incomplete(needed)),
10✔
65
        Err(_) => Err(Err::Error(PgsqlParseError::InvalidLength)),
3✔
66
    }
67
}
5,961✔
68

69
fn parse_exact_length(
132✔
70
    i: &[u8], expected_length: u32,
132✔
71
) -> IResult<&[u8], u32, PgsqlParseError<&[u8]>> {
132✔
72
    let res = verify(be_u32::<&[u8], nom8::error::Error<_>>, |&x| {
132✔
73
        x == expected_length
131✔
74
    }).parse(i);
132✔
75
    match res {
1✔
76
        Ok(result) => Ok((result.0, result.1)),
131✔
77
        Err(nom8::Err::Incomplete(needed)) => Err(Err::Incomplete(needed)),
1✔
UNCOV
78
        Err(_) => Err(Err::Error(PgsqlParseError::InvalidLength)),
×
79
    }
80
}
132✔
81

82
#[derive(Debug, PartialEq, Eq)]
83
pub(crate) enum PgsqlParameters {
84
    // startup parameters
85
    User,
86
    Database,
87
    Options,
88
    Replication,
89
    // runtime parameters
90
    ServerVersion,
91
    ServerEncoding,
92
    ClientEncoding,
93
    ApplicationName,
94
    DefaultTransactionReadOnly,
95
    InHotStandby,
96
    IsSuperuser,
97
    SessionAuthorization,
98
    DateStyle,
99
    IntervalStyle,
100
    TimeZone,
101
    IntegerDatetimes,
102
    StandardConformingStrings,
103
    UnknownParameter(Vec<u8>),
104
}
105

106
impl PgsqlParameters {
107
    pub(crate) fn to_str(&self) -> &str {
242✔
108
        match self {
242✔
109
            PgsqlParameters::User => "user",
×
110
            PgsqlParameters::Database => "database",
23✔
111
            PgsqlParameters::Options => "options",
×
112
            PgsqlParameters::Replication => "replication",
1✔
113
            PgsqlParameters::ServerVersion => "server_version",
16✔
114
            PgsqlParameters::ServerEncoding => "server_encoding",
15✔
115
            PgsqlParameters::ClientEncoding => "client_encoding",
37✔
116
            PgsqlParameters::ApplicationName => "application_name",
36✔
117
            PgsqlParameters::DefaultTransactionReadOnly => "default_transaction_read_only",
1✔
118
            PgsqlParameters::InHotStandby => "in_hot_standby",
1✔
119
            PgsqlParameters::IsSuperuser => "is_superuser",
16✔
120
            PgsqlParameters::SessionAuthorization => "session_authorization",
16✔
121
            PgsqlParameters::DateStyle => "date_style",
17✔
122
            PgsqlParameters::IntervalStyle => "interval_style",
15✔
123
            PgsqlParameters::TimeZone => "time_zone",
16✔
124
            PgsqlParameters::IntegerDatetimes => "integer_datetimes",
15✔
125
            PgsqlParameters::StandardConformingStrings => "standard_conforming_strings",
15✔
126
            PgsqlParameters::UnknownParameter(name) => {
2✔
127
                std::str::from_utf8(name).unwrap_or("unknown_parameter")
2✔
128
            }
129
        }
130
    }
242✔
131
}
132

133
impl From<&[u8]> for PgsqlParameters {
134
    fn from(name: &[u8]) -> Self {
291✔
135
        match name {
291✔
136
            br#"user"# => PgsqlParameters::User,
291✔
137
            br#"database"# => PgsqlParameters::Database,
261✔
138
            br#"options"# => PgsqlParameters::Options,
216✔
139
            br#"replication"# => PgsqlParameters::Replication,
216✔
140
            br#"server_version"# => PgsqlParameters::ServerVersion,
213✔
141
            br#"server_encoding"# => PgsqlParameters::ServerEncoding,
196✔
142
            br#"client_encoding"# => PgsqlParameters::ClientEncoding,
39✔
143
            br#"application_name"# => PgsqlParameters::ApplicationName,
142✔
144
            br#"default_transaction_read_only"# => PgsqlParameters::DefaultTransactionReadOnly,
101✔
145
            br#"in_hot_standby"# => PgsqlParameters::InHotStandby,
1✔
146
            br#"is_superuser"# => PgsqlParameters::IsSuperuser,
100✔
147
            br#"session_authorization"# => PgsqlParameters::SessionAuthorization,
84✔
148
            br#"DateStyle"# => PgsqlParameters::DateStyle,
68✔
149
            br#"IntervalStyle"# => PgsqlParameters::IntervalStyle,
48✔
150
            br#"TimeZone"# => PgsqlParameters::TimeZone,
17✔
151
            br#"integer_datetimes"# => PgsqlParameters::IntegerDatetimes,
33✔
152
            br#"standard_conforming_strings"# => PgsqlParameters::StandardConformingStrings,
17✔
153
            _ => PgsqlParameters::UnknownParameter(name.to_vec()),
4✔
154
        }
155
    }
291✔
156
}
157

158
#[derive(Debug, PartialEq, Eq)]
159
pub(crate) struct PgsqlParameter {
160
    pub name: PgsqlParameters,
161
    pub value: Vec<u8>,
162
}
163

164
#[derive(Debug, PartialEq, Eq)]
165
pub(crate) struct PgsqlStartupParameters {
166
    pub user: PgsqlParameter,
167
    pub optional_params: Option<Vec<PgsqlParameter>>,
168
}
169

170
#[derive(Debug, PartialEq, Eq)]
171
pub(crate) struct DummyStartupPacket {
172
    length: u32,
173
    proto_major: u16,
174
    proto_minor: u16,
175
}
176

177
#[derive(Debug, PartialEq, Eq)]
178
pub(crate) struct StartupPacket {
179
    pub length: u32,
180
    pub proto_major: u16,
181
    pub proto_minor: u16,
182
    pub params: PgsqlStartupParameters,
183
}
184

185
#[derive(Debug, PartialEq, Eq)]
186
pub(crate) struct RegularPacket {
187
    pub identifier: u8,
188
    pub length: u32,
189
    pub payload: Vec<u8>,
190
}
191

192
#[derive(Debug, PartialEq, Eq)]
193
pub(crate) struct PgsqlErrorNoticeMessageField {
194
    pub field_type: PgsqlErrorNoticeFieldType,
195
    pub field_value: Vec<u8>,
196
}
197

198
#[derive(Debug, PartialEq, Eq)]
199
pub(crate) struct ErrorNoticeMessage {
200
    pub identifier: u8,
201
    pub length: u32,
202
    pub message_body: Vec<PgsqlErrorNoticeMessageField>,
203
}
204

205
#[derive(Debug, PartialEq, Eq)]
206
pub(crate) enum SSLResponseMessage {
207
    SSLAccepted,
208
    SSLRejected,
209
    InvalidResponse,
210
}
211

212
impl From<u8> for SSLResponseMessage {
213
    fn from(identifier: u8) -> Self {
×
214
        match identifier {
×
215
            b'S' => Self::SSLAccepted,
×
216
            b'N' => Self::SSLRejected,
×
217
            _ => Self::InvalidResponse,
×
218
        }
219
    }
×
220
}
221

222
impl From<char> for SSLResponseMessage {
223
    fn from(identifier: char) -> Self {
45✔
224
        match identifier {
45✔
225
            'S' => Self::SSLAccepted,
3✔
226
            'N' => Self::SSLRejected,
42✔
227
            _ => Self::InvalidResponse,
×
228
        }
229
    }
45✔
230
}
231

232
#[derive(Debug, PartialEq, Eq)]
233
pub(crate) struct ParameterStatusMessage {
234
    pub identifier: u8,
235
    pub length: u32,
236
    pub param: PgsqlParameter,
237
}
238

239
#[derive(Debug, PartialEq, Eq)]
240
pub(crate) struct BackendKeyDataMessage {
241
    pub identifier: u8,
242
    pub length: u32,
243
    pub backend_pid: u32,
244
    pub secret_key: u32,
245
}
246

247
#[derive(Debug, PartialEq, Eq)]
248
pub(crate) struct ConsolidatedDataRowPacket {
249
    pub identifier: u8,
250
    pub row_cnt: u64, // row or msg cnt
251
    pub data_size: u64,
252
}
253

254
#[derive(Debug, PartialEq, Eq)]
255
pub(crate) struct ReadyForQueryMessage {
256
    pub identifier: u8,
257
    pub length: u32,
258
    pub transaction_status: u8,
259
}
260

261
#[derive(Debug, PartialEq, Eq)]
262
pub(crate) struct NotificationResponse {
263
    pub identifier: u8,
264
    pub length: u32,
265
    pub pid: u32,
266
    // two str fields, one right after the other
267
    pub channel_name: Vec<u8>,
268
    pub payload: Vec<u8>,
269
}
270

271
#[derive(Debug, PartialEq, Eq)]
272
pub(crate) struct CopyResponse {
273
    pub identifier: u8,
274
    pub length: u32,
275
    pub column_cnt: u16,
276
    // for each column, there are column_cnt u16 format codes received
277
    // for now, we're not storing those
278
}
279

280
#[derive(Debug, PartialEq, Eq)]
281
pub(crate) struct NoPayloadMessage {
282
    pub identifier: u8,
283
    pub length: u32,
284
}
285

286
#[derive(Debug, PartialEq, Eq)]
287
pub(crate) enum PgsqlBEMessage {
288
    SSLResponse(SSLResponseMessage),
289
    ErrorResponse(ErrorNoticeMessage),
290
    NoticeResponse(ErrorNoticeMessage),
291
    AuthenticationOk(AuthenticationMessage),
292
    AuthenticationCleartextPassword(AuthenticationMessage),
293
    AuthenticationMD5Password(AuthenticationMessage),
294
    AuthenticationSSPI(AuthenticationMessage),
295
    AuthenticationSASL(AuthenticationSASLMechanismMessage),
296
    AuthenticationSASLContinue(AuthenticationMessage),
297
    AuthenticationSASLFinal(AuthenticationMessage),
298
    ParameterStatus(ParameterStatusMessage),
299
    BackendKeyData(BackendKeyDataMessage),
300
    CommandComplete(RegularPacket),
301
    CopyOutResponse(CopyResponse),
302
    CopyInResponse(CopyResponse),
303
    ConsolidatedCopyDataOut(ConsolidatedDataRowPacket),
304
    CopyDone(NoPayloadMessage),
305
    ReadyForQuery(ReadyForQueryMessage),
306
    RowDescription(RowDescriptionMessage),
307
    ConsolidatedDataRow(ConsolidatedDataRowPacket),
308
    NotificationResponse(NotificationResponse),
309
    UnknownMessageType(RegularPacket),
310
}
311

312
impl PgsqlBEMessage {
313
    pub(crate) fn to_str(&self) -> &'static str {
120✔
314
        match self {
×
315
            PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLAccepted) => "ssl_accepted",
×
316
            PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLRejected) => "ssl_rejected",
×
317
            PgsqlBEMessage::ErrorResponse(_) => "error_response",
×
318
            PgsqlBEMessage::NoticeResponse(_) => "notice_response",
×
319
            PgsqlBEMessage::AuthenticationOk(_) => "authentication_ok",
16✔
320
            PgsqlBEMessage::AuthenticationCleartextPassword(_) => {
321
                "authentication_cleartext_password"
1✔
322
            }
323
            PgsqlBEMessage::AuthenticationMD5Password(_) => "authentication_md5_password",
16✔
324
            PgsqlBEMessage::AuthenticationSSPI(_) => "authentication_sspi",
×
325
            PgsqlBEMessage::AuthenticationSASL(_) => "authentication_sasl",
3✔
326
            PgsqlBEMessage::AuthenticationSASLContinue(_) => "authentication_sasl_continue",
3✔
327
            PgsqlBEMessage::AuthenticationSASLFinal(_) => "authentication_sasl_final",
3✔
328
            PgsqlBEMessage::ParameterStatus(_) => "parameter_status",
×
329
            PgsqlBEMessage::BackendKeyData(_) => "backend_key_data",
×
330
            PgsqlBEMessage::CommandComplete(_) => "command_completed",
74✔
331
            PgsqlBEMessage::CopyOutResponse(_) => "copy_out_response",
1✔
332
            PgsqlBEMessage::CopyInResponse(_) => "copy_in_response",
1✔
333
            PgsqlBEMessage::ConsolidatedCopyDataOut(_) => "copy_data_out",
1✔
334
            PgsqlBEMessage::CopyDone(_) => "copy_done",
1✔
335
            PgsqlBEMessage::ReadyForQuery(_) => "ready_for_query",
×
336
            PgsqlBEMessage::RowDescription(_) => "row_description",
×
337
            PgsqlBEMessage::SSLResponse(SSLResponseMessage::InvalidResponse) => {
338
                "invalid_be_message"
×
339
            }
340
            PgsqlBEMessage::ConsolidatedDataRow(_) => "data_row",
×
341
            PgsqlBEMessage::NotificationResponse(_) => "notification_response",
×
342
            PgsqlBEMessage::UnknownMessageType(_) => "unknown_message_type",
×
343
        }
344
    }
120✔
345

346
    pub fn get_backendkey_info(&self) -> (u32, u32) {
16✔
347
        match self {
16✔
348
            PgsqlBEMessage::BackendKeyData(message) => {
16✔
349
                return (message.backend_pid, message.secret_key);
16✔
350
            }
351
            _ => (0, 0),
×
352
        }
353
    }
16✔
354
}
355

356
#[derive(Debug, PartialEq, Eq, Clone)]
357
pub(crate) enum SASLAuthenticationMechanism {
358
    ScramSha256,
359
    ScramSha256Plus,
360
    // UnknownMechanism,
361
}
362

363
impl SASLAuthenticationMechanism {
364
    pub(crate) fn to_str(&self) -> &'static str {
3✔
365
        match self {
3✔
366
            SASLAuthenticationMechanism::ScramSha256 => "scram_SHA256",
3✔
367
            SASLAuthenticationMechanism::ScramSha256Plus => "scram_SHA256_plus",
×
368
        }
369
    }
3✔
370
}
371

372
type SASLInitialResponse = (SASLAuthenticationMechanism, u32, Vec<u8>);
373

374
#[derive(Debug, PartialEq, Eq)]
375
pub(crate) struct CancelRequestMessage {
376
    pub pid: u32,
377
    pub backend_key: u32,
378
}
379

380
#[derive(Debug, PartialEq, Eq)]
381
pub(crate) enum PgsqlFEMessage {
382
    SSLRequest(DummyStartupPacket),
383
    StartupMessage(StartupPacket),
384
    PasswordMessage(RegularPacket),
385
    SASLInitialResponse(SASLInitialResponsePacket),
386
    SASLResponse(RegularPacket),
387
    SimpleQuery(RegularPacket),
388
    ConsolidatedCopyDataIn(ConsolidatedDataRowPacket),
389
    CopyDone(NoPayloadMessage),
390
    CopyFail(RegularPacket),
391
    CancelRequest(CancelRequestMessage),
392
    Terminate(NoPayloadMessage),
393
    UnknownMessageType(RegularPacket),
394
}
395

396
impl PgsqlFEMessage {
397
    pub(crate) fn to_str(&self) -> &'static str {
117✔
398
        match self {
117✔
399
            PgsqlFEMessage::StartupMessage(_) => "startup_message",
×
400
            PgsqlFEMessage::SSLRequest(_) => "ssl_request",
×
401
            PgsqlFEMessage::PasswordMessage(_) => "password",
4✔
402
            PgsqlFEMessage::SASLInitialResponse(_) => "sasl_initial_response",
×
403
            PgsqlFEMessage::SASLResponse(_) => "sasl_response",
×
404
            PgsqlFEMessage::SimpleQuery(_) => "simple_query",
95✔
405
            PgsqlFEMessage::ConsolidatedCopyDataIn(_) => "copy_data_in",
1✔
406
            PgsqlFEMessage::CopyDone(_) => "copy_done",
1✔
407
            PgsqlFEMessage::CopyFail(_) => "copy_fail",
×
408
            PgsqlFEMessage::CancelRequest(_) => "cancel_request",
×
409
            PgsqlFEMessage::Terminate(_) => "termination_message",
16✔
410
            PgsqlFEMessage::UnknownMessageType(_) => "unknown_message_type",
×
411
        }
412
    }
117✔
413
}
414

415
#[derive(Debug, PartialEq, Eq)]
416
pub(crate) struct AuthenticationMessage {
417
    pub identifier: u8,
418
    pub length: u32,
419
    pub auth_type: u32,
420
    pub payload: Vec<u8>,
421
}
422

423
#[derive(Debug, PartialEq, Eq)]
424
pub(crate) struct SASLInitialResponsePacket {
425
    pub identifier: u8,
426
    pub length: u32,
427
    pub auth_mechanism: SASLAuthenticationMechanism,
428
    pub param_length: u32,
429
    pub sasl_param: Vec<u8>,
430
}
431

432
#[derive(Debug, PartialEq, Eq)]
433
pub(crate) struct AuthenticationSASLMechanismMessage {
434
    identifier: u8,
435
    length: u32,
436
    auth_type: u32,
437
    auth_mechanisms: Vec<SASLAuthenticationMechanism>,
438
}
439

440
#[derive(Debug, PartialEq, Eq)]
441
pub(crate) struct RowField {
442
    pub field_name: Vec<u8>,
443
    pub table_oid: u32,
444
    pub column_index: u16,
445
    pub data_type_oid: u32,
446
    // "see pg_type.typlen. Note that negative values denote variable-width types"
447
    pub data_type_size: i16,
448
    // "The value will generally be -1 for types that do not need pg_attribute.atttypmod."
449
    pub type_modifier: i32,
450
    // "The format code being used for the field. Currently will be zero (text) or one (binary). In a RowDescription returned from the variant of Describe, will always be zero"
451
    pub format_code: u16,
452
}
453

454
#[derive(Debug, PartialEq, Eq)]
455
pub(crate) struct RowDescriptionMessage {
456
    pub identifier: u8,
457
    pub length: u32,
458
    pub field_count: u16,
459
    pub fields: Vec<RowField>,
460
}
461

462
#[derive(Debug, PartialEq, Eq)]
463
pub(crate) struct ColumnFieldValue {
464
    // Can be 0, or -1 as a special NULL column value
465
    pub value_length: i32,
466
    pub value: Vec<u8>,
467
}
468

469
#[derive(Debug, PartialEq, Eq)]
470
pub(crate) enum PgsqlErrorNoticeFieldType {
471
    SeverityLocalizable,
472
    SeverityNonLocalizable,
473
    CodeSqlStateCode,
474
    Message,
475
    Detail,
476
    Hint,
477
    Position,
478
    InternalPosition,
479
    InternalQuery,
480
    Where,
481
    SchemaName,
482
    TableName,
483
    ColumnName,
484
    DataType,
485
    ConstraintName,
486
    File,
487
    Line,
488
    Routine,
489
    // Indicates end of message
490
    TerminatorToken,
491
    // From the documentation: "Since more field types might be added in future, frontends should silently ignore fields of unrecognized type." For us, then, I think the best option is actually to print it as we parse it, so it is readable?
492
    UnknownFieldType,
493
}
494

495
impl PgsqlErrorNoticeFieldType {
496
    pub(crate) fn to_str(&self) -> &'static str {
146✔
497
        match self {
146✔
498
            PgsqlErrorNoticeFieldType::SeverityLocalizable => "severity_localizable",
21✔
499
            PgsqlErrorNoticeFieldType::SeverityNonLocalizable => "severity_non_localizable",
20✔
500
            PgsqlErrorNoticeFieldType::CodeSqlStateCode => "code",
21✔
501
            PgsqlErrorNoticeFieldType::Message => "message",
21✔
502
            PgsqlErrorNoticeFieldType::Detail => "detail",
×
503
            PgsqlErrorNoticeFieldType::Hint => "hint",
×
504
            PgsqlErrorNoticeFieldType::Position => "position",
×
505
            PgsqlErrorNoticeFieldType::InternalPosition => "internal_position",
×
506
            PgsqlErrorNoticeFieldType::InternalQuery => "internal_query",
×
507
            PgsqlErrorNoticeFieldType::Where => "where",
×
508
            PgsqlErrorNoticeFieldType::SchemaName => "schema_name",
×
509
            PgsqlErrorNoticeFieldType::TableName => "table_name",
×
510
            PgsqlErrorNoticeFieldType::ColumnName => "column_name",
×
511
            PgsqlErrorNoticeFieldType::DataType => "data_type",
×
512
            PgsqlErrorNoticeFieldType::ConstraintName => "constraint_name",
×
513
            PgsqlErrorNoticeFieldType::File => "file",
21✔
514
            PgsqlErrorNoticeFieldType::Line => "line",
21✔
515
            PgsqlErrorNoticeFieldType::Routine => "routine",
21✔
516
            PgsqlErrorNoticeFieldType::TerminatorToken => "",
×
517
            PgsqlErrorNoticeFieldType::UnknownFieldType => "unknown_field_type",
×
518
        }
519
    }
146✔
520
}
521

522
impl From<char> for PgsqlErrorNoticeFieldType {
523
    fn from(identifier: char) -> PgsqlErrorNoticeFieldType {
18✔
524
        match identifier {
18✔
525
            'S' => PgsqlErrorNoticeFieldType::SeverityLocalizable,
×
526
            'V' => PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
18✔
527
            'C' => PgsqlErrorNoticeFieldType::CodeSqlStateCode,
×
528
            'M' => PgsqlErrorNoticeFieldType::Message,
×
529
            'D' => PgsqlErrorNoticeFieldType::Detail,
×
530
            'H' => PgsqlErrorNoticeFieldType::Hint,
×
531
            'P' => PgsqlErrorNoticeFieldType::Position,
×
532
            'p' => PgsqlErrorNoticeFieldType::InternalPosition,
×
533
            'q' => PgsqlErrorNoticeFieldType::InternalQuery,
×
534
            'W' => PgsqlErrorNoticeFieldType::Where,
×
535
            's' => PgsqlErrorNoticeFieldType::SchemaName,
×
536
            't' => PgsqlErrorNoticeFieldType::TableName,
×
537
            'c' => PgsqlErrorNoticeFieldType::ColumnName,
×
538
            'd' => PgsqlErrorNoticeFieldType::DataType,
×
539
            'n' => PgsqlErrorNoticeFieldType::ConstraintName,
×
540
            'F' => PgsqlErrorNoticeFieldType::File,
×
541
            'L' => PgsqlErrorNoticeFieldType::Line,
×
542
            'R' => PgsqlErrorNoticeFieldType::Routine,
×
543
            '\u{0}' => PgsqlErrorNoticeFieldType::TerminatorToken,
×
544
            // Pgsql documentation says "frontends should silently ignore fields of unrecognized type."
545
            _ => PgsqlErrorNoticeFieldType::UnknownFieldType,
×
546
        }
547
    }
18✔
548
}
549

550
impl From<u8> for PgsqlErrorNoticeFieldType {
551
    fn from(identifier: u8) -> PgsqlErrorNoticeFieldType {
111✔
552
        match identifier {
111✔
553
            b'S' => PgsqlErrorNoticeFieldType::SeverityLocalizable,
24✔
554
            b'V' => PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
×
555
            b'C' => PgsqlErrorNoticeFieldType::CodeSqlStateCode,
×
556
            b'M' => PgsqlErrorNoticeFieldType::Message,
23✔
557
            b'D' => PgsqlErrorNoticeFieldType::Detail,
1✔
UNCOV
558
            b'H' => PgsqlErrorNoticeFieldType::Hint,
×
UNCOV
559
            b'P' => PgsqlErrorNoticeFieldType::Position,
×
UNCOV
560
            b'p' => PgsqlErrorNoticeFieldType::InternalPosition,
×
UNCOV
561
            b'q' => PgsqlErrorNoticeFieldType::InternalQuery,
×
UNCOV
562
            b'W' => PgsqlErrorNoticeFieldType::Where,
×
UNCOV
563
            b's' => PgsqlErrorNoticeFieldType::SchemaName,
×
UNCOV
564
            b't' => PgsqlErrorNoticeFieldType::TableName,
×
UNCOV
565
            b'c' => PgsqlErrorNoticeFieldType::ColumnName,
×
UNCOV
566
            b'd' => PgsqlErrorNoticeFieldType::DataType,
×
UNCOV
567
            b'n' => PgsqlErrorNoticeFieldType::ConstraintName,
×
568
            b'F' => PgsqlErrorNoticeFieldType::File,
21✔
569
            b'L' => PgsqlErrorNoticeFieldType::Line,
21✔
570
            b'R' => PgsqlErrorNoticeFieldType::Routine,
21✔
571
            b'\0' => PgsqlErrorNoticeFieldType::TerminatorToken,
×
572
            // Pgsql documentation says "frontends should silently ignore fields of unrecognized type."
UNCOV
573
            _ => PgsqlErrorNoticeFieldType::UnknownFieldType,
×
574
        }
575
    }
111✔
576
}
577

578
// Currently the set of parameters that could trigger a ParameterStatus message is fixed:
579
// server_version
580
// server_encoding
581
// client_encoding
582
// application_name
583
// default_transaction_read_only
584
// in_hot_standby
585
// is_superuser
586
// session_authorization
587
// DateStyle
588
// IntervalStyle
589
// TimeZone
590
// integer_datetimes
591
// standard_conforming_strings
592
// (source: PostgreSQL documentation)
593
// We may be interested, then, in controling this, somehow, to prevent weird things?
594
fn pgsql_parse_generic_parameter(
321✔
595
    i: &[u8],
321✔
596
) -> IResult<&[u8], PgsqlParameter, PgsqlParseError<&[u8]>> {
321✔
597
    let (i, param_name) = take_until1(b"\x00".as_ref()).parse(i)?;
321✔
598
    let (i, _) = tag(b"\x00".as_ref()).parse(i)?;
291✔
599
    let (i, param_value) = take_until(b"\x00".as_ref()).parse(i)?;
291✔
600
    let (i, _) = tag(b"\x00".as_ref()).parse(i)?;
291✔
601
    Ok((
291✔
602
        i,
291✔
603
        PgsqlParameter {
291✔
604
            name: PgsqlParameters::from(param_name),
291✔
605
            value: param_value.to_vec(),
291✔
606
        },
291✔
607
    ))
291✔
608
}
321✔
609

610
fn pgsql_parse_startup_parameters(
30✔
611
    i: &[u8],
30✔
612
) -> IResult<&[u8], PgsqlStartupParameters, PgsqlParseError<&[u8]>> {
30✔
613
    let (i, mut optional) = opt(terminated(
30✔
614
        many1(pgsql_parse_generic_parameter),
30✔
615
        tag(b"\x00".as_ref()),
30✔
616
    )).parse(i)?;
30✔
617
    if let Some(ref mut params) = optional {
29✔
618
        let mut user = PgsqlParameter {
29✔
619
            name: PgsqlParameters::User,
29✔
620
            value: Vec::new(),
29✔
621
        };
29✔
622
        let mut index: usize = 0;
29✔
623
        for (j, p) in params.iter().enumerate() {
114✔
624
            if p.name == PgsqlParameters::User {
114✔
625
                user.value.extend_from_slice(&p.value);
29✔
626
                index = j;
29✔
627
            }
85✔
628
        }
629
        params.remove(index);
29✔
630
        if user.value.is_empty() {
29✔
UNCOV
631
            return Err(Err::Error(make_error(i, ErrorKind::Tag)));
×
632
        }
29✔
633
        return Ok((
29✔
634
            i,
29✔
635
            PgsqlStartupParameters {
29✔
636
                user,
29✔
637
                optional_params: if !params.is_empty() { optional } else { None },
29✔
638
            },
639
        ));
UNCOV
640
    }
×
UNCOV
641
    return Err(Err::Error(make_error(i, ErrorKind::Tag)));
×
642
}
30✔
643

644
fn parse_sasl_initial_response_payload(
4✔
645
    i: &[u8],
4✔
646
) -> IResult<&[u8], SASLInitialResponse, PgsqlParseError<&[u8]>> {
4✔
647
    let (i, sasl_mechanism) = parse_sasl_mechanism(i)?;
4✔
648
    let (i, param_length) = be_u32.parse(i)?;
4✔
649
    // From RFC 5802 - the client-first-message will always start w/
650
    // 'n', 'y' or 'p', otherwise it's invalid, I think we should check that, at some point
651
    let (i, param) = terminated(take(param_length), eof).parse(i)?;
4✔
652
    Ok((i, (sasl_mechanism, param_length, param.to_vec())))
4✔
653
}
4✔
654

655
pub(crate) fn parse_sasl_initial_response(
4✔
656
    i: &[u8],
4✔
657
) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
4✔
658
    let (i, identifier) = verify(be_u8, |&x| x == b'p').parse(i)?;
4✔
659
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
4✔
660
    let (i, payload) = map_parser(
4✔
661
        take(length - PGSQL_LENGTH_FIELD),
4✔
662
        parse_sasl_initial_response_payload,
4✔
663
    ).parse(i)?;
4✔
664
    Ok((
4✔
665
        i,
4✔
666
        PgsqlFEMessage::SASLInitialResponse(SASLInitialResponsePacket {
4✔
667
            identifier,
4✔
668
            length,
4✔
669
            auth_mechanism: payload.0,
4✔
670
            param_length: payload.1,
4✔
671
            sasl_param: payload.2,
4✔
672
        }),
4✔
673
    ))
4✔
674
}
4✔
675

676
pub(crate) fn parse_sasl_response(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
4✔
677
    let (i, identifier) = verify(be_u8, |&x| x == b'p').parse(i)?;
4✔
678
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
4✔
679
    let (i, payload) = take(length - PGSQL_LENGTH_FIELD).parse(i)?;
4✔
680
    let resp = PgsqlFEMessage::SASLResponse(RegularPacket {
4✔
681
        identifier,
4✔
682
        length,
4✔
683
        payload: payload.to_vec(),
4✔
684
    });
4✔
685
    Ok((i, resp))
4✔
686
}
4✔
687

688
fn pgsql_parse_startup_packet(
90✔
689
    i: &[u8],
90✔
690
) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
90✔
691
    let (i, length) = parse_gte_length(i, 8)?;
90✔
692
    let (i, proto_major) = peek(be_u16).parse(i)?;
84✔
693
    let (i, b) = take(length - PGSQL_LENGTH_FIELD).parse(i)?;
84✔
694
    let (_, message) = match proto_major {
81✔
695
        1..=3 => {
81✔
696
            let (b, proto_major) = be_u16.parse(b)?;
30✔
697
            let (b, proto_minor) = be_u16.parse(b)?;
30✔
698
            let (b, params) = pgsql_parse_startup_parameters(b)?;
30✔
699
            (
29✔
700
                b,
29✔
701
                PgsqlFEMessage::StartupMessage(StartupPacket {
29✔
702
                    length,
29✔
703
                    proto_major,
29✔
704
                    proto_minor,
29✔
705
                    params,
29✔
706
                }),
29✔
707
            )
29✔
708
        }
709
        PGSQL_DUMMY_PROTO_MAJOR => {
710
            let (b, proto_major) = be_u16.parse(b)?;
48✔
711
            let (b, proto_minor) = be_u16.parse(b)?;
48✔
712
            let (b, message) = match proto_minor {
48✔
713
                PGSQL_DUMMY_PROTO_CANCEL_REQUEST => parse_cancel_request(b)?,
4✔
714
                PGSQL_DUMMY_PROTO_MINOR_SSL => (
44✔
715
                    b,
44✔
716
                    PgsqlFEMessage::SSLRequest(DummyStartupPacket {
44✔
717
                        length,
44✔
718
                        proto_major,
44✔
719
                        proto_minor,
44✔
720
                    }),
44✔
721
                ),
44✔
UNCOV
722
                _ => return Err(Err::Error(make_error(b, ErrorKind::Switch))),
×
723
            };
724

725
            (b, message)
48✔
726
        }
727
        _ => return Err(Err::Error(make_error(b, ErrorKind::Switch))),
3✔
728
    };
729
    Ok((i, message))
77✔
730
}
90✔
731

732
// TODO Decide if it's a good idea to offer GSS encryption support right now, as the documentation seems to have conflicting information...
733
// If we do:
734
// To initiate a GSSAPI-encrypted connection, the frontend initially sends a GSSENCRequest message rather than a
735
// StartupMessage. The server then responds with a single byte containing G or N, indicating that it is willing or unwilling to perform GSSAPI encryption, respectively. The frontend might close the connection at this point if it is
736
// dissatisfied with the response. To continue after G, using the GSSAPI C bindings as discussed in RFC2744 or equivalent,
737
// perform a GSSAPI initialization by calling gss_init_sec_context() in a loop and sending the result to the server,
738
// starting with an empty input and then with each result from the server, until it returns no output. When sending the
739
// results of gss_init_sec_context() to the server, prepend the length of the message as a four byte integer in network
740
// byte order. To continue after N, send the usual StartupMessage and proceed without encryption. (Alternatively, it is
741
// permissible to issue an SSLRequest message after an N response to try to use SSL encryption instead of GSSAPI.)
742
// Source: https://www.postgresql.org/docs/13/protocol-flow.html#id-1.10.5.7.11, GSSAPI Session Encryption
743

744
// Password can be encrypted or in cleartext
745
pub(crate) fn parse_password_message(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
15✔
746
    let (i, identifier) = verify(be_u8, |&x| x == b'p').parse(i)?;
15✔
747
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
15✔
748
    let (i, password) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until1(b"\x00".as_ref())).parse(i)?;
15✔
749
    Ok((
12✔
750
        i,
12✔
751
        PgsqlFEMessage::PasswordMessage(RegularPacket {
12✔
752
            identifier,
12✔
753
            length,
12✔
754
            payload: password.to_vec(),
12✔
755
        }),
12✔
756
    ))
12✔
757
}
15✔
758

759
fn parse_simple_query(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
78✔
760
    let (i, identifier) = verify(be_u8, |&x| x == b'Q').parse(i)?;
78✔
761
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
78✔
762
    let (i, query) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until1(b"\x00".as_ref())).parse(i)?;
78✔
763
    Ok((
78✔
764
        i,
78✔
765
        PgsqlFEMessage::SimpleQuery(RegularPacket {
78✔
766
            identifier,
78✔
767
            length,
78✔
768
            payload: query.to_vec(),
78✔
769
        }),
78✔
770
    ))
78✔
771
}
78✔
772

773
fn parse_cancel_request(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
6✔
774
    let (i, pid) = be_u32.parse(i)?;
6✔
775
    let (i, backend_key) = be_u32.parse(i)?;
5✔
776
    Ok((
5✔
777
        i,
5✔
778
        PgsqlFEMessage::CancelRequest(CancelRequestMessage { pid, backend_key }),
5✔
779
    ))
5✔
780
}
6✔
781

782
fn parse_terminate_message(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
17✔
783
    let (i, identifier) = verify(be_u8, |&x| x == b'X').parse(i)?;
17✔
784
    let (i, length) = parse_exact_length(i, PGSQL_LENGTH_FIELD)?;
17✔
785
    Ok((
16✔
786
        i,
16✔
787
        PgsqlFEMessage::Terminate(NoPayloadMessage { identifier, length }),
16✔
788
    ))
16✔
789
}
17✔
790

791
// Messages that begin with 'p' but are not password ones are not parsed here
792
pub(crate) fn parse_request(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
189✔
793
    let (i, tag) = peek(be_u8).parse(i)?;
189✔
794
    let (i, message) = match tag {
189✔
795
        b'\0' => pgsql_parse_startup_packet(i)?,
88✔
796
        b'Q' => parse_simple_query(i)?,
78✔
797
        b'X' => parse_terminate_message(i)?,
17✔
798
        b'd' => parse_consolidated_copy_data_in(i)?,
1✔
799
        b'c' => parse_copy_in_done(i)?,
1✔
UNCOV
800
        b'f' => parse_copy_fail(i)?,
×
801
        _ => {
802
            let (i, identifier) = be_u8.parse(i)?;
4✔
803
            let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
4✔
804
            let (i, payload) = take(length - PGSQL_LENGTH_FIELD).parse(i)?;
4✔
805
            let unknown = PgsqlFEMessage::UnknownMessageType(RegularPacket {
4✔
806
                identifier,
4✔
807
                length,
4✔
808
                payload: payload.to_vec(),
4✔
809
            });
4✔
810
            SCLogDebug!("parsed unknown FE message, identifier is {:?}", identifier);
4✔
811
            (i, unknown)
4✔
812
        }
813
    };
814
    Ok((i, message))
176✔
815
}
189✔
816

817
fn pgsql_parse_authentication_message<'a>(
61✔
818
    i: &'a [u8],
61✔
819
) -> IResult<&'a [u8], PgsqlBEMessage, PgsqlParseError<&'a [u8]>> {
61✔
820
    let (i, identifier) = verify(be_u8, |&x| x == b'R').parse(i)?;
61✔
821
    let (i, length) = parse_gte_length(i, 8)?;
61✔
822
    let (i, auth_type) = be_u32.parse(i)?;
60✔
823
    let (i, message) = map_parser(take(length - 8), |b: &'a [u8]| {
59✔
824
        match auth_type {
56✔
825
            0 => Ok((
20✔
826
                b,
20✔
827
                PgsqlBEMessage::AuthenticationOk(AuthenticationMessage {
20✔
828
                    identifier,
20✔
829
                    length,
20✔
830
                    auth_type,
20✔
831
                    payload: b.to_vec(),
20✔
832
                }),
20✔
833
            )),
20✔
834
            3 => Ok((
3✔
835
                b,
3✔
836
                PgsqlBEMessage::AuthenticationCleartextPassword(AuthenticationMessage {
3✔
837
                    identifier,
3✔
838
                    length,
3✔
839
                    auth_type,
3✔
840
                    payload: b.to_vec(),
3✔
841
                }),
3✔
842
            )),
3✔
843
            5 => {
844
                let (b, salt) = all_consuming(take(4_usize)).parse(b)?;
18✔
845
                Ok((
18✔
846
                    b,
18✔
847
                    PgsqlBEMessage::AuthenticationMD5Password(AuthenticationMessage {
18✔
848
                        identifier,
18✔
849
                        length,
18✔
850
                        auth_type,
18✔
851
                        payload: salt.to_vec(),
18✔
852
                    }),
18✔
853
                ))
18✔
854
            }
855
            9 => Ok((
1✔
856
                b,
1✔
857
                PgsqlBEMessage::AuthenticationSSPI(AuthenticationMessage {
1✔
858
                    identifier,
1✔
859
                    length,
1✔
860
                    auth_type,
1✔
861
                    payload: b.to_vec(),
1✔
862
                }),
1✔
863
            )),
1✔
864
            // TODO - For SASL, should we parse specific details of the challenge itself? (as seen in: https://github.com/launchbadge/sqlx/blob/master/sqlx-core/src/postgres/message/authentication.rs )
865
            10 => {
866
                let (b, auth_mechanisms) = parse_sasl_mechanisms(b)?;
5✔
867
                Ok((
5✔
868
                    b,
5✔
869
                    PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage {
5✔
870
                        identifier,
5✔
871
                        length,
5✔
872
                        auth_type,
5✔
873
                        auth_mechanisms,
5✔
874
                    }),
5✔
875
                ))
5✔
876
            }
877
            11 => Ok((
4✔
878
                b,
4✔
879
                PgsqlBEMessage::AuthenticationSASLContinue(AuthenticationMessage {
4✔
880
                    identifier,
4✔
881
                    length,
4✔
882
                    auth_type,
4✔
883
                    payload: b.to_vec(),
4✔
884
                }),
4✔
885
            )),
4✔
886
            12 => Ok((
5✔
887
                b,
5✔
888
                PgsqlBEMessage::AuthenticationSASLFinal(AuthenticationMessage {
5✔
889
                    identifier,
5✔
890
                    length,
5✔
891
                    auth_type,
5✔
892
                    payload: b.to_vec(),
5✔
893
                }),
5✔
894
            )),
5✔
895
            // TODO add other authentication messages
UNCOV
896
            _ => return Err(Err::Error(make_error(i, ErrorKind::Switch))),
×
897
        }
898
    }).parse(i)?;
59✔
899
    Ok((i, message))
56✔
900
}
61✔
901

902
fn parse_parameter_status_message(
176✔
903
    i: &[u8],
176✔
904
) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
176✔
905
    let (i, identifier) = verify(be_u8, |&x| x == b'S').parse(i)?;
176✔
906
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
176✔
907
    let (i, param) = map_parser(
176✔
908
        take(length - PGSQL_LENGTH_FIELD),
176✔
909
        pgsql_parse_generic_parameter,
176✔
910
    ).parse(i)?;
176✔
911
    Ok((
176✔
912
        i,
176✔
913
        PgsqlBEMessage::ParameterStatus(ParameterStatusMessage {
176✔
914
            identifier,
176✔
915
            length,
176✔
916
            param,
176✔
917
        }),
176✔
918
    ))
176✔
919
}
176✔
920

921
pub(crate) fn parse_ssl_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
50✔
922
    let (i, tag) = alt((char('N'), char('S'))).parse(i)?;
50✔
923
    Ok((
45✔
924
        i,
45✔
925
        PgsqlBEMessage::SSLResponse(SSLResponseMessage::from(tag)),
45✔
926
    ))
45✔
927
}
50✔
928

929
fn parse_backend_key_data_message(
17✔
930
    i: &[u8],
17✔
931
) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
17✔
932
    let (i, identifier) = verify(be_u8, |&x| x == b'K').parse(i)?;
17✔
933
    let (i, length) = parse_exact_length(i, 12)?;
17✔
934
    let (i, pid) = be_u32.parse(i)?;
17✔
935
    let (i, secret_key) = be_u32.parse(i)?;
17✔
936
    Ok((
17✔
937
        i,
17✔
938
        PgsqlBEMessage::BackendKeyData(BackendKeyDataMessage {
17✔
939
            identifier,
17✔
940
            length,
17✔
941
            backend_pid: pid,
17✔
942
            secret_key,
17✔
943
        }),
17✔
944
    ))
17✔
945
}
17✔
946

947
fn parse_command_complete(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
63✔
948
    let (i, identifier) = verify(be_u8, |&x| x == b'C').parse(i)?;
63✔
949
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
63✔
950
    let (i, payload) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until(b"\x00".as_ref())).parse(i)?;
63✔
951
    Ok((
63✔
952
        i,
63✔
953
        PgsqlBEMessage::CommandComplete(RegularPacket {
63✔
954
            identifier,
63✔
955
            length,
63✔
956
            payload: payload.to_vec(),
63✔
957
        }),
63✔
958
    ))
63✔
959
}
63✔
960

961
fn parse_ready_for_query(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
96✔
962
    let (i, identifier) = verify(be_u8, |&x| x == b'Z').parse(i)?;
96✔
963
    let (i, length) = parse_exact_length(i, 5)?;
96✔
964
    let (i, status) = verify(be_u8, |&x| x == b'I' || x == b'T' || x == b'E').parse(i)?;
96✔
965
    Ok((
96✔
966
        i,
96✔
967
        PgsqlBEMessage::ReadyForQuery(ReadyForQueryMessage {
96✔
968
            identifier,
96✔
969
            length,
96✔
970
            transaction_status: status,
96✔
971
        }),
96✔
972
    ))
96✔
973
}
96✔
974

975
fn parse_row_field(i: &[u8]) -> IResult<&[u8], RowField, PgsqlParseError<&[u8]>> {
69✔
976
    let (i, field_name) = take_until1(b"\x00".as_ref()).parse(i)?;
69✔
977
    let (i, _) = tag(b"\x00".as_ref()).parse(i)?;
69✔
978
    let (i, table_oid) = be_u32.parse(i)?;
69✔
979
    let (i, column_index) = be_u16.parse(i)?;
69✔
980
    let (i, data_type_oid) = be_u32.parse(i)?;
69✔
981
    let (i, data_type_size) = be_i16.parse(i)?;
69✔
982
    let (i, type_modifier) = be_i32.parse(i)?;
69✔
983
    let (i, format_code) = be_u16.parse(i)?;
69✔
984
    Ok((
69✔
985
        i,
69✔
986
        RowField {
69✔
987
            field_name: field_name.to_vec(),
69✔
988
            table_oid,
69✔
989
            column_index,
69✔
990
            data_type_oid,
69✔
991
            data_type_size,
69✔
992
            type_modifier,
69✔
993
            format_code,
69✔
994
        },
69✔
995
    ))
69✔
996
}
69✔
997

998
fn parse_row_description(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
13✔
999
    let (i, identifier) = verify(be_u8, |&x| x == b'T').parse(i)?;
13✔
1000
    let (i, length) = parse_gte_length(i, 7)?;
13✔
1001
    let (i, field_count) = be_u16.parse(i)?;
13✔
1002
    let (i, fields) = map_parser(
13✔
1003
        take(length - 6),
13✔
1004
        many_m_n(0, field_count.into(), parse_row_field),
13✔
1005
    ).parse(i)?;
13✔
1006
    Ok((
13✔
1007
        i,
13✔
1008
        PgsqlBEMessage::RowDescription(RowDescriptionMessage {
13✔
1009
            identifier,
13✔
1010
            length,
13✔
1011
            field_count,
13✔
1012
            fields,
13✔
1013
        }),
13✔
1014
    ))
13✔
1015
}
13✔
1016

1017
fn parse_data_row_value(i: &[u8]) -> IResult<&[u8], ColumnFieldValue, PgsqlParseError<&[u8]>> {
35,206✔
1018
    let (i, value_length) = be_i32.parse(i)?;
35,206✔
1019
    let (i, value) = cond(value_length >= 0, take(value_length as usize)).parse(i)?;
35,206✔
1020
    Ok((
1021
        i,
35,206✔
1022
        ColumnFieldValue {
35,206✔
1023
            value_length,
35,206✔
1024
            value: {
35,206✔
1025
                match value {
35,206✔
1026
                    Some(data) => data.to_vec(),
35,191✔
1027
                    None => [].to_vec(),
15✔
1028
                }
1029
            },
1030
        },
1031
    ))
1032
}
35,206✔
1033

1034
/// For each column, add up the data size. Return the total
1035
fn add_up_data_size(columns: Vec<ColumnFieldValue>) -> u64 {
5,045✔
1036
    let mut data_size: u64 = 0;
5,045✔
1037
    for field in columns {
40,251✔
1038
        // -1 value means data value is NULL, let's not add that up
1039
        if field.value_length > 0 {
35,206✔
1040
            data_size += field.value_length as u64;
35,191✔
1041
        }
35,191✔
1042
    }
1043
    data_size
5,045✔
1044
}
5,045✔
1045

1046
fn parse_copy_out_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
1✔
1047
    let (i, identifier) = verify(be_u8, |&x| x == b'H').parse(i)?;
1✔
1048
    // copy out message : identifier (u8), length (u32), format (u8), cols (u16), formats (u16*cols)
1049
    let (i, length) = parse_gte_length(i, 8)?;
1✔
1050
    let (i, _format) = be_u8.parse(i)?;
1✔
1051
    let (i, columns) = be_u16.parse(i)?;
1✔
1052
    let (i, _formats) = many_m_n(0, columns.to_usize(), be_u16).parse(i)?;
1✔
1053
    Ok((
1✔
1054
        i,
1✔
1055
        PgsqlBEMessage::CopyOutResponse(CopyResponse {
1✔
1056
            identifier,
1✔
1057
            length,
1✔
1058
            column_cnt: columns,
1✔
1059
        })
1✔
1060
    ))
1✔
1061
}
1✔
1062

1063
fn parse_copy_in_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
1✔
1064
    let (i, identifier) = verify(be_u8, |&x| x == b'G').parse(i)?;
1✔
1065
    let (i, length) = parse_gte_length(i, 8)?;
1✔
1066
    let (i, _format) = be_u8.parse(i)?;
1✔
1067
    let (i, columns) = be_u16.parse(i)?;
1✔
1068
    let (i, _formats) = many_m_n(0, columns.to_usize(), be_u16).parse(i)?;
1✔
1069
    Ok((
1✔
1070
        i,
1✔
1071
        PgsqlBEMessage::CopyInResponse(CopyResponse {
1✔
1072
            identifier,
1✔
1073
            length,
1✔
1074
            column_cnt: columns,
1✔
1075
        })
1✔
1076
    ))
1✔
1077
}
1✔
1078

1079
fn parse_consolidated_copy_data_out(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
5✔
1080
    let (i, identifier) = verify(be_u8, |&x| x == b'd').parse(i)?;
5✔
1081
    let (i, length) = parse_gte_length(i, 5)?;
5✔
1082
    let (i, _data) = take(length - PGSQL_LENGTH_FIELD).parse(i)?;
5✔
1083
    SCLogDebug!("data_size is {:?}", _data);
1084
    Ok((
5✔
1085
        i, PgsqlBEMessage::ConsolidatedCopyDataOut(ConsolidatedDataRowPacket {
5✔
1086
            identifier,
5✔
1087
            row_cnt: 1,
5✔
1088
            data_size: (length - PGSQL_LENGTH_FIELD) as u64 })
5✔
1089
    ))
5✔
1090
}
5✔
1091

1092
fn parse_consolidated_copy_data_in(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
1✔
1093
    let (i, identifier) = verify(be_u8, |&x| x == b'd').parse(i)?;
1✔
1094
    let (i, length) = parse_gte_length(i, 5)?;
1✔
1095
    let (i, _data) = take(length - PGSQL_LENGTH_FIELD).parse(i)?;
1✔
1096
    SCLogDebug!("data size is {:?}", _data);
1097
    Ok((
1✔
1098
        i, PgsqlFEMessage::ConsolidatedCopyDataIn(ConsolidatedDataRowPacket {
1✔
1099
            identifier,
1✔
1100
            row_cnt: 1,
1✔
1101
            data_size: (length - PGSQL_LENGTH_FIELD) as u64 })
1✔
1102
    ))
1✔
1103
}
1✔
1104

1105
fn parse_copy_in_done(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
1✔
1106
    let (i, identifier) = verify(be_u8, |&x| x == b'c').parse(i)?;
1✔
1107
    let (i, length) = parse_exact_length(i, PGSQL_LENGTH_FIELD)?;
1✔
1108
    Ok((
1✔
1109
        i, PgsqlFEMessage::CopyDone(NoPayloadMessage {
1✔
1110
            identifier,
1✔
1111
            length
1✔
1112
        })
1✔
1113
    ))
1✔
1114
}
1✔
1115

1116
fn parse_copy_out_done(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
1✔
1117
    let (i, identifier) = verify(be_u8, |&x| x == b'c').parse(i)?;
1✔
1118
    let (i, length) = parse_exact_length(i, PGSQL_LENGTH_FIELD)?;
1✔
1119
    Ok((
1✔
1120
        i, PgsqlBEMessage::CopyDone(NoPayloadMessage {
1✔
1121
            identifier,
1✔
1122
            length
1✔
1123
        })
1✔
1124
    ))
1✔
1125
}
1✔
1126

UNCOV
1127
fn parse_copy_fail(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
×
UNCOV
1128
    let (i, identifier) = verify(be_u8, |&x| x == b'f').parse(i)?;
×
UNCOV
1129
    let (i, length) = parse_gte_length(i, 5)?;
×
UNCOV
1130
    let (i, data) = take(length - PGSQL_LENGTH_FIELD).parse(i)?;
×
UNCOV
1131
    Ok((
×
UNCOV
1132
        i, PgsqlFEMessage::CopyFail(RegularPacket {
×
UNCOV
1133
            identifier,
×
UNCOV
1134
            length,
×
UNCOV
1135
            payload: data.to_vec(),
×
UNCOV
1136
        })
×
UNCOV
1137
    ))
×
UNCOV
1138
}
×
1139

1140
// Currently, we don't store the actual DataRow messages, as those could easily become a burden, memory-wise
1141
// We use ConsolidatedDataRow to store info we still want to log: message size.
1142
// Later on, we calculate the number of lines the command actually returned by counting ConsolidatedDataRow messages
1143
fn parse_consolidated_data_row(
5,414✔
1144
    i: &[u8],
5,414✔
1145
) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
5,414✔
1146
    let (i, identifier) = verify(be_u8, |&x| x == b'D').parse(i)?;
5,414✔
1147
    let (i, length) = parse_gte_length(i, 7)?;
5,414✔
1148
    let (i, field_count) = be_u16.parse(i)?;
5,410✔
1149
    // 6 here is for skipping length + field_count
1150
    let (i, rows) = map_parser(
5,409✔
1151
        take(length - 6),
5,409✔
1152
        many_m_n(0, field_count.into(), parse_data_row_value),
5,409✔
1153
    ).parse(i)?;
5,409✔
1154
    Ok((
5,045✔
1155
        i,
5,045✔
1156
        PgsqlBEMessage::ConsolidatedDataRow(ConsolidatedDataRowPacket {
5,045✔
1157
            identifier,
5,045✔
1158
            row_cnt: 1,
5,045✔
1159
            data_size: add_up_data_size(rows),
5,045✔
1160
        }),
5,045✔
1161
    ))
5,045✔
1162
}
5,414✔
1163

1164
fn parse_sasl_mechanism(
15✔
1165
    i: &[u8],
15✔
1166
) -> IResult<&[u8], SASLAuthenticationMechanism, PgsqlParseError<&[u8]>> {
15✔
1167
    let res: IResult<_, _, ()> = terminated(tag("SCRAM-SHA-256-PLUS"), tag(b"\x00".as_ref())).parse(i);
15✔
1168
    if let Ok((i, _)) = res {
15✔
1169
        return Ok((i, SASLAuthenticationMechanism::ScramSha256Plus));
2✔
1170
    }
13✔
1171
    let res: IResult<_, _, ()> = terminated(tag("SCRAM-SHA-256"), tag(b"\x00".as_ref())).parse(i);
13✔
1172
    if let Ok((i, _)) = res {
13✔
1173
        return Ok((i, SASLAuthenticationMechanism::ScramSha256));
8✔
1174
    }
5✔
1175
    return Err(Err::Error(make_error(i, ErrorKind::Alt)));
5✔
1176
}
15✔
1177

1178
fn parse_sasl_mechanisms(
5✔
1179
    i: &[u8],
5✔
1180
) -> IResult<&[u8], Vec<SASLAuthenticationMechanism>, PgsqlParseError<&[u8]>> {
5✔
1181
    terminated(many1(parse_sasl_mechanism), tag(b"\x00".as_ref())).parse(i)
5✔
1182
}
5✔
1183

1184
fn parse_error_response_code(
27✔
1185
    i: &[u8],
27✔
1186
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
27✔
1187
    let (i, _field_type) = char('C').parse(i)?;
27✔
1188
    let (i, field_value) = map_parser(take(6_usize), alphanumeric1).parse(i)?;
27✔
1189
    Ok((
25✔
1190
        i,
25✔
1191
        PgsqlErrorNoticeMessageField {
25✔
1192
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
25✔
1193
            field_value: field_value.to_vec(),
25✔
1194
        },
25✔
1195
    ))
25✔
1196
}
27✔
1197

1198
// Parse an error response with non-localizeable severity message.
1199
// Possible values: ERROR, FATAL, or PANIC
1200
fn parse_error_response_severity(
17✔
1201
    i: &[u8],
17✔
1202
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
17✔
1203
    let (i, field_type) = char('V').parse(i)?;
17✔
1204
    let (i, field_value) = alt((tag("ERROR"), tag("FATAL"), tag("PANIC"))).parse(i)?;
17✔
1205
    let (i, _) = tag(b"\x00".as_ref()).parse(i)?;
17✔
1206
    Ok((
17✔
1207
        i,
17✔
1208
        PgsqlErrorNoticeMessageField {
17✔
1209
            field_type: PgsqlErrorNoticeFieldType::from(field_type),
17✔
1210
            field_value: field_value.to_vec(),
17✔
1211
        },
17✔
1212
    ))
17✔
1213
}
17✔
1214

1215
// The non-localizable version of Severity field has different values,
1216
// in case of a notice: 'WARNING', 'NOTICE', 'DEBUG', 'INFO' or 'LOG'
1217
fn parse_notice_response_severity(
1✔
1218
    i: &[u8],
1✔
1219
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
1✔
1220
    let (i, field_type) = char('V').parse(i)?;
1✔
1221
    let (i, field_value) = alt((
1✔
1222
        tag("WARNING"),
1✔
1223
        tag("NOTICE"),
1✔
1224
        tag("DEBUG"),
1✔
1225
        tag("INFO"),
1✔
1226
        tag("LOG"),
1✔
1227
    )).parse(i)?;
1✔
1228
    let (i, _) = tag(b"\x00".as_ref()).parse(i)?;
1✔
1229
    Ok((
1✔
1230
        i,
1✔
1231
        PgsqlErrorNoticeMessageField {
1✔
1232
            field_type: PgsqlErrorNoticeFieldType::from(field_type),
1✔
1233
            field_value: field_value.to_vec(),
1✔
1234
        },
1✔
1235
    ))
1✔
1236
}
1✔
1237

1238
fn parse_error_response_field(
154✔
1239
    i: &[u8], is_err_msg: bool,
154✔
1240
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
154✔
1241
    let (i, field_type) = peek(be_u8).parse(i)?;
154✔
1242
    let (i, data) = match field_type {
154✔
1243
        b'V' => {
1244
            if is_err_msg {
18✔
1245
                parse_error_response_severity(i)?
17✔
1246
            } else {
1247
                parse_notice_response_severity(i)?
1✔
1248
            }
1249
        }
1250
        b'C' => parse_error_response_code(i)?,
24✔
1251
        _ => {
1252
            let (i, field_type) = be_u8.parse(i)?;
112✔
1253
            let (i, field_value) = take_until(b"\x00".as_ref()).parse(i)?;
112✔
1254
            let (i, _just_tag) = tag(b"\x00".as_ref()).parse(i)?;
111✔
1255
            let message = PgsqlErrorNoticeMessageField {
111✔
1256
                field_type: PgsqlErrorNoticeFieldType::from(field_type),
111✔
1257
                field_value: field_value.to_vec(),
111✔
1258
            };
111✔
1259
            return Ok((i, message));
111✔
1260
        }
1261
    };
1262
    Ok((i, data))
41✔
1263
}
154✔
1264

1265
fn parse_error_notice_fields(
24✔
1266
    i: &[u8], is_err_msg: bool,
24✔
1267
) -> IResult<&[u8], Vec<PgsqlErrorNoticeMessageField>, PgsqlParseError<&[u8]>> {
24✔
1268
    let (i, data) = many_till(|b| parse_error_response_field(b, is_err_msg), tag(b"\x00".as_ref())).parse(i)?;
150✔
1269
    Ok((i, data.0))
23✔
1270
}
24✔
1271

1272
fn pgsql_parse_error_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
20✔
1273
    let (i, identifier) = verify(be_u8, |&x| x == b'E').parse(i)?;
20✔
1274
    let (i, length) = parse_gte_length(i, 11)?;
20✔
1275
    let (i, message_body) = map_parser(take(length - PGSQL_LENGTH_FIELD), |b| {
20✔
1276
        parse_error_notice_fields(b, true)
19✔
1277
    }).parse(i)?;
20✔
1278

1279
    Ok((
19✔
1280
        i,
19✔
1281
        PgsqlBEMessage::ErrorResponse(ErrorNoticeMessage {
19✔
1282
            identifier,
19✔
1283
            length,
19✔
1284
            message_body,
19✔
1285
        }),
19✔
1286
    ))
19✔
1287
}
20✔
1288

1289
fn pgsql_parse_notice_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
3✔
1290
    let (i, identifier) = verify(be_u8, |&x| x == b'N').parse(i)?;
3✔
1291
    let (i, length) = parse_gte_length(i, 11)?;
3✔
1292
    let (i, message_body) = map_parser(take(length - PGSQL_LENGTH_FIELD), |b| {
1✔
1293
        parse_error_notice_fields(b, false)
1✔
1294
    }).parse(i)?;
1✔
1295
    Ok((
1✔
1296
        i,
1✔
1297
        PgsqlBEMessage::NoticeResponse(ErrorNoticeMessage {
1✔
1298
            identifier,
1✔
1299
            length,
1✔
1300
            message_body,
1✔
1301
        }),
1✔
1302
    ))
1✔
1303
}
3✔
1304

1305
fn parse_notification_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
2✔
1306
    let (i, identifier) = verify(be_u8, |&x| x == b'A').parse(i)?;
2✔
1307
    // length (u32) + pid (u32) + at least one byte, for we have two str fields
1308
    let (i, length) = parse_gte_length(i, 10)?;
2✔
1309
    let (i, data) = map_parser(take(length - PGSQL_LENGTH_FIELD), |b| {
2✔
1310
        let (b, pid) = be_u32.parse(b)?;
2✔
1311
        let (b, channel_name) = take_until_and_consume(b"\x00").parse(b)?;
2✔
1312
        let (b, payload) = take_until_and_consume(b"\x00").parse(b)?;
2✔
1313
        Ok((b, (pid, channel_name, payload)))
2✔
1314
    }).parse(i)?;
2✔
1315
    let msg = PgsqlBEMessage::NotificationResponse(NotificationResponse {
2✔
1316
        identifier,
2✔
1317
        length,
2✔
1318
        pid: data.0,
2✔
1319
        channel_name: data.1.to_vec(),
2✔
1320
        payload: data.2.to_vec(),
2✔
1321
    });
2✔
1322
    Ok((i, msg))
2✔
1323
}
2✔
1324

1325
pub(crate) fn pgsql_parse_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
5,873✔
1326
    let (i, tag) = peek(be_u8).parse(i)?;
5,873✔
1327
    let (i, message) = match tag {
5,873✔
1328
        b'E' => pgsql_parse_error_response(i)?,
20✔
1329
        b'K' => parse_backend_key_data_message(i)?,
16✔
1330
        b'N' => pgsql_parse_notice_response(i)?,
3✔
1331
        b'R' => pgsql_parse_authentication_message(i)?,
61✔
1332
        b'S' => parse_parameter_status_message(i)?,
174✔
1333
        b'C' => parse_command_complete(i)?,
63✔
1334
        b'c' => parse_copy_out_done(i)?,
1✔
1335
        b'Z' => parse_ready_for_query(i)?,
95✔
1336
        b'T' => parse_row_description(i)?,
12✔
1337
        b'A' => parse_notification_response(i)?,
2✔
1338
        b'D' => parse_consolidated_data_row(i)?,
5,413✔
1339
        b'd' => parse_consolidated_copy_data_out(i)?,
5✔
1340
        b'H' => parse_copy_out_response(i)?,
1✔
1341
        b'G' => parse_copy_in_response(i)?,
1✔
1342
        _ => {
1343
            let (i, identifier) = be_u8.parse(i)?;
6✔
1344
            let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
6✔
1345
            let (i, payload) = take(length - PGSQL_LENGTH_FIELD).parse(i)?;
6✔
1346
            let unknown = PgsqlBEMessage::UnknownMessageType(RegularPacket {
5✔
1347
                identifier,
5✔
1348
                length,
5✔
1349
                payload: payload.to_vec(),
5✔
1350
            });
5✔
1351
            SCLogDebug!("parsed unknown BE message, identifier is {:?}", identifier);
5✔
1352
            (i, unknown)
5✔
1353
        }
1354
    };
1355
    Ok((i, message))
5,495✔
1356
}
5,873✔
1357

1358
#[cfg(test)]
1359
mod tests {
1360

1361
    use super::*;
1362
    use nom8::Needed;
1363

1364
    impl ErrorNoticeMessage {
1365
        fn new(identifier: u8, length: u32) -> Self {
2✔
1366
            ErrorNoticeMessage {
2✔
1367
                identifier,
2✔
1368
                length,
2✔
1369
                message_body: Vec::new(),
2✔
1370
            }
2✔
1371
        }
2✔
1372
    }
1373

1374
    #[test]
1375
    fn test_parse_request() {
1✔
1376
        // An SSLRequest
1✔
1377
        let buf: &[u8] = &[0x00, 0x00, 0x00, 0x08, 0x04, 0xd2, 0x16, 0x2f];
1✔
1378
        let ssl_request = DummyStartupPacket {
1✔
1379
            length: 8,
1✔
1380
            proto_major: PGSQL_DUMMY_PROTO_MAJOR,
1✔
1381
            proto_minor: PGSQL_DUMMY_PROTO_MINOR_SSL,
1✔
1382
        };
1✔
1383
        let request_ok = PgsqlFEMessage::SSLRequest(ssl_request);
1✔
1384

1✔
1385
        let (_remainder, result) = parse_request(buf).unwrap();
1✔
1386
        assert_eq!(result, request_ok);
1✔
1387

1388
        // incomplete message
1389
        let result = parse_request(&buf[0..7]);
1✔
1390
        assert!(result.is_err());
1✔
1391

1392
        // Same request, but length is wrong
1393
        let buf: &[u8] = &[0x00, 0x00, 0x00, 0x07, 0x04, 0xd2, 0x16, 0x2f];
1✔
1394
        let result = parse_request(buf);
1✔
1395
        assert!(result.is_err());
1✔
1396

1397
        let buf: &[u8] = &[
1✔
1398
            /* Length 85 */ 0x00, 0x00, 0x00, 0x55, /* Proto version */ 0x00, 0x03, 0x00,
1✔
1399
            0x00, /* user */ 0x75, 0x73, 0x65, 0x72, 0x00, /* [value] rep */ 0x72, 0x65,
1✔
1400
            0x70, 0x00, /* database */ 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00,
1✔
1401
            /* [optional] */ 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63,
1✔
1402
            /* replication replication true application_name walreceiver */
1✔
1403
            0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
1✔
1404
            0x69, 0x6f, 0x6e, 0x00, 0x74, 0x72, 0x75, 0x65, 0x00, 0x61, 0x70, 0x70, 0x6c, 0x69,
1✔
1405
            0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x77, 0x61,
1✔
1406
            0x6c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x00, 0x00,
1✔
1407
        ];
1✔
1408
        let result = parse_request(buf);
1✔
1409
        match result {
1410
            Ok((remainder, _message)) => {
1✔
1411
                // there should be nothing left
1✔
1412
                assert_eq!(remainder.len(), 0);
1✔
1413
            }
1414
            Err(Err::Error(err)) => {
1415
                panic!("Result should not be an error: {:?}.", err);
1416
            }
1417
            Err(Err::Incomplete(_)) => {
1418
                panic!("Result should not have been incomplete.");
1419
            }
1420
            _ => {
1421
                panic!("Unexpected behavior!");
1422
            }
1423
        }
1424

1425
        // A valid startup message/request without optional parameters
1426
        // ...&....user.oryx.database.mailstore..
1427
        let buf: &[u8] = &[
1✔
1428
            0x00, 0x00, 0x00, 0x26, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
1✔
1429
            0x72, 0x79, 0x78, 0x00, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x6d,
1✔
1430
            0x61, 0x69, 0x6c, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x00, 0x00,
1✔
1431
        ];
1✔
1432
        let user = PgsqlParameter {
1✔
1433
            name: PgsqlParameters::User,
1✔
1434
            value: br#"oryx"#.to_vec(),
1✔
1435
        };
1✔
1436
        let database = PgsqlParameter {
1✔
1437
            name: PgsqlParameters::Database,
1✔
1438
            value: br#"mailstore"#.to_vec(),
1✔
1439
        };
1✔
1440
        let database_param: Vec<PgsqlParameter> = vec![database];
1✔
1441
        let params = PgsqlStartupParameters {
1✔
1442
            user,
1✔
1443
            optional_params: Some(database_param),
1✔
1444
        };
1✔
1445
        let expected_result = PgsqlFEMessage::StartupMessage(StartupPacket {
1✔
1446
            length: 38,
1✔
1447
            proto_major: 3,
1✔
1448
            proto_minor: 0,
1✔
1449
            params,
1✔
1450
        });
1✔
1451
        let result = parse_request(buf);
1✔
1452
        match result {
1453
            Ok((remainder, message)) => {
1✔
1454
                assert_eq!(message, expected_result);
1✔
1455
                assert_eq!(remainder.len(), 0);
1✔
1456
            }
1457
            Err(Err::Error(err)) => {
1458
                panic!("Shouldn't be error: {:?}", err);
1459
            }
1460
            Err(Err::Incomplete(needed)) => {
1461
                panic!("Should not be Incomplete! Needed: {:?}", needed);
1462
            }
1463
            _ => {
1464
                panic!("Unexpected behavior");
1465
            }
1466
        }
1467

1468
        // A valid startup message/request without any optional parameters
1469
        // ........user.oryx..
1470
        let buf: &[u8] = &[
1✔
1471
            0x00, 0x00, 0x00, 0x13, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
1✔
1472
            0x72, 0x79, 0x78, 0x00, 0x00,
1✔
1473
        ];
1✔
1474
        let user = PgsqlParameter {
1✔
1475
            name: PgsqlParameters::User,
1✔
1476
            value: br#"oryx"#.to_vec(),
1✔
1477
        };
1✔
1478
        let params = PgsqlStartupParameters {
1✔
1479
            user,
1✔
1480
            optional_params: None,
1✔
1481
        };
1✔
1482
        let expected_result = PgsqlFEMessage::StartupMessage(StartupPacket {
1✔
1483
            length: 19,
1✔
1484
            proto_major: 3,
1✔
1485
            proto_minor: 0,
1✔
1486
            params,
1✔
1487
        });
1✔
1488
        let result = parse_request(buf);
1✔
1489
        match result {
1490
            Ok((remainder, message)) => {
1✔
1491
                assert_eq!(message, expected_result);
1✔
1492
                assert_eq!(remainder.len(), 0);
1✔
1493
            }
1494
            Err(Err::Error(err)) => {
1495
                panic!("Shouldn't be error: {:?}", err);
1496
            }
1497
            Err(Err::Incomplete(needed)) => {
1498
                panic!("Should not be Incomplete! Needed: {:?}", needed);
1499
            }
1500
            _ => {
1501
                panic!("Unexpected behavior");
1502
            }
1503
        }
1504

1505
        // A startup message/request with length off by one
1506
        let buf: &[u8] = &[
1✔
1507
            0x00, 0x00, 0x00, 0x12, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
1✔
1508
            0x72, 0x79, 0x78, 0x00, 0x00,
1✔
1509
        ];
1✔
1510
        let result = parse_request(buf);
1✔
1511
        assert!(result.is_err());
1✔
1512

1513
        // A startup message/request with bad length
1514
        let buf: &[u8] = &[
1✔
1515
            0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
1✔
1516
            0x72, 0x79, 0x78, 0x00, 0x00,
1✔
1517
        ];
1✔
1518
        let result = parse_request(buf);
1✔
1519
        assert!(result.is_err());
1✔
1520

1521
        // A startup message/request with corrupted user param
1522
        let buf: &[u8] = &[
1✔
1523
            0x00, 0x00, 0x00, 0x013, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x00, 0x6f, 0x72,
1✔
1524
            0x79, 0x78, 0x00, 0x00,
1✔
1525
        ];
1✔
1526
        let result = parse_request(buf);
1✔
1527
        assert!(result.is_err());
1✔
1528

1529
        // A startup message/request missing the terminator
1530
        let buf: &[u8] = &[
1✔
1531
            0x00, 0x00, 0x00, 0x013, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
1✔
1532
            0x72, 0x79, 0x78, 0x00,
1✔
1533
        ];
1✔
1534
        let result = parse_request(buf);
1✔
1535
        assert!(result.is_err());
1✔
1536

1537
        // A termination message
1538
        let buf: &[u8] = &[0x58, 0x00, 0x00, 0x00, 0x04];
1✔
1539
        let result = parse_request(buf);
1✔
1540
        assert!(result.is_ok());
1✔
1541

1542
        let result = parse_request(&buf[0..3]);
1✔
1543
        assert!(result.is_err());
1✔
1544
    }
1✔
1545

1546
    #[test]
1547
    fn test_cancel_request_message() {
1✔
1548
        // A cancel request message
1✔
1549
        let buf: &[u8] = &[
1✔
1550
            0x00, 0x00, 0x00, 0x10, // length: 16 (fixed)
1✔
1551
            0x04, 0xd2, 0x16, 0x2e, // 1234.5678 - identifies a cancel request
1✔
1552
            0x00, 0x00, 0x76, 0x31, // PID: 30257
1✔
1553
            0x23, 0x84, 0xf7, 0x2d,
1✔
1554
        ]; // Backend key: 595916589
1✔
1555
        let result = parse_cancel_request(buf);
1✔
1556
        assert!(result.is_ok());
1✔
1557

1558
        let result = parse_cancel_request(&buf[0..3]);
1✔
1559
        assert!(result.is_err());
1✔
1560

1561
        let result = pgsql_parse_startup_packet(buf);
1✔
1562
        assert!(result.is_ok());
1✔
1563

1564
        let fail_result = pgsql_parse_startup_packet(&buf[0..3]);
1✔
1565
        assert!(fail_result.is_err());
1✔
1566

1567
        let result = parse_request(buf);
1✔
1568
        assert!(result.is_ok());
1✔
1569

1570
        let fail_result = parse_request(&buf[0..3]);
1✔
1571
        assert!(fail_result.is_err());
1✔
1572
    }
1✔
1573

1574
    #[test]
1575
    fn test_parse_error_response_code() {
1✔
1576
        let buf: &[u8] = &[0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00];
1✔
1577
        let value_str = "28000".as_bytes();
1✔
1578
        let ok_res = PgsqlErrorNoticeMessageField {
1✔
1579
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
1✔
1580
            field_value: value_str.to_vec(),
1✔
1581
        };
1✔
1582
        let result = parse_error_response_code(buf);
1✔
1583
        assert!(result.is_ok());
1✔
1584

1585
        let (remainder, result) = parse_error_response_code(buf).unwrap();
1✔
1586
        assert_eq!(result, ok_res);
1✔
1587
        assert_eq!(remainder.len(), 0);
1✔
1588

1589
        let result = parse_error_response_code(&buf[0..5]);
1✔
1590
        assert!(result.is_err());
1✔
1591
    }
1✔
1592

1593
    #[test]
1594
    fn test_parse_password_messages() {
1✔
1595
        // A password message (MD5)
1✔
1596
        let buf: &[u8] = &[
1✔
1597
            0x70, 0x00, 0x00, 0x00, 0x28, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
1✔
1598
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
1✔
1599
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00,
1✔
1600
        ];
1✔
1601
        let ok_result = PgsqlFEMessage::PasswordMessage(RegularPacket {
1✔
1602
            identifier: b'p',
1✔
1603
            length: 40,
1✔
1604
            payload: br#"md5ceffc01dcde7541829deef6b5e9c9142"#.to_vec(),
1✔
1605
        });
1✔
1606
        let (_remainder, result) = parse_password_message(buf).unwrap();
1✔
1607
        assert_eq!(result, ok_result);
1✔
1608

1609
        // Length is off by one here
1610
        let buf: &[u8] = &[
1✔
1611
            0x70, 0x00, 0x00, 0x00, 0x27, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
1✔
1612
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
1✔
1613
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00,
1✔
1614
        ];
1✔
1615
        let result = parse_password_message(buf);
1✔
1616
        assert!(result.is_err());
1✔
1617

1618
        // Length also off by one, but now bigger than it should
1619
        let buf: &[u8] = &[
1✔
1620
            0x70, 0x00, 0x00, 0x00, 0x29, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
1✔
1621
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
1✔
1622
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00,
1✔
1623
        ];
1✔
1624
        let result = parse_password_message(buf);
1✔
1625
        assert!(result.is_err());
1✔
1626

1627
        // Incomplete payload
1628
        let buf: &[u8] = &[
1✔
1629
            0x70, 0x00, 0x00, 0x00, 0x28, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
1✔
1630
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
1✔
1631
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32,
1✔
1632
        ];
1✔
1633
        let result = parse_password_message(buf);
1✔
1634
        assert!(result.is_err());
1✔
1635
    }
1✔
1636

1637
    #[test]
1638
    fn test_parse_error_response_field() {
1✔
1639
        // VFATAL
1✔
1640
        let input: &[u8] = &[0x56, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00];
1✔
1641

1✔
1642
        let value_str = "FATAL".as_bytes();
1✔
1643
        let ok_res = PgsqlErrorNoticeMessageField {
1✔
1644
            field_type: PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
1✔
1645
            field_value: value_str.to_vec(),
1✔
1646
        };
1✔
1647

1✔
1648
        let (remainder, result) = parse_error_response_field(input, true).unwrap();
1✔
1649
        assert_eq!(result, ok_res);
1✔
1650
        assert_eq!(remainder.len(), 0);
1✔
1651

1652
        // "Mno pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off "
1653
        let input: &[u8] = &[
1✔
1654
            0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e,
1✔
1655
            0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
1✔
1656
            0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e,
1✔
1657
            0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f,
1✔
1658
            0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30,
1✔
1659
            0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65,
1✔
1660
            0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00,
1✔
1661
        ];
1✔
1662

1✔
1663
        let value_str = br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#;
1✔
1664
        let ok_res = PgsqlErrorNoticeMessageField {
1✔
1665
            field_type: PgsqlErrorNoticeFieldType::Message,
1✔
1666
            field_value: value_str.to_vec(),
1✔
1667
        };
1✔
1668

1✔
1669
        let (remainder, result) = parse_error_response_field(input, true).unwrap();
1✔
1670
        assert_eq!(result, ok_res);
1✔
1671
        assert_eq!(remainder.len(), 0);
1✔
1672

1673
        // if incomplete, here we should get an error
1674
        let result = parse_error_response_field(&input[0..12], true);
1✔
1675
        assert!(result.is_err());
1✔
1676

1677
        // C28000
1678
        let input: &[u8] = &[0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00];
1✔
1679
        let value_str = "28000".as_bytes();
1✔
1680
        let ok_res = PgsqlErrorNoticeMessageField {
1✔
1681
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
1✔
1682
            field_value: value_str.to_vec(),
1✔
1683
        };
1✔
1684
        let (remainder, result) = parse_error_response_field(input, true).unwrap();
1✔
1685
        assert_eq!(result, ok_res);
1✔
1686
        assert_eq!(remainder.len(), 0);
1✔
1687
    }
1✔
1688

1689
    // After sending AuthenticationOk, the backend will send a series of messages with parameters, a backend key message, and finally a ready for query message
1690
    #[test]
1691
    fn test_parse_startup_phase_wrapup() {
1✔
1692
        // S   .application_name psql
1✔
1693
        let buf: &[u8] = &[
1✔
1694
            0x53, 0x00, 0x00, 0x00, 0x1a, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,
1✔
1695
            0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x70, 0x73, 0x71, 0x6c, 0x00,
1✔
1696
        ];
1✔
1697

1✔
1698
        let ok_res = PgsqlBEMessage::ParameterStatus(ParameterStatusMessage {
1✔
1699
            identifier: b'S',
1✔
1700
            length: 26,
1✔
1701
            param: PgsqlParameter {
1✔
1702
                name: PgsqlParameters::ApplicationName,
1✔
1703
                value: br#"psql"#.to_vec(),
1✔
1704
            },
1✔
1705
        });
1✔
1706

1✔
1707
        let (_remainder, result) = parse_parameter_status_message(buf).unwrap();
1✔
1708
        assert_eq!(result, ok_res);
1✔
1709

1710
        let result = pgsql_parse_response(buf);
1✔
1711
        match result {
1712
            Ok((_remainder, message)) => {
1✔
1713
                assert_eq!(message, ok_res);
1✔
1714
            }
1715
            Err(Err::Error(err)) => {
1716
                panic!("Shouldn't be err {:?}, expected Ok(_).", err);
1717
            }
1718
            Err(Err::Incomplete(needed)) => {
1719
                panic!("Should not be incomplete {:?}, expected Ok(_)", needed);
1720
            }
1721
            _ => panic!("Unexpected behavior, expected Ok(_)"),
1722
        }
1723

1724
        // S   .integer_datetimes on
1725
        let buf: &[u8] = &[
1✔
1726
            0x53, 0x00, 0x00, 0x00, 0x19, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x64,
1✔
1727
            0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x00, 0x6f, 0x6e, 0x00,
1✔
1728
        ];
1✔
1729
        let result = parse_parameter_status_message(buf);
1✔
1730
        assert!(result.is_ok());
1✔
1731

1732
        // K       =.... // PID 61 Key 3152142766
1733
        let buf: &[u8] = &[
1✔
1734
            0x4b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0xe1, 0xe1, 0xae,
1✔
1735
        ];
1✔
1736

1✔
1737
        let result = parse_backend_key_data_message(buf);
1✔
1738
        assert!(result.is_ok());
1✔
1739

1740
        // Z   .I
1741
        let buf: &[u8] = &[0x5a, 0x00, 0x00, 0x00, 0x05, 0x49];
1✔
1742
        let result = parse_ready_for_query(buf);
1✔
1743
        assert!(result.is_ok());
1✔
1744
    }
1✔
1745

1746
    #[test]
1747
    fn test_parse_error_notice_fields() {
1✔
1748
        let input: &[u8] = &[0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x00];
1✔
1749

1✔
1750
        let field1 = PgsqlErrorNoticeMessageField {
1✔
1751
            field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable,
1✔
1752
            field_value: br#"FATAL"#.to_vec(),
1✔
1753
        };
1✔
1754
        let field2 = PgsqlErrorNoticeMessageField {
1✔
1755
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
1✔
1756
            field_value: br#"28000"#.to_vec(),
1✔
1757
        };
1✔
1758
        let field3 = PgsqlErrorNoticeMessageField{
1✔
1759
            field_type: PgsqlErrorNoticeFieldType::Message,
1✔
1760
            field_value: br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#.to_vec(),
1✔
1761
        };
1✔
1762
        // let field4 = PgsqlErrorNoticeMessageField {
1✔
1763
        //     field_type: PgsqlErrorNoticeFieldType::TerminatorToken,
1✔
1764
        //     field_value: br#""#.to_vec(),
1✔
1765
        // };
1✔
1766

1✔
1767
        let mut ok_res: Vec<PgsqlErrorNoticeMessageField> = Vec::new();
1✔
1768
        ok_res.push(field1);
1✔
1769
        // ok_res.push(field4);
1✔
1770

1✔
1771
        let (remainder, result) = parse_error_notice_fields(input, true).unwrap();
1✔
1772
        assert_eq!(result, ok_res);
1✔
1773
        assert_eq!(remainder.len(), 0);
1✔
1774
        // ok_res.pop();
1775

1776
        ok_res.push(field2);
1✔
1777
        ok_res.push(field3);
1✔
1778

1✔
1779
        // let field4 = PgsqlErrorNoticeMessageField {
1✔
1780
        //     field_type: PgsqlErrorNoticeFieldType::TerminatorToken,
1✔
1781
        //     field_value: br#""#.to_vec(),
1✔
1782
        // };
1✔
1783

1✔
1784
        // ok_res.push(field4);
1✔
1785

1✔
1786
        let input: &[u8] = &[
1✔
1787
            0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00,
1✔
1788
            0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e,
1✔
1789
            0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
1✔
1790
            0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e,
1✔
1791
            0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f,
1✔
1792
            0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30,
1✔
1793
            0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65,
1✔
1794
            0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, 0x00,
1✔
1795
        ];
1✔
1796

1✔
1797
        let (remainder, result) = parse_error_notice_fields(input, true).unwrap();
1✔
1798
        assert_eq!(result, ok_res);
1✔
1799
        assert_eq!(remainder.len(), 0);
1✔
1800

1801
        let input: &[u8] = &[
1✔
1802
            0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00,
1✔
1803
            0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e,
1✔
1804
            0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
1✔
1805
            0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e,
1✔
1806
            0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f,
1✔
1807
            0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30,
1✔
1808
            0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65,
1✔
1809
            0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, 0x46, 0x61,
1✔
1810
            0x75, 0x74, 0x68, 0x2e, 0x63, 0x00, 0x4c, 0x34, 0x38, 0x31, 0x00, 0x52, 0x43, 0x6c,
1✔
1811
            0x69, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
1✔
1812
            0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,
1✔
1813
        ];
1✔
1814

1✔
1815
        let result = parse_error_notice_fields(input, true);
1✔
1816
        assert!(result.is_ok());
1✔
1817

1818
        let result = parse_error_notice_fields(&input[0..12], true);
1✔
1819
        match result {
1✔
1820
            Ok((_remainder, _message)) => panic!("Result should not be ok, but incomplete."),
1821

1822
            Err(Err::Error(err)) => {
1823
                panic!("Shouldn't be error: {:?}", err);
1824
            }
1825
            Err(Err::Incomplete(needed)) => {
1✔
1826
                assert_eq!(needed, Needed::new(2));
1✔
1827
            }
1828
            _ => panic!("Unexpected behavior."),
1829
        }
1830
    }
1✔
1831

1832
    #[test]
1833
    fn test_parse_error_notice_response() {
1✔
1834
        // test case buffer
1✔
1835
        let buf: &[u8] = &[
1✔
1836
            /* identifier */ 0x45, /* length */ 0x00, 0x00, 0x00, 0x96,
1✔
1837
            /* Severity */ 0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, /* Code */ 0x43,
1✔
1838
            0x32, 0x38, 0x30, 0x30, 0x30, 0x00, /* Message */ 0x4d, 0x6e, 0x6f, 0x20, 0x70,
1✔
1839
            0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x65, 0x6e, 0x74,
1✔
1840
            0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61,
1✔
1841
            0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
1✔
1842
            0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x22, 0x31,
1✔
1843
            0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30, 0x2e, 0x31, 0x31, 0x22, 0x2c,
1✔
1844
            0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65, 0x70, 0x22, 0x2c, 0x20, 0x53,
1✔
1845
            0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, /* File */ 0x46, 0x61, 0x75, 0x74, 0x68,
1✔
1846
            0x2e, 0x63, 0x00, /* Line */ 0x4c, 0x34, 0x38, 0x31, 0x00,
1✔
1847
            /* Routine */ 0x52, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68,
1✔
1848
            0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,
1✔
1849
        ];
1✔
1850

1✔
1851
        // expected result
1✔
1852
        let field1 = PgsqlErrorNoticeMessageField {
1✔
1853
            field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable,
1✔
1854
            field_value: br#"FATAL"#.to_vec(),
1✔
1855
        };
1✔
1856
        let field2 = PgsqlErrorNoticeMessageField {
1✔
1857
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
1✔
1858
            field_value: br#"28000"#.to_vec(),
1✔
1859
        };
1✔
1860
        let field3 = PgsqlErrorNoticeMessageField {
1✔
1861
            field_type: PgsqlErrorNoticeFieldType::Message,
1✔
1862
            field_value: br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#.to_vec(),
1✔
1863
        };
1✔
1864
        let field4 = PgsqlErrorNoticeMessageField {
1✔
1865
            field_type: PgsqlErrorNoticeFieldType::File,
1✔
1866
            field_value: br#"auth.c"#.to_vec(),
1✔
1867
        };
1✔
1868
        let field5 = PgsqlErrorNoticeMessageField {
1✔
1869
            field_type: PgsqlErrorNoticeFieldType::Line,
1✔
1870
            field_value: br#"481"#.to_vec(),
1✔
1871
        };
1✔
1872
        let field6 = PgsqlErrorNoticeMessageField {
1✔
1873
            field_type: PgsqlErrorNoticeFieldType::Routine,
1✔
1874
            field_value: br#"ClientAuthentication"#.to_vec(),
1✔
1875
        };
1✔
1876

1✔
1877
        let mut payload = ErrorNoticeMessage::new(b'E', 150);
1✔
1878
        payload.message_body.push(field1);
1✔
1879
        payload.message_body.push(field2);
1✔
1880
        payload.message_body.push(field3);
1✔
1881
        payload.message_body.push(field4);
1✔
1882
        payload.message_body.push(field5);
1✔
1883
        payload.message_body.push(field6);
1✔
1884

1✔
1885
        let ok_res = PgsqlBEMessage::ErrorResponse(payload);
1✔
1886

1✔
1887
        let result = pgsql_parse_response(buf);
1✔
1888
        match result {
1889
            Ok((remainder, message)) => {
1✔
1890
                assert_eq!(message, ok_res);
1✔
1891
                assert_eq!(remainder.len(), 0);
1✔
1892
            }
1893
            Err(Err::Error(err)) => {
1894
                panic!("Shouldn't be error: {:?}", err);
1895
            }
1896
            Err(Err::Incomplete(needed)) => {
1897
                panic!("Should not be Incomplete! Needed: {:?}", needed);
1898
            }
1899
            _ => {
1900
                panic!("Unexpected behavior");
1901
            }
1902
        }
1903

1904
        let result_incomplete = pgsql_parse_response(&buf[0..22]);
1✔
1905
        match result_incomplete {
1✔
1906
            Err(Err::Incomplete(needed)) => {
1✔
1907
                // parser first tries to take whole message (length + identifier = 151), but buffer is incomplete
1✔
1908
                assert_eq!(needed, Needed::new(129));
1✔
1909
            }
1910
            _ => {
1911
                panic!("Unexpected behavior. Should be incomplete.");
1912
            }
1913
        }
1914

1915
        //repeat for different case-scenarios:
1916
        // - message is valid
1917
        // some invalid character
1918
    }
1✔
1919

1920
    #[test]
1921
    fn test_parse_notice_response() {
1✔
1922
        // N   .SDEBUG VDEBUG C23505 Mduplicate key value violates unique constraint "unique_a" DKey (a)=(mea5) already exists. Fnbtinsert.c L397 R_bt_check_unique
1✔
1923
        let buf: &[u8] = &[
1✔
1924
            0x4e, 0x00, 0x00, 0x00, 0x99, 0x53, 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x56, 0x44,
1✔
1925
            0x45, 0x42, 0x55, 0x47, 0x00, 0x43, 0x32, 0x33, 0x35, 0x30, 0x35, 0x00, 0x4d, 0x64,
1✔
1926
            0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x76,
1✔
1927
            0x61, 0x6c, 0x75, 0x65, 0x20, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20,
1✔
1928
            0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61,
1✔
1929
            0x69, 0x6e, 0x74, 0x20, 0x22, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x61, 0x22,
1✔
1930
            0x00, 0x44, 0x4b, 0x65, 0x79, 0x20, 0x28, 0x61, 0x29, 0x3d, 0x28, 0x6d, 0x65, 0x61,
1✔
1931
            0x35, 0x29, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x65, 0x78, 0x69,
1✔
1932
            0x73, 0x74, 0x73, 0x2e, 0x00, 0x46, 0x6e, 0x62, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72,
1✔
1933
            0x74, 0x2e, 0x63, 0x00, 0x4c, 0x33, 0x39, 0x37, 0x00, 0x52, 0x5f, 0x62, 0x74, 0x5f,
1✔
1934
            0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x00, 0x00,
1✔
1935
        ];
1✔
1936

1✔
1937
        // expected result
1✔
1938
        let field1 = PgsqlErrorNoticeMessageField {
1✔
1939
            field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable,
1✔
1940
            field_value: br#"DEBUG"#.to_vec(),
1✔
1941
        };
1✔
1942
        let field2 = PgsqlErrorNoticeMessageField {
1✔
1943
            field_type: PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
1✔
1944
            field_value: br#"DEBUG"#.to_vec(),
1✔
1945
        };
1✔
1946
        let field3 = PgsqlErrorNoticeMessageField {
1✔
1947
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
1✔
1948
            field_value: br#"23505"#.to_vec(),
1✔
1949
        };
1✔
1950
        let field4 = PgsqlErrorNoticeMessageField {
1✔
1951
            field_type: PgsqlErrorNoticeFieldType::Message,
1✔
1952
            field_value: br#"duplicate key value violates unique constraint "unique_a""#.to_vec(),
1✔
1953
        };
1✔
1954
        let field5 = PgsqlErrorNoticeMessageField {
1✔
1955
            field_type: PgsqlErrorNoticeFieldType::Detail,
1✔
1956
            field_value: br#"Key (a)=(mea5) already exists."#.to_vec(),
1✔
1957
        };
1✔
1958
        let field6 = PgsqlErrorNoticeMessageField {
1✔
1959
            field_type: PgsqlErrorNoticeFieldType::File,
1✔
1960
            field_value: br#"nbtinsert.c"#.to_vec(),
1✔
1961
        };
1✔
1962
        let field7 = PgsqlErrorNoticeMessageField {
1✔
1963
            field_type: PgsqlErrorNoticeFieldType::Line,
1✔
1964
            field_value: br#"397"#.to_vec(),
1✔
1965
        };
1✔
1966
        let field8 = PgsqlErrorNoticeMessageField {
1✔
1967
            field_type: PgsqlErrorNoticeFieldType::Routine,
1✔
1968
            field_value: br#"_bt_check_unique"#.to_vec(),
1✔
1969
        };
1✔
1970

1✔
1971
        let mut payload = ErrorNoticeMessage::new(b'N', 153);
1✔
1972
        payload.message_body.push(field1);
1✔
1973
        payload.message_body.push(field2);
1✔
1974
        payload.message_body.push(field3);
1✔
1975
        payload.message_body.push(field4);
1✔
1976
        payload.message_body.push(field5);
1✔
1977
        payload.message_body.push(field6);
1✔
1978
        payload.message_body.push(field7);
1✔
1979
        payload.message_body.push(field8);
1✔
1980

1✔
1981
        let ok_res = PgsqlBEMessage::NoticeResponse(payload);
1✔
1982

1✔
1983
        let result = pgsql_parse_response(buf);
1✔
1984
        match result {
1985
            Ok((remainder, message)) => {
1✔
1986
                assert_eq!(message, ok_res);
1✔
1987
                assert_eq!(remainder.len(), 0);
1✔
1988
            }
1989
            Err(Err::Error(err)) => {
1990
                panic!("Shouldn't be error: {:?}", err);
1991
            }
1992
            Err(Err::Incomplete(needed)) => {
1993
                panic!("Should not be Incomplete! Needed: {:?} ", needed);
1994
            }
1995
            _ => {
1996
                panic!("Unexpected behavior");
1997
            }
1998
        }
1999
    }
1✔
2000

2001
    #[test]
2002
    fn test_parse_sasl_authentication_message() {
1✔
2003
        let buf: &[u8] = &[
1✔
2004
            /* identifier R */ 0x52, /* length 28 */ 0x00, 0x00, 0x00, 0x1c,
1✔
2005
            /* auth_type */ 0x00, 0x00, 0x00, 0x0a, /* SCRAM-SHA-256-PLUS */ 0x53, 0x43,
1✔
2006
            0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x50, 0x4c,
1✔
2007
            0x55, 0x53, 0x00, 0x00,
1✔
2008
        ];
1✔
2009
        let mechanism = vec![SASLAuthenticationMechanism::ScramSha256Plus];
1✔
2010
        let ok_res = PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage {
1✔
2011
            identifier: b'R',
1✔
2012
            length: 28,
1✔
2013
            auth_type: 10,
1✔
2014
            auth_mechanisms: mechanism,
1✔
2015
        });
1✔
2016

1✔
2017
        let result = pgsql_parse_response(buf);
1✔
2018
        match result {
2019
            Ok((remainder, message)) => {
1✔
2020
                assert_eq!(message, ok_res);
1✔
2021
                assert_eq!(remainder.len(), 0);
1✔
2022
            }
2023
            Err(Err::Error(err)) => {
2024
                panic!("Shouldn't be error: {:?}", err);
2025
            }
2026
            Err(Err::Incomplete(needed)) => {
2027
                panic!("Should not be Incomplete! Needed: {:?}", needed);
2028
            }
2029
            _ => {
2030
                panic!("Unexpected behavior");
2031
            }
2032
        }
2033

2034
        let buf: &[u8] = &[
1✔
2035
            /* identifier R */ 0x52, /* length 42 */ 0x00, 0x00, 0x00, 0x2a,
1✔
2036
            /* auth_type */ 0x00, 0x00, 0x00, 0x0a, /* SCRAM-SHA-256-PLUS */ 0x53, 0x43,
1✔
2037
            0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x50, 0x4c,
1✔
2038
            0x55, 0x53, 0x00, /* SCRAM-SHA-256 */ 0x53, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x53,
1✔
2039
            0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x00, 0x00,
1✔
2040
        ];
1✔
2041
        let mechanism = vec![
1✔
2042
            SASLAuthenticationMechanism::ScramSha256Plus,
1✔
2043
            SASLAuthenticationMechanism::ScramSha256,
1✔
2044
        ];
1✔
2045
        let ok_res = PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage {
1✔
2046
            identifier: b'R',
1✔
2047
            length: 42,
1✔
2048
            auth_type: 10,
1✔
2049
            auth_mechanisms: mechanism,
1✔
2050
        });
1✔
2051

1✔
2052
        let result = pgsql_parse_response(buf);
1✔
2053
        match result {
2054
            Ok((remainder, message)) => {
1✔
2055
                assert_eq!(message, ok_res);
1✔
2056
                assert_eq!(remainder.len(), 0);
1✔
2057
            }
2058
            Err(Err::Error(err)) => {
2059
                panic!("Shouldn't be error: {:?}", err);
2060
            }
2061
            Err(Err::Incomplete(needed)) => {
2062
                panic!("Should not be Incomplete! Needed: {:?}", needed);
2063
            }
2064
            _ => {
2065
                panic!("Unexpected behavior");
2066
            }
2067
        }
2068

2069
        let incomplete_result = pgsql_parse_response(&buf[0..27]);
1✔
2070
        match incomplete_result {
1✔
2071
            Ok((_remainder, _message)) => panic!("Should not be Ok(_), expected Incomplete!"),
2072
            Err(Err::Error(err)) => {
2073
                panic!("Should not be error {:?}, expected Incomplete!", err)
2074
            }
2075
            Err(Err::Incomplete(needed)) => assert_eq!(needed, Needed::new(16)),
1✔
2076
            _ => panic!("Unexpected behavior, expected Incomplete."),
2077
        }
2078
    }
1✔
2079

2080
    #[test]
2081
    fn test_parse_sasl_continue_authentication_message() {
1✔
2082
        // As found in: https://blog.hackeriet.no/Better-password-hashing-in-PostgreSQL/
1✔
2083
        let buf: &[u8] = &[
1✔
2084
            /* 'R' */ 0x52, /* 92 */ 0x00, 0x00, 0x00, 0x5c, /* 11 */ 0x00, 0x00,
1✔
2085
            0x00, 0x0b, /* challenge data*/ 0x72, 0x3d, 0x2f, 0x7a, 0x2b, 0x67, 0x69, 0x5a,
1✔
2086
            0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41, 0x65, 0x48, 0x72,
1✔
2087
            0x37, 0x63, 0x76, 0x70, 0x71, 0x56, 0x33, 0x75, 0x6f, 0x37, 0x47, 0x2f, 0x62, 0x4a,
1✔
2088
            0x42, 0x49, 0x4a, 0x4f, 0x33, 0x70, 0x6a, 0x56, 0x4d, 0x37, 0x74, 0x33, 0x6e, 0x67,
1✔
2089
            0x2c, 0x73, 0x3d, 0x34, 0x55, 0x56, 0x36, 0x38, 0x62, 0x49, 0x6b, 0x43, 0x38, 0x66,
1✔
2090
            0x39, 0x2f, 0x58, 0x38, 0x78, 0x48, 0x37, 0x61, 0x50, 0x68, 0x67, 0x3d, 0x3d, 0x2c,
1✔
2091
            0x69, 0x3d, 0x34, 0x30, 0x39, 0x36,
1✔
2092
        ];
1✔
2093

1✔
2094
        let ok_res = PgsqlBEMessage::AuthenticationSASLContinue(
1✔
2095
            AuthenticationMessage {
1✔
2096
                identifier: b'R',
1✔
2097
                length: 92,
1✔
2098
                auth_type: 11,
1✔
2099
                payload: br#"r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,s=4UV68bIkC8f9/X8xH7aPhg==,i=4096"#.to_vec(),
1✔
2100
        });
1✔
2101

1✔
2102
        let result = pgsql_parse_response(buf);
1✔
2103
        match result {
2104
            Ok((remainder, message)) => {
1✔
2105
                assert_eq!(message, ok_res);
1✔
2106
                assert_eq!(remainder.len(), 0);
1✔
2107
            }
2108
            Err(Err::Error(err)) => {
2109
                panic!("Shouldn't be error {:?} expected Ok(_)", err)
2110
            }
2111
            Err(Err::Incomplete(needed)) => {
2112
                panic!("shouldn't be incomplete {:?}, expected Ok(_)", needed)
2113
            }
2114
            _ => panic!("Unexpected behavior, expected Ok(_)"),
2115
        }
2116

2117
        let result_incomplete = pgsql_parse_response(&buf[0..31]);
1✔
2118
        match result_incomplete {
1✔
2119
            Ok((_remainder, _message)) => panic!("Should not be Ok(_), expected Incomplete!"),
2120
            Err(Err::Error(err)) => {
2121
                panic!("Shouldn't be error {:?} expected Incomplete!", err)
2122
            }
2123
            Err(Err::Incomplete(needed)) => {
1✔
2124
                assert_eq!(needed, Needed::new(62));
1✔
2125
            }
2126
            _ => panic!("Unexpected behavior, expected Ok(_)"),
2127
        }
2128
    }
1✔
2129

2130
    #[test]
2131
    fn test_parse_sasl_final_authentication_message() {
1✔
2132
        let buf: &[u8] = &[
1✔
2133
            /* R */ 0x52, /* 54 */ 0x00, 0x00, 0x00, 0x36, /* 12 */ 0x00, 0x00,
1✔
2134
            0x00, 0x0c, /* signature */ 0x76, 0x3d, 0x64, 0x31, 0x50, 0x58, 0x61, 0x38, 0x54,
1✔
2135
            0x4b, 0x46, 0x50, 0x5a, 0x72, 0x52, 0x33, 0x4d, 0x42, 0x52, 0x6a, 0x4c, 0x79, 0x33,
1✔
2136
            0x2b, 0x4a, 0x36, 0x79, 0x78, 0x72, 0x66, 0x77, 0x2f, 0x7a, 0x7a, 0x70, 0x38, 0x59,
1✔
2137
            0x54, 0x39, 0x65, 0x78, 0x56, 0x37, 0x73, 0x38, 0x3d,
1✔
2138
        ];
1✔
2139
        let ok_res = PgsqlBEMessage::AuthenticationSASLFinal(AuthenticationMessage {
1✔
2140
            identifier: b'R',
1✔
2141
            length: 54,
1✔
2142
            auth_type: 12,
1✔
2143
            payload: br#"v=d1PXa8TKFPZrR3MBRjLy3+J6yxrfw/zzp8YT9exV7s8="#.to_vec(),
1✔
2144
        });
1✔
2145

1✔
2146
        let result = pgsql_parse_response(buf);
1✔
2147
        match result {
2148
            Ok((remainder, message)) => {
1✔
2149
                assert_eq!(message, ok_res);
1✔
2150
                assert_eq!(remainder.len(), 0);
1✔
2151
            }
2152
            Err(Err::Error(err)) => {
2153
                panic!("Shouldn't be error {:?}, expected Ok(_)", err);
2154
            }
2155
            Err(Err::Incomplete(needed)) => {
2156
                panic!("Shouldn't be incomplete {:?}, expected OK(_)", needed);
2157
            }
2158
            _ => panic!("Unexpected behavior, expected Ok(_)"),
2159
        }
2160

2161
        let result_incomplete = pgsql_parse_response(&buf[0..34]);
1✔
2162
        match result_incomplete {
1✔
2163
            Err(Err::Incomplete(needed)) => {
1✔
2164
                assert_eq!(needed, Needed::new(21));
1✔
2165
            }
2166
            _ => panic!("Unexpected behavior, expected incomplete."),
2167
        }
2168

2169
        let bad_buf: &[u8] = &[
1✔
2170
            /* ` */ 0x60, /* 54 */ 0x00, 0x00, 0x00, 0x36, /* 12 */ 0x00, 0x00,
1✔
2171
            0x00, 0x0c, /* signature */ 0x76, 0x3d, 0x64, 0x31, 0x50, 0x58, 0x61, 0x38, 0x54,
1✔
2172
            0x4b, 0x46, 0x50, 0x5a, 0x72, 0x52, 0x33, 0x4d, 0x42, 0x52, 0x6a, 0x4c, 0x79, 0x33,
1✔
2173
            0x2b, 0x4a, 0x36, 0x79, 0x78, 0x72, 0x66, 0x77, 0x2f, 0x7a, 0x7a, 0x70, 0x38, 0x59,
1✔
2174
            0x54, 0x39, 0x65, 0x78, 0x56, 0x37, 0x73, 0x38, 0x3d,
1✔
2175
        ];
1✔
2176
        let (remainder, result) =
1✔
2177
            pgsql_parse_response(bad_buf).expect("parsing sasl final response failed");
1✔
2178
        let res = PgsqlBEMessage::UnknownMessageType(RegularPacket {
1✔
2179
            identifier: b'`',
1✔
2180
            length: 54,
1✔
2181
            payload: bad_buf[5..].to_vec(),
1✔
2182
        });
1✔
2183
        assert_eq!(result, res);
1✔
2184
        assert!(remainder.is_empty());
1✔
2185
    }
1✔
2186

2187
    #[test]
2188
    fn test_parse_sasl_frontend_messages() {
1✔
2189
        // SASL Initial Response
1✔
2190
        // (as seen in https://blog.hackeriet.no/Better-password-hashing-in-PostgreSQL/)
1✔
2191
        let buf: &[u8] = &[
1✔
2192
            /* p */ 0x70, /* 54 */ 0x00, 0x00, 0x00, 0x36,
1✔
2193
            /* sasl mechanism */ 0x53, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d,
1✔
2194
            0x32, 0x35, 0x36, 0x00, /* 32 */ 0x00, 0x00, 0x00, 0x20,
1✔
2195
            /* FE 1st msg */ 0x6e, 0x2c, 0x2c, 0x6e, 0x3d, 0x2c, 0x72, 0x3d, 0x2f, 0x7a, 0x2b,
1✔
2196
            0x67, 0x69, 0x5a, 0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41,
1✔
2197
            0x65, 0x48, 0x72, 0x37, 0x63, 0x76, 0x70,
1✔
2198
        ];
1✔
2199
        let ok_res = PgsqlFEMessage::SASLInitialResponse(SASLInitialResponsePacket {
1✔
2200
            identifier: b'p',
1✔
2201
            length: 54,
1✔
2202
            auth_mechanism: SASLAuthenticationMechanism::ScramSha256,
1✔
2203
            param_length: 32,
1✔
2204
            sasl_param: br#"n,,n=,r=/z+giZiTxAH7r8sNAeHr7cvp"#.to_vec(),
1✔
2205
        });
1✔
2206

1✔
2207
        let result = parse_sasl_initial_response(buf);
1✔
2208
        match result {
2209
            Ok((remainder, message)) => {
1✔
2210
                assert_eq!(message, ok_res);
1✔
2211
                assert_eq!(remainder.len(), 0);
1✔
2212
            }
2213
            Err(Err::Error(err)) => {
2214
                panic!("Shouldn't be error {:?}, expected Ok(_)", err)
2215
            }
2216
            Err(Err::Incomplete(needed)) => {
2217
                panic!("Shouldn't be incomplete: {:?}, expected Ok(_)", needed)
2218
            }
2219
            _ => panic!("Unexpected behavior, expected Ok(_)"),
2220
        }
2221

2222
        let buf: &[u8] = &[
1✔
2223
            /* p */ 0x70, /* 108 */ 0x00, 0x00, 0x00, 0x6c, /* final msg*/ 0x63,
1✔
2224
            0x3d, 0x62, 0x69, 0x77, 0x73, 0x2c, 0x72, 0x3d, 0x2f, 0x7a, 0x2b, 0x67, 0x69, 0x5a,
1✔
2225
            0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41, 0x65, 0x48, 0x72,
1✔
2226
            0x37, 0x63, 0x76, 0x70, 0x71, 0x56, 0x33, 0x75, 0x6f, 0x37, 0x47, 0x2f, 0x62, 0x4a,
1✔
2227
            0x42, 0x49, 0x4a, 0x4f, 0x33, 0x70, 0x6a, 0x56, 0x4d, 0x37, 0x74, 0x33, 0x6e, 0x67,
1✔
2228
            0x2c, 0x70, 0x3d, 0x41, 0x46, 0x70, 0x53, 0x59, 0x48, 0x2f, 0x4b, 0x2f, 0x38, 0x62,
1✔
2229
            0x75, 0x78, 0x31, 0x6d, 0x52, 0x50, 0x55, 0x77, 0x78, 0x54, 0x65, 0x38, 0x6c, 0x42,
1✔
2230
            0x75, 0x49, 0x50, 0x45, 0x79, 0x68, 0x69, 0x2f, 0x37, 0x55, 0x46, 0x50, 0x51, 0x70,
1✔
2231
            0x53, 0x72, 0x34, 0x41, 0x3d,
1✔
2232
        ];
1✔
2233

1✔
2234
        let ok_res = PgsqlFEMessage::SASLResponse(
1✔
2235
            RegularPacket {
1✔
2236
                identifier: b'p',
1✔
2237
                length: 108,
1✔
2238
                payload: br#"c=biws,r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,p=AFpSYH/K/8bux1mRPUwxTe8lBuIPEyhi/7UFPQpSr4A="#.to_vec(),
1✔
2239
            });
1✔
2240

1✔
2241
        let result = parse_sasl_response(buf);
1✔
2242
        match result {
2243
            Ok((_remainder, message)) => {
1✔
2244
                assert_eq!(message, ok_res);
1✔
2245
            }
2246
            Err(Err::Error(err)) => {
2247
                panic!("Shouldn't be error: {:?} expected Ok(_)", err)
2248
            }
2249
            Err(Err::Incomplete(needed)) => {
2250
                panic!("Shouldn't be incomplete: {:?}, expected Ok(_)", needed)
2251
            }
2252
            _ => panic!("Unexpected behavior, should be Ok(_)"),
2253
        }
2254
    }
1✔
2255

2256
    // Test messages with fixed formats, like AuthenticationSSPI
2257
    #[test]
2258
    fn test_parse_simple_authentication_requests() {
1✔
2259
        let buf: &[u8] = &[
1✔
2260
            /* R */ 0x52, /* 8 */ 0x00, 0x00, 0x00, 0x08, /* 9 */ 0x00, 0x00, 0x00,
1✔
2261
            0x09,
1✔
2262
        ];
1✔
2263

1✔
2264
        let ok_res = PgsqlBEMessage::AuthenticationSSPI(AuthenticationMessage {
1✔
2265
            identifier: b'R',
1✔
2266
            length: 8,
1✔
2267
            auth_type: 9,
1✔
2268
            payload: Vec::<u8>::new(),
1✔
2269
        });
1✔
2270

1✔
2271
        let (_remainder, result) = pgsql_parse_response(buf).unwrap();
1✔
2272
        assert_eq!(result, ok_res);
1✔
2273
    }
1✔
2274

2275
    #[test]
2276
    fn test_parse_response() {
1✔
2277
        // An SSL response - N
1✔
2278
        let buf: &[u8] = &[0x4e];
1✔
2279
        let response_ok = PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLRejected);
1✔
2280
        let (_remainder, result) = parse_ssl_response(buf).unwrap();
1✔
2281
        assert_eq!(result, response_ok);
1✔
2282

2283
        // An SSL response - S
2284
        let buf: &[u8] = &[0x53];
1✔
2285
        let response_ok = PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLAccepted);
1✔
2286

1✔
2287
        let (_remainder, result) = parse_ssl_response(buf).unwrap();
1✔
2288
        assert_eq!(result, response_ok);
1✔
2289

2290
        // Not an SSL response
2291
        let buf: &[u8] = &[0x52];
1✔
2292
        let result = parse_ssl_response(buf);
1✔
2293
        assert!(result.is_err());
1✔
2294

2295
        // - auth MD5
2296
        let buf: &[u8] = &[
1✔
2297
            0x52, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x05, 0xf2, 0x11, 0xa3, 0xed,
1✔
2298
        ];
1✔
2299
        let ok_res = PgsqlBEMessage::AuthenticationMD5Password(AuthenticationMessage {
1✔
2300
            identifier: b'R',
1✔
2301
            length: 12,
1✔
2302
            auth_type: 5,
1✔
2303
            payload: vec![0xf2, 0x11, 0xa3, 0xed],
1✔
2304
        });
1✔
2305
        let result = pgsql_parse_response(buf);
1✔
2306
        match result {
2307
            Ok((remainder, message)) => {
1✔
2308
                assert_eq!(message, ok_res);
1✔
2309
                assert_eq!(remainder.len(), 0);
1✔
2310
            }
2311
            Err(Err::Error(err)) => {
2312
                panic!("Shouldn't be error: {:?}", err);
2313
            }
2314
            Err(Err::Incomplete(needed)) => {
2315
                panic!("Should not be Incomplete! Needed: {:?}", needed);
2316
            }
2317
            _ => {
2318
                panic!("Unexpected behavior");
2319
            }
2320
        }
2321

2322
        // - auth clear text...
2323
        let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03];
1✔
2324
        let ok_res = PgsqlBEMessage::AuthenticationCleartextPassword(AuthenticationMessage {
1✔
2325
            identifier: b'R',
1✔
2326
            length: 8,
1✔
2327
            auth_type: 3,
1✔
2328
            payload: Vec::<u8>::new(),
1✔
2329
        });
1✔
2330
        let result = pgsql_parse_response(buf);
1✔
2331
        match result {
2332
            Ok((remainder, message)) => {
1✔
2333
                assert_eq!(remainder.len(), 0);
1✔
2334
                assert_eq!(message, ok_res);
1✔
2335
            }
2336
            Err(Err::Error(err)) => {
2337
                panic!("Shouldn't be error: {:?}", err);
2338
            }
2339
            Err(Err::Incomplete(needed)) => {
2340
                panic!("Should not be incomplete. Needed {:?}", needed);
2341
            }
2342
            _ => {
2343
                panic!("Unexpected behavior");
2344
            }
2345
        }
2346

2347
        let result = pgsql_parse_response(&buf[0..6]);
1✔
2348
        assert!(result.is_err());
1✔
2349

2350
        let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03];
1✔
2351
        let result = pgsql_parse_response(buf);
1✔
2352
        assert!(result.is_err());
1✔
2353

2354
        // - auth Ok
2355
        let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00];
1✔
2356
        let ok_res = PgsqlBEMessage::AuthenticationOk(AuthenticationMessage {
1✔
2357
            identifier: b'R',
1✔
2358
            length: 8,
1✔
2359
            auth_type: 0,
1✔
2360
            payload: Vec::<u8>::new(),
1✔
2361
        });
1✔
2362
        let result = pgsql_parse_response(buf);
1✔
2363
        match result {
2364
            Ok((remainder, message)) => {
1✔
2365
                assert_eq!(message, ok_res);
1✔
2366
                assert_eq!(remainder.len(), 0);
1✔
2367
            }
2368
            Err(Err::Error(err)) => {
2369
                panic!("Should not be error {:?}", err);
2370
            }
2371
            Err(Err::Incomplete(needed)) => {
2372
                panic!("Should not be incomplete. Needed: {:?}", needed);
2373
            }
2374
            _ => {
2375
                panic!("Unexpected behavior!");
2376
            }
2377
        }
2378

2379
        //A series of response messages from the backend:
2380
        // R   ·    S   ·application_name
2381
        // S   ·client_encoding UTF8 S   ·DateStyle ISO, MDY
2382
        // S   &default_transaction_read_only off S   ·in_hot_standby off
2383
        // S   ·integer_datetimes on S   ·IntervalStyle postgres
2384
        // S   ·is_superuser off S   ·server_encoding UTF8
2385
        // S   ·server_version 14.5 S   "session_authorization ctfpost
2386
        // S   #standard_conforming_strings on S   ·TimeZone Europe/Paris
2387
        // K      ···O··Z   ·I
2388
        let buf = &[
1✔
2389
            0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x16,
1✔
2390
            0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61,
1✔
2391
            0x6d, 0x65, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x19, 0x63, 0x6c, 0x69, 0x65, 0x6e,
1✔
2392
            0x74, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x55, 0x54, 0x46,
1✔
2393
            0x38, 0x00, 0x53, 0x00, 0x00, 0x00, 0x17, 0x44, 0x61, 0x74, 0x65, 0x53, 0x74, 0x79,
1✔
2394
            0x6c, 0x65, 0x00, 0x49, 0x53, 0x4f, 0x2c, 0x20, 0x4d, 0x44, 0x59, 0x00, 0x53, 0x00,
1✔
2395
            0x00, 0x00, 0x26, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x74, 0x72, 0x61,
1✔
2396
            0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f,
1✔
2397
            0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x66, 0x66, 0x00, 0x53, 0x00, 0x00, 0x00, 0x17,
1✔
2398
            0x69, 0x6e, 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79,
1✔
2399
            0x00, 0x6f, 0x66, 0x66, 0x00, 0x53, 0x00, 0x00, 0x00, 0x19, 0x69, 0x6e, 0x74, 0x65,
1✔
2400
            0x67, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x00,
1✔
2401
            0x6f, 0x6e, 0x00, 0x53, 0x00, 0x00, 0x00, 0x1b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
1✔
2402
            0x61, 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x00, 0x70, 0x6f, 0x73, 0x74, 0x67, 0x72,
1✔
2403
            0x65, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x15, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x70,
1✔
2404
            0x65, 0x72, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f, 0x66, 0x66, 0x00, 0x53, 0x00, 0x00,
1✔
2405
            0x00, 0x19, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64,
1✔
2406
            0x69, 0x6e, 0x67, 0x00, 0x55, 0x54, 0x46, 0x38, 0x00, 0x53, 0x00, 0x00, 0x00, 0x18,
1✔
2407
            0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
1✔
2408
            0x00, 0x31, 0x34, 0x2e, 0x35, 0x00, 0x53, 0x00, 0x00, 0x00, 0x22, 0x73, 0x65, 0x73,
1✔
2409
            0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
1✔
2410
            0x74, 0x69, 0x6f, 0x6e, 0x00, 0x63, 0x74, 0x66, 0x70, 0x6f, 0x73, 0x74, 0x00, 0x53,
1✔
2411
            0x00, 0x00, 0x00, 0x23, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x63,
1✔
2412
            0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69,
1✔
2413
            0x6e, 0x67, 0x73, 0x00, 0x6f, 0x6e, 0x00, 0x53, 0x00, 0x00, 0x00, 0x1a, 0x54, 0x69,
1✔
2414
            0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x00, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
1✔
2415
            0x50, 0x61, 0x72, 0x69, 0x73, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b,
1✔
2416
            0x8d, 0xcf, 0x4f, 0xb6, 0xcf, 0x5a, 0x00, 0x00, 0x00, 0x05, 0x49,
1✔
2417
        ];
1✔
2418

1✔
2419
        let result = pgsql_parse_response(buf);
1✔
2420
        assert!(result.is_ok());
1✔
2421
    }
1✔
2422

2423
    #[test]
2424
    fn test_parse_row_description() {
1✔
2425
        // RowDescription message
1✔
2426
        // T  ..
1✔
2427
        // source   @  .   .......  version   @  .   .......  sid   @  .   . .....
1✔
2428
        let buffer: &[u8] = &[
1✔
2429
            0x54, 0x00, 0x00, 0x00, 0x50, 0x00, 0x03, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00,
1✔
2430
            0x00, 0x00, 0x40, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff,
1✔
2431
            0xff, 0xff, 0x00, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,
1✔
2432
            0x40, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1✔
2433
            0x00, 0x00, 0x73, 0x69, 0x64, 0x00, 0x00, 0x00, 0x40, 0x09, 0x00, 0x03, 0x00, 0x00,
1✔
2434
            0x00, 0x14, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
1✔
2435
        ];
1✔
2436

1✔
2437
        let field1 = RowField {
1✔
2438
            field_name: br#"source"#.to_vec(),
1✔
2439
            table_oid: 16393,
1✔
2440
            column_index: 1,
1✔
2441
            data_type_oid: 25,
1✔
2442
            data_type_size: -1,
1✔
2443
            type_modifier: -1,
1✔
2444
            format_code: 0,
1✔
2445
        };
1✔
2446

1✔
2447
        let field2 = RowField {
1✔
2448
            field_name: br#"version"#.to_vec(),
1✔
2449
            table_oid: 16393,
1✔
2450
            column_index: 2,
1✔
2451
            data_type_oid: 25,
1✔
2452
            data_type_size: -1,
1✔
2453
            type_modifier: -1,
1✔
2454
            format_code: 0,
1✔
2455
        };
1✔
2456

1✔
2457
        let field3 = RowField {
1✔
2458
            field_name: br#"sid"#.to_vec(),
1✔
2459
            table_oid: 16393,
1✔
2460
            column_index: 3,
1✔
2461
            data_type_oid: 20,
1✔
2462
            data_type_size: 8,
1✔
2463
            type_modifier: -1,
1✔
2464
            format_code: 0,
1✔
2465
        };
1✔
2466

1✔
2467
        let fields_vec = vec![field1, field2, field3];
1✔
2468

1✔
2469
        let ok_res = PgsqlBEMessage::RowDescription(RowDescriptionMessage {
1✔
2470
            identifier: b'T',
1✔
2471
            length: 80,
1✔
2472
            field_count: 3,
1✔
2473
            fields: fields_vec,
1✔
2474
        });
1✔
2475

1✔
2476
        let result = parse_row_description(buffer);
1✔
2477

2478
        match result {
2479
            Ok((rem, response)) => {
1✔
2480
                assert_eq!(response, ok_res);
1✔
2481
                assert!(rem.is_empty());
1✔
2482
            }
2483
            Err(Err::Incomplete(needed)) => {
2484
                panic!("Should not be Incomplete! Needed: {:?}", needed);
2485
            }
2486
            Err(Err::Error(err)) => {
2487
                panic!("Shouldn't be error: {:?}", err);
2488
            }
2489
            _ => {
2490
                panic!("Unexpected behavior");
2491
            }
2492
        }
2493
    }
1✔
2494

2495
    #[test]
2496
    fn test_parse_data_row() {
1✔
2497
        let buffer: &[u8] = &[
1✔
2498
            0x44, 0x00, 0x00, 0x00, 0x23, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x65, 0x74, 0x2f,
1✔
2499
            0x6f, 0x70, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x03, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00,
1✔
2500
            0x07, 0x32, 0x30, 0x32, 0x31, 0x37, 0x30, 0x31,
1✔
2501
        ];
1✔
2502

1✔
2503
        let result = parse_consolidated_data_row(buffer);
1✔
2504
        assert!(result.is_ok());
1✔
2505
    }
1✔
2506

2507
    #[test]
2508
    fn test_command_complete() {
1✔
2509
        let buffer: &[u8] = &[
1✔
2510
            0x43, 0x00, 0x00, 0x00, 0x0d, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x20, 0x33, 0x00,
1✔
2511
        ];
1✔
2512

1✔
2513
        let ok_res = PgsqlBEMessage::CommandComplete(RegularPacket {
1✔
2514
            identifier: b'C',
1✔
2515
            length: 13,
1✔
2516
            payload: b"SELECT 3".to_vec(),
1✔
2517
        });
1✔
2518

1✔
2519
        let result = pgsql_parse_response(buffer);
1✔
2520

2521
        match result {
2522
            Ok((rem, message)) => {
1✔
2523
                assert_eq!(ok_res, message);
1✔
2524
                assert!(rem.is_empty());
1✔
2525
            }
2526
            Err(Err::Incomplete(needed)) => {
2527
                panic!(
2528
                    "Shouldn't be Incomplete! Expected Ok(). Needed: {:?}",
2529
                    needed
2530
                );
2531
            }
2532
            Err(Err::Error(err)) => {
2533
                panic!("Shouldn't be Error: {:?}, expected Ok()", err);
2534
            }
2535
            _ => {
2536
                panic!("Unexpected behavior, should be Ok()");
2537
            }
2538
        }
2539
    }
1✔
2540

2541
    #[test]
2542
    fn test_parse_notification_response() {
1✔
2543
        // Handcrafted notification response message, based on documentation specification
1✔
2544
        // identifier: 'A'
1✔
2545
        // length: 39
1✔
2546
        // pid: 61
1✔
2547
        // channel_name: test_channel
1✔
2548
        // payload: Test notification
1✔
2549
        let buf: &[u8] = &[
1✔
2550
            0x41, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3d, // test_channel
1✔
2551
            0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x00,
1✔
2552
            // Test notification
1✔
2553
            0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
1✔
2554
            0x69, 0x6f, 0x6e, 0x00,
1✔
2555
        ];
1✔
2556

1✔
2557
        let ok_res = PgsqlBEMessage::NotificationResponse(NotificationResponse {
1✔
2558
            identifier: b'A',
1✔
2559
            length: 39,
1✔
2560
            pid: 61,
1✔
2561
            channel_name: br#"test_channel"#.to_vec(),
1✔
2562
            payload: br#"Test notification"#.to_vec(),
1✔
2563
        });
1✔
2564

1✔
2565
        let (_rem, result) = pgsql_parse_response(buf).unwrap();
1✔
2566

1✔
2567
        assert_eq!(ok_res, result);
1✔
2568
    }
1✔
2569
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc