/src/suricata7/rust/src/pgsql/parser.rs
Line | Count | Source |
1 | | /* Copyright (C) 2022 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::nom7::take_until_and_consume; |
23 | | use nom7::branch::alt; |
24 | | use nom7::bytes::streaming::{tag, take, take_until, take_until1}; |
25 | | use nom7::character::streaming::{alphanumeric1, char}; |
26 | | use nom7::combinator::{all_consuming, cond, eof, map_parser, opt, peek, verify}; |
27 | | use nom7::error::{make_error, ErrorKind}; |
28 | | use nom7::multi::{many1, many_m_n, many_till}; |
29 | | use nom7::number::streaming::{be_i16, be_i32}; |
30 | | use nom7::number::streaming::{be_u16, be_u32, be_u8}; |
31 | | use nom7::sequence::{terminated, tuple}; |
32 | | use nom7::{Err, IResult}; |
33 | | |
34 | | pub const PGSQL_LENGTH_FIELD: u32 = 4; |
35 | | |
36 | | pub const PGSQL_DUMMY_PROTO_MAJOR: u16 = 1234; // 0x04d2 |
37 | | pub const PGSQL_DUMMY_PROTO_CANCEL_REQUEST: u16 = 5678; // 0x162e |
38 | | pub const PGSQL_DUMMY_PROTO_MINOR_SSL: u16 = 5679; //0x162f |
39 | | pub const _PGSQL_DUMMY_PROTO_MINOR_GSSAPI: u16 = 5680; // 0x1630 |
40 | | |
41 | 6.41M | fn parse_length(i: &[u8]) -> IResult<&[u8], u32> { |
42 | 6.41M | verify(be_u32, |&x| x >= PGSQL_LENGTH_FIELD)(i) |
43 | 6.41M | } |
44 | | |
45 | | #[derive(Debug, PartialEq, Eq)] |
46 | | pub enum PgsqlParameters { |
47 | | // startup parameters |
48 | | User, |
49 | | Database, |
50 | | Options, |
51 | | Replication, |
52 | | // runtime parameters |
53 | | ServerVersion, |
54 | | ServerEncoding, |
55 | | ClientEncoding, |
56 | | ApplicationName, |
57 | | DefaultTransactionReadOnly, |
58 | | InHotStandby, |
59 | | IsSuperuser, |
60 | | SessionAuthorization, |
61 | | DateStyle, |
62 | | IntervalStyle, |
63 | | TimeZone, |
64 | | IntegerDatetimes, |
65 | | StandardConformingStrings, |
66 | | UnknownParameter(Vec<u8>), |
67 | | } |
68 | | |
69 | | impl PgsqlParameters { |
70 | 0 | pub fn to_str(&self) -> &str { |
71 | 0 | match self { |
72 | 0 | PgsqlParameters::User => "user", |
73 | 0 | PgsqlParameters::Database => "database", |
74 | 0 | PgsqlParameters::Options => "options", |
75 | 0 | PgsqlParameters::Replication => "replication", |
76 | 0 | PgsqlParameters::ServerVersion => "server_version", |
77 | 0 | PgsqlParameters::ServerEncoding => "server_encoding", |
78 | 0 | PgsqlParameters::ClientEncoding => "client_encoding", |
79 | 0 | PgsqlParameters::ApplicationName => "application_name", |
80 | 0 | PgsqlParameters::DefaultTransactionReadOnly => "default_transaction_read_only", |
81 | 0 | PgsqlParameters::InHotStandby => "in_hot_standby", |
82 | 0 | PgsqlParameters::IsSuperuser => "is_superuser", |
83 | 0 | PgsqlParameters::SessionAuthorization => "session_authorization", |
84 | 0 | PgsqlParameters::DateStyle => "date_style", |
85 | 0 | PgsqlParameters::IntervalStyle => "interval_style", |
86 | 0 | PgsqlParameters::TimeZone => "time_zone", |
87 | 0 | PgsqlParameters::IntegerDatetimes => "integer_datetimes", |
88 | 0 | PgsqlParameters::StandardConformingStrings => "standard_conforming_strings", |
89 | 0 | PgsqlParameters::UnknownParameter(name) => { |
90 | 0 | std::str::from_utf8(name).unwrap_or("unknown_parameter") |
91 | | } |
92 | | } |
93 | 0 | } |
94 | | } |
95 | | |
96 | | impl From<&[u8]> for PgsqlParameters { |
97 | 2.43M | fn from(name: &[u8]) -> Self { |
98 | 2.43M | match name { |
99 | 2.43M | br#"user"# => PgsqlParameters::User, |
100 | 2.33M | br#"database"# => PgsqlParameters::Database, |
101 | 2.32M | br#"options"# => PgsqlParameters::Options, |
102 | 2.31M | br#"replication"# => PgsqlParameters::Replication, |
103 | 2.29M | br#"server_version"# => PgsqlParameters::ServerVersion, |
104 | 2.27M | br#"server_encoding"# => PgsqlParameters::ServerEncoding, |
105 | 781 | br#"client_encoding"# => PgsqlParameters::ClientEncoding, |
106 | 2.25M | br#"application_name"# => PgsqlParameters::ApplicationName, |
107 | 2.25M | br#"default_transaction_read_only"# => PgsqlParameters::DefaultTransactionReadOnly, |
108 | 395 | br#"in_hot_standby"# => PgsqlParameters::InHotStandby, |
109 | 2.23M | br#"is_superuser"# => PgsqlParameters::IsSuperuser, |
110 | 2.22M | br#"session_authorization"# => PgsqlParameters::SessionAuthorization, |
111 | 2.21M | br#"DateStyle"# => PgsqlParameters::DateStyle, |
112 | 2.20M | br#"IntervalStyle"# => PgsqlParameters::IntervalStyle, |
113 | 470 | br#"TimeZone"# => PgsqlParameters::TimeZone, |
114 | 2.19M | br#"integer_datetimes"# => PgsqlParameters::IntegerDatetimes, |
115 | 2.18M | br#"standard_conforming_strings"# => PgsqlParameters::StandardConformingStrings, |
116 | 2.34M | _ => PgsqlParameters::UnknownParameter(name.to_vec()), |
117 | | } |
118 | 2.43M | } |
119 | | } |
120 | | |
121 | | #[derive(Debug, PartialEq, Eq)] |
122 | | pub struct PgsqlParameter { |
123 | | pub name: PgsqlParameters, |
124 | | pub value: Vec<u8>, |
125 | | } |
126 | | |
127 | | #[derive(Debug, PartialEq, Eq)] |
128 | | pub struct PgsqlStartupParameters { |
129 | | pub user: PgsqlParameter, |
130 | | pub optional_params: Option<Vec<PgsqlParameter>>, |
131 | | } |
132 | | |
133 | | #[derive(Debug, PartialEq, Eq)] |
134 | | pub struct DummyStartupPacket { |
135 | | length: u32, |
136 | | proto_major: u16, |
137 | | proto_minor: u16, |
138 | | } |
139 | | |
140 | | #[derive(Debug, PartialEq, Eq)] |
141 | | pub struct StartupPacket { |
142 | | pub length: u32, |
143 | | pub proto_major: u16, |
144 | | pub proto_minor: u16, |
145 | | pub params: PgsqlStartupParameters, |
146 | | } |
147 | | |
148 | | #[derive(Debug, PartialEq, Eq)] |
149 | | pub struct RegularPacket { |
150 | | pub identifier: u8, |
151 | | pub length: u32, |
152 | | pub payload: Vec<u8>, |
153 | | } |
154 | | |
155 | | #[derive(Debug, PartialEq, Eq)] |
156 | | pub struct PgsqlErrorNoticeMessageField { |
157 | | pub field_type: PgsqlErrorNoticeFieldType, |
158 | | pub field_value: Vec<u8>, |
159 | | } |
160 | | |
161 | | #[derive(Debug, PartialEq, Eq)] |
162 | | pub struct ErrorNoticeMessage { |
163 | | pub identifier: u8, |
164 | | pub length: u32, |
165 | | pub message_body: Vec<PgsqlErrorNoticeMessageField>, |
166 | | } |
167 | | |
168 | | #[derive(Debug, PartialEq, Eq)] |
169 | | pub enum SSLResponseMessage { |
170 | | SSLAccepted, |
171 | | SSLRejected, |
172 | | InvalidResponse, |
173 | | } |
174 | | |
175 | | impl From<u8> for SSLResponseMessage { |
176 | 0 | fn from(identifier: u8) -> Self { |
177 | 0 | match identifier { |
178 | 0 | b'S' => Self::SSLAccepted, |
179 | 0 | b'N' => Self::SSLRejected, |
180 | 0 | _ => Self::InvalidResponse, |
181 | | } |
182 | 0 | } |
183 | | } |
184 | | |
185 | | impl From<char> for SSLResponseMessage { |
186 | 1.81k | fn from(identifier: char) -> Self { |
187 | 1.81k | match identifier { |
188 | 345 | 'S' => Self::SSLAccepted, |
189 | 1.46k | 'N' => Self::SSLRejected, |
190 | 0 | _ => Self::InvalidResponse, |
191 | | } |
192 | 1.81k | } |
193 | | } |
194 | | |
195 | | #[derive(Debug, PartialEq, Eq)] |
196 | | pub struct ParameterStatusMessage { |
197 | | pub identifier: u8, |
198 | | pub length: u32, |
199 | | pub param: PgsqlParameter, |
200 | | } |
201 | | |
202 | | #[derive(Debug, PartialEq, Eq)] |
203 | | pub struct BackendKeyDataMessage { |
204 | | pub identifier: u8, |
205 | | pub length: u32, |
206 | | pub backend_pid: u32, |
207 | | pub secret_key: u32, |
208 | | } |
209 | | |
210 | | #[derive(Debug, PartialEq, Eq)] |
211 | | pub struct ConsolidatedDataRowPacket { |
212 | | pub identifier: u8, |
213 | | pub row_cnt: u64, |
214 | | pub data_size: u64, |
215 | | } |
216 | | |
217 | | #[derive(Debug, PartialEq, Eq)] |
218 | | pub struct ReadyForQueryMessage { |
219 | | pub identifier: u8, |
220 | | pub length: u32, |
221 | | pub transaction_status: u8, |
222 | | } |
223 | | |
224 | | #[derive(Debug, PartialEq, Eq)] |
225 | | pub struct NotificationResponse { |
226 | | pub identifier: u8, |
227 | | pub length: u32, |
228 | | pub pid: u32, |
229 | | // two str fields, one right after the other |
230 | | pub channel_name: Vec<u8>, |
231 | | pub payload: Vec<u8>, |
232 | | } |
233 | | |
234 | | #[derive(Debug, PartialEq, Eq)] |
235 | | pub enum PgsqlBEMessage { |
236 | | SSLResponse(SSLResponseMessage), |
237 | | ErrorResponse(ErrorNoticeMessage), |
238 | | NoticeResponse(ErrorNoticeMessage), |
239 | | AuthenticationOk(AuthenticationMessage), |
240 | | AuthenticationCleartextPassword(AuthenticationMessage), |
241 | | AuthenticationMD5Password(AuthenticationMessage), |
242 | | AuthenticationSSPI(AuthenticationMessage), |
243 | | AuthenticationSASL(AuthenticationSASLMechanismMessage), |
244 | | AuthenticationSASLContinue(AuthenticationMessage), |
245 | | AuthenticationSASLFinal(AuthenticationMessage), |
246 | | ParameterStatus(ParameterStatusMessage), |
247 | | BackendKeyData(BackendKeyDataMessage), |
248 | | CommandComplete(RegularPacket), |
249 | | ReadyForQuery(ReadyForQueryMessage), |
250 | | RowDescription(RowDescriptionMessage), |
251 | | ConsolidatedDataRow(ConsolidatedDataRowPacket), |
252 | | NotificationResponse(NotificationResponse), |
253 | | UnknownMessageType(RegularPacket), |
254 | | } |
255 | | |
256 | | impl PgsqlBEMessage { |
257 | 0 | pub fn to_str(&self) -> &'static str { |
258 | 0 | match self { |
259 | 0 | PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLAccepted) => "ssl_accepted", |
260 | 0 | PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLRejected) => "ssl_rejected", |
261 | 0 | PgsqlBEMessage::ErrorResponse(_) => "error_response", |
262 | 0 | PgsqlBEMessage::NoticeResponse(_) => "notice_response", |
263 | 0 | PgsqlBEMessage::AuthenticationOk(_) => "authentication_ok", |
264 | | PgsqlBEMessage::AuthenticationCleartextPassword(_) => { |
265 | 0 | "authentication_cleartext_password" |
266 | | } |
267 | 0 | PgsqlBEMessage::AuthenticationMD5Password(_) => "authentication_md5_password", |
268 | 0 | PgsqlBEMessage::AuthenticationSSPI(_) => "authentication_sspi", |
269 | 0 | PgsqlBEMessage::AuthenticationSASL(_) => "authentication_sasl", |
270 | 0 | PgsqlBEMessage::AuthenticationSASLContinue(_) => "authentication_sasl_continue", |
271 | 0 | PgsqlBEMessage::AuthenticationSASLFinal(_) => "authentication_sasl_final", |
272 | 0 | PgsqlBEMessage::ParameterStatus(_) => "parameter_status", |
273 | 0 | PgsqlBEMessage::BackendKeyData(_) => "backend_key_data", |
274 | 0 | PgsqlBEMessage::CommandComplete(_) => "command_completed", |
275 | 0 | PgsqlBEMessage::ReadyForQuery(_) => "ready_for_query", |
276 | 0 | PgsqlBEMessage::RowDescription(_) => "row_description", |
277 | | PgsqlBEMessage::SSLResponse(SSLResponseMessage::InvalidResponse) => { |
278 | 0 | "invalid_be_message" |
279 | | } |
280 | 0 | PgsqlBEMessage::ConsolidatedDataRow(_) => "data_row", |
281 | 0 | PgsqlBEMessage::NotificationResponse(_) => "notification_response", |
282 | 0 | PgsqlBEMessage::UnknownMessageType(_) => "unknown_message_type" |
283 | | } |
284 | 0 | } |
285 | | |
286 | 10.1k | pub fn get_backendkey_info(&self) -> (u32, u32) { |
287 | 10.1k | match self { |
288 | 10.1k | PgsqlBEMessage::BackendKeyData(message) => { |
289 | 10.1k | return (message.backend_pid, message.secret_key); |
290 | | } |
291 | 0 | _ => (0, 0), |
292 | | } |
293 | 10.1k | } |
294 | | } |
295 | | |
296 | | #[derive(Debug, PartialEq, Eq, Clone)] |
297 | | pub enum SASLAuthenticationMechanism { |
298 | | ScramSha256, |
299 | | ScramSha256Plus, |
300 | | // UnknownMechanism, |
301 | | } |
302 | | |
303 | | impl SASLAuthenticationMechanism { |
304 | 0 | pub fn to_str(&self) -> &'static str { |
305 | 0 | match self { |
306 | 0 | SASLAuthenticationMechanism::ScramSha256 => "scram_SHA256", |
307 | 0 | SASLAuthenticationMechanism::ScramSha256Plus => "scram_SHA256_plus", |
308 | | } |
309 | 0 | } |
310 | | } |
311 | | |
312 | | #[derive(Debug, PartialEq, Eq)] |
313 | | pub struct TerminationMessage { |
314 | | pub identifier: u8, |
315 | | pub length: u32, |
316 | | } |
317 | | |
318 | | #[derive(Debug, PartialEq, Eq)] |
319 | | pub struct CancelRequestMessage { |
320 | | pub pid: u32, |
321 | | pub backend_key: u32, |
322 | | } |
323 | | |
324 | | #[derive(Debug, PartialEq, Eq)] |
325 | | pub enum PgsqlFEMessage { |
326 | | SSLRequest(DummyStartupPacket), |
327 | | StartupMessage(StartupPacket), |
328 | | PasswordMessage(RegularPacket), |
329 | | SASLInitialResponse(SASLInitialResponsePacket), |
330 | | SASLResponse(RegularPacket), |
331 | | SimpleQuery(RegularPacket), |
332 | | CancelRequest(CancelRequestMessage), |
333 | | Terminate(TerminationMessage), |
334 | | UnknownMessageType(RegularPacket), |
335 | | } |
336 | | |
337 | | impl PgsqlFEMessage { |
338 | 4 | pub fn to_str(&self) -> &'static str { |
339 | 4 | match self { |
340 | 0 | PgsqlFEMessage::StartupMessage(_) => "startup_message", |
341 | 0 | PgsqlFEMessage::SSLRequest(_) => "ssl_request", |
342 | 0 | PgsqlFEMessage::PasswordMessage(_) => "password_message", |
343 | 0 | PgsqlFEMessage::SASLInitialResponse(_) => "sasl_initial_response", |
344 | 0 | PgsqlFEMessage::SASLResponse(_) => "sasl_response", |
345 | 1 | PgsqlFEMessage::SimpleQuery(_) => "simple_query", |
346 | 0 | PgsqlFEMessage::CancelRequest(_) => "cancel_request", |
347 | 3 | PgsqlFEMessage::Terminate(_) => "termination_message", |
348 | 0 | PgsqlFEMessage::UnknownMessageType(_) => "unknown_message_type", |
349 | | } |
350 | 4 | } |
351 | | } |
352 | | |
353 | | #[derive(Debug, PartialEq, Eq)] |
354 | | pub struct AuthenticationMessage { |
355 | | pub identifier: u8, |
356 | | pub length: u32, |
357 | | pub auth_type: u32, |
358 | | pub payload: Vec<u8>, |
359 | | } |
360 | | |
361 | | #[derive(Debug, PartialEq, Eq)] |
362 | | pub struct SASLInitialResponsePacket { |
363 | | pub identifier: u8, |
364 | | pub length: u32, |
365 | | pub auth_mechanism: SASLAuthenticationMechanism, |
366 | | pub param_length: u32, |
367 | | pub sasl_param: Vec<u8>, |
368 | | } |
369 | | |
370 | | #[derive(Debug, PartialEq, Eq)] |
371 | | pub struct AuthenticationSASLMechanismMessage { |
372 | | identifier: u8, |
373 | | length: u32, |
374 | | auth_type: u32, |
375 | | auth_mechanisms: Vec<SASLAuthenticationMechanism>, |
376 | | } |
377 | | |
378 | | #[derive(Debug, PartialEq, Eq)] |
379 | | pub struct RowField { |
380 | | pub field_name: Vec<u8>, |
381 | | pub table_oid: u32, |
382 | | pub column_index: u16, |
383 | | pub data_type_oid: u32, |
384 | | // "see pg_type.typlen. Note that negative values denote variable-width types" |
385 | | pub data_type_size: i16, |
386 | | // "The value will generally be -1 for types that do not need pg_attribute.atttypmod." |
387 | | pub type_modifier: i32, |
388 | | // "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" |
389 | | pub format_code: u16, |
390 | | } |
391 | | |
392 | | #[derive(Debug, PartialEq, Eq)] |
393 | | pub struct RowDescriptionMessage { |
394 | | pub identifier: u8, |
395 | | pub length: u32, |
396 | | pub field_count: u16, |
397 | | pub fields: Vec<RowField>, |
398 | | } |
399 | | |
400 | | #[derive(Debug, PartialEq, Eq)] |
401 | | pub struct ColumnFieldValue { |
402 | | // Can be 0, or -1 as a special NULL column value |
403 | | pub value_length: i32, |
404 | | pub value: Vec<u8>, |
405 | | } |
406 | | |
407 | | #[derive(Debug, PartialEq, Eq)] |
408 | | pub enum PgsqlErrorNoticeFieldType { |
409 | | SeverityLocalizable, |
410 | | SeverityNonLocalizable, |
411 | | CodeSqlStateCode, |
412 | | Message, |
413 | | Detail, |
414 | | Hint, |
415 | | Position, |
416 | | InternalPosition, |
417 | | InternalQuery, |
418 | | Where, |
419 | | SchemaName, |
420 | | TableName, |
421 | | ColumnName, |
422 | | DataType, |
423 | | ConstraintName, |
424 | | File, |
425 | | Line, |
426 | | Routine, |
427 | | // Indicates end of message |
428 | | TerminatorToken, |
429 | | // 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? |
430 | | UnknownFieldType, |
431 | | } |
432 | | |
433 | | impl PgsqlErrorNoticeFieldType { |
434 | 0 | pub fn to_str(&self) -> &'static str { |
435 | 0 | match self { |
436 | 0 | PgsqlErrorNoticeFieldType::SeverityLocalizable => "severity_localizable", |
437 | 0 | PgsqlErrorNoticeFieldType::SeverityNonLocalizable => "severity_non_localizable", |
438 | 0 | PgsqlErrorNoticeFieldType::CodeSqlStateCode => "code", |
439 | 0 | PgsqlErrorNoticeFieldType::Message => "message", |
440 | 0 | PgsqlErrorNoticeFieldType::Detail => "detail", |
441 | 0 | PgsqlErrorNoticeFieldType::Hint => "hint", |
442 | 0 | PgsqlErrorNoticeFieldType::Position => "position", |
443 | 0 | PgsqlErrorNoticeFieldType::InternalPosition => "internal_position", |
444 | 0 | PgsqlErrorNoticeFieldType::InternalQuery => "internal_query", |
445 | 0 | PgsqlErrorNoticeFieldType::Where => "where", |
446 | 0 | PgsqlErrorNoticeFieldType::SchemaName => "schema_name", |
447 | 0 | PgsqlErrorNoticeFieldType::TableName => "table_name", |
448 | 0 | PgsqlErrorNoticeFieldType::ColumnName => "column_name", |
449 | 0 | PgsqlErrorNoticeFieldType::DataType => "data_type", |
450 | 0 | PgsqlErrorNoticeFieldType::ConstraintName => "constraint_name", |
451 | 0 | PgsqlErrorNoticeFieldType::File => "file", |
452 | 0 | PgsqlErrorNoticeFieldType::Line => "line", |
453 | 0 | PgsqlErrorNoticeFieldType::Routine => "routine", |
454 | 0 | PgsqlErrorNoticeFieldType::TerminatorToken => "", |
455 | 0 | PgsqlErrorNoticeFieldType::UnknownFieldType => "unknown_field_type", |
456 | | } |
457 | 0 | } |
458 | | } |
459 | | |
460 | | impl From<char> for PgsqlErrorNoticeFieldType { |
461 | 6.25k | fn from(identifier: char) -> PgsqlErrorNoticeFieldType { |
462 | 6.25k | match identifier { |
463 | 0 | 'S' => PgsqlErrorNoticeFieldType::SeverityLocalizable, |
464 | 6.25k | 'V' => PgsqlErrorNoticeFieldType::SeverityNonLocalizable, |
465 | 0 | 'C' => PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
466 | 0 | 'M' => PgsqlErrorNoticeFieldType::Message, |
467 | 0 | 'D' => PgsqlErrorNoticeFieldType::Detail, |
468 | 0 | 'H' => PgsqlErrorNoticeFieldType::Hint, |
469 | 0 | 'P' => PgsqlErrorNoticeFieldType::Position, |
470 | 0 | 'p' => PgsqlErrorNoticeFieldType::InternalPosition, |
471 | 0 | 'q' => PgsqlErrorNoticeFieldType::InternalQuery, |
472 | 0 | 'W' => PgsqlErrorNoticeFieldType::Where, |
473 | 0 | 's' => PgsqlErrorNoticeFieldType::SchemaName, |
474 | 0 | 't' => PgsqlErrorNoticeFieldType::TableName, |
475 | 0 | 'c' => PgsqlErrorNoticeFieldType::ColumnName, |
476 | 0 | 'd' => PgsqlErrorNoticeFieldType::DataType, |
477 | 0 | 'n' => PgsqlErrorNoticeFieldType::ConstraintName, |
478 | 0 | 'F' => PgsqlErrorNoticeFieldType::File, |
479 | 0 | 'L' => PgsqlErrorNoticeFieldType::Line, |
480 | 0 | 'R' => PgsqlErrorNoticeFieldType::Routine, |
481 | 0 | '\u{0}' => PgsqlErrorNoticeFieldType::TerminatorToken, |
482 | | // Pgsql documentation says "frontends should silently ignore fields of unrecognized type." |
483 | 0 | _ => PgsqlErrorNoticeFieldType::UnknownFieldType, |
484 | | } |
485 | 6.25k | } |
486 | | } |
487 | | |
488 | | impl From<u8> for PgsqlErrorNoticeFieldType { |
489 | 2.11M | fn from(identifier: u8) -> PgsqlErrorNoticeFieldType { |
490 | 2.11M | match identifier { |
491 | 884 | b'S' => PgsqlErrorNoticeFieldType::SeverityLocalizable, |
492 | 0 | b'V' => PgsqlErrorNoticeFieldType::SeverityNonLocalizable, |
493 | 0 | b'C' => PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
494 | 534 | b'M' => PgsqlErrorNoticeFieldType::Message, |
495 | 860 | b'D' => PgsqlErrorNoticeFieldType::Detail, |
496 | 1.38k | b'H' => PgsqlErrorNoticeFieldType::Hint, |
497 | 1.68k | b'P' => PgsqlErrorNoticeFieldType::Position, |
498 | 1.10k | b'p' => PgsqlErrorNoticeFieldType::InternalPosition, |
499 | 1.97k | b'q' => PgsqlErrorNoticeFieldType::InternalQuery, |
500 | 966 | b'W' => PgsqlErrorNoticeFieldType::Where, |
501 | 3.31k | b's' => PgsqlErrorNoticeFieldType::SchemaName, |
502 | 685 | b't' => PgsqlErrorNoticeFieldType::TableName, |
503 | 981 | b'c' => PgsqlErrorNoticeFieldType::ColumnName, |
504 | 3.37k | b'd' => PgsqlErrorNoticeFieldType::DataType, |
505 | 1.41k | b'n' => PgsqlErrorNoticeFieldType::ConstraintName, |
506 | 1.43k | b'F' => PgsqlErrorNoticeFieldType::File, |
507 | 1.53k | b'L' => PgsqlErrorNoticeFieldType::Line, |
508 | 2.12k | b'R' => PgsqlErrorNoticeFieldType::Routine, |
509 | 0 | b'\0' => PgsqlErrorNoticeFieldType::TerminatorToken, |
510 | | // Pgsql documentation says "frontends should silently ignore fields of unrecognized type." |
511 | 2.08M | _ => PgsqlErrorNoticeFieldType::UnknownFieldType, |
512 | | } |
513 | 2.11M | } |
514 | | } |
515 | | |
516 | | // Currently the set of parameters that could trigger a ParameterStatus message is fixed: |
517 | | // server_version |
518 | | // server_encoding |
519 | | // client_encoding |
520 | | // application_name |
521 | | // default_transaction_read_only |
522 | | // in_hot_standby |
523 | | // is_superuser |
524 | | // session_authorization |
525 | | // DateStyle |
526 | | // IntervalStyle |
527 | | // TimeZone |
528 | | // integer_datetimes |
529 | | // standard_conforming_strings |
530 | | // (source: PostgreSQL documentation) |
531 | | // We may be interested, then, in controling this, somehow, to prevent weird things? |
532 | 2.54M | fn pgsql_parse_generic_parameter(i: &[u8]) -> IResult<&[u8], PgsqlParameter> { |
533 | 2.54M | let (i, param_name) = take_until1("\x00")(i)?; |
534 | 2.44M | let (i, _) = tag("\x00")(i)?; |
535 | 2.44M | let (i, param_value) = take_until("\x00")(i)?; |
536 | 2.43M | let (i, _) = tag("\x00")(i)?; |
537 | 2.43M | Ok((i, PgsqlParameter { |
538 | 2.43M | name: PgsqlParameters::from(param_name), |
539 | 2.43M | value: param_value.to_vec(), |
540 | 2.43M | })) |
541 | 2.54M | } |
542 | | |
543 | 101k | pub fn pgsql_parse_startup_parameters(i: &[u8]) -> IResult<&[u8], PgsqlStartupParameters> { |
544 | 101k | let (i, mut optional) = opt(terminated(many1(pgsql_parse_generic_parameter), tag("\x00")))(i)?; |
545 | 61.6k | if let Some(ref mut params) = optional { |
546 | 61.6k | let mut user = PgsqlParameter{name: PgsqlParameters::User, value: Vec::new() }; |
547 | 61.6k | let mut index: usize = 0; |
548 | 2.26M | for (j, p) in params.iter().enumerate() { |
549 | 2.26M | if p.name == PgsqlParameters::User { |
550 | 80.3k | user.value.extend_from_slice(&p.value); |
551 | 80.3k | index = j; |
552 | 2.18M | } |
553 | | } |
554 | 61.6k | params.remove(index); |
555 | 61.6k | if user.value.is_empty() { |
556 | 1.11k | return Err(Err::Error(make_error(i, ErrorKind::Tag))); |
557 | 60.5k | } |
558 | 60.5k | return Ok((i, PgsqlStartupParameters{ |
559 | 60.5k | user, |
560 | 60.5k | optional_params: if !params.is_empty() { |
561 | 55.9k | optional |
562 | 4.61k | } else { None }, |
563 | | })); |
564 | 11 | } |
565 | 11 | return Err(Err::Error(make_error(i, ErrorKind::Tag))); |
566 | 101k | } |
567 | | |
568 | 984 | fn parse_sasl_initial_response_payload(i: &[u8]) -> IResult<&[u8], (SASLAuthenticationMechanism, u32, Vec<u8>)> { |
569 | 984 | let (i, sasl_mechanism) = parse_sasl_mechanism(i)?; |
570 | 974 | let (i, param_length) = be_u32(i)?; |
571 | | // From RFC 5802 - the client-first-message will always start w/ |
572 | | // 'n', 'y' or 'p', otherwise it's invalid, I think we should check that, at some point |
573 | 780 | let (i, param) = terminated(take(param_length), eof)(i)?; |
574 | 359 | Ok((i, (sasl_mechanism, param_length, param.to_vec()))) |
575 | 984 | } |
576 | | |
577 | 17.0k | pub fn parse_sasl_initial_response(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
578 | 17.0k | let (i, identifier) = verify(be_u8, |&x| x == b'p')(i)?; |
579 | 17.0k | let (i, length) = parse_length(i)?; |
580 | 16.4k | let (i, payload) = map_parser(take(length - PGSQL_LENGTH_FIELD), parse_sasl_initial_response_payload)(i)?; |
581 | 359 | Ok((i, PgsqlFEMessage::SASLInitialResponse( |
582 | 359 | SASLInitialResponsePacket { |
583 | 359 | identifier, |
584 | 359 | length, |
585 | 359 | auth_mechanism: payload.0, |
586 | 359 | param_length: payload.1, |
587 | 359 | sasl_param: payload.2, |
588 | 359 | }))) |
589 | 17.0k | } |
590 | | |
591 | 10.7k | pub fn parse_sasl_response(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
592 | 10.7k | let (i, identifier) = verify(be_u8, |&x| x == b'p')(i)?; |
593 | 10.6k | let (i, length) = parse_length(i)?; |
594 | 9.75k | let (i, payload) = take(length - PGSQL_LENGTH_FIELD)(i)?; |
595 | 1.74k | let resp = PgsqlFEMessage::SASLResponse( |
596 | 1.74k | RegularPacket { |
597 | 1.74k | identifier, |
598 | 1.74k | length, |
599 | 1.74k | payload: payload.to_vec(), |
600 | 1.74k | }); |
601 | 1.74k | Ok((i, resp)) |
602 | 10.7k | } |
603 | | |
604 | 515k | pub fn pgsql_parse_startup_packet(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
605 | 515k | let (i, len) = verify(be_u32, |&x| x >= 8)(i)?; |
606 | 495k | let (i, proto_major) = peek(be_u16)(i)?; |
607 | 488k | let (i, b) = take(len - PGSQL_LENGTH_FIELD)(i)?; |
608 | 275k | let (_, message) = |
609 | 317k | match proto_major { |
610 | 317k | 1..=3 => { |
611 | 101k | let (b, proto_major) = be_u16(b)?; |
612 | 101k | let (b, proto_minor) = be_u16(b)?; |
613 | 101k | let (b, params) = pgsql_parse_startup_parameters(b)?; |
614 | 60.5k | (b, PgsqlFEMessage::StartupMessage(StartupPacket{ |
615 | 60.5k | length: len, |
616 | 60.5k | proto_major, |
617 | 60.5k | proto_minor, |
618 | 60.5k | params})) |
619 | | }, |
620 | | PGSQL_DUMMY_PROTO_MAJOR => { |
621 | 215k | let (b, proto_major) = be_u16(b)?; |
622 | 215k | let (b, proto_minor) = be_u16(b)?; |
623 | 215k | let (b, message) = match proto_minor { |
624 | | PGSQL_DUMMY_PROTO_CANCEL_REQUEST => { |
625 | 1.26k | parse_cancel_request(b)? |
626 | | }, |
627 | 214k | PGSQL_DUMMY_PROTO_MINOR_SSL => (b, PgsqlFEMessage::SSLRequest(DummyStartupPacket{ |
628 | 214k | length: len, |
629 | 214k | proto_major, |
630 | 214k | proto_minor |
631 | 214k | })), |
632 | 67 | _ => return Err(Err::Error(make_error(b, ErrorKind::Switch))), |
633 | | }; |
634 | | |
635 | 214k | (b, message) |
636 | | } |
637 | 87 | _ => return Err(Err::Error(make_error(b, ErrorKind::Switch))), |
638 | | }; |
639 | 275k | Ok((i, message)) |
640 | 515k | } |
641 | | |
642 | | // TODO Decide if it's a good idea to offer GSS encryption support right now, as the documentation seems to have conflicting information... |
643 | | // If we do: |
644 | | // To initiate a GSSAPI-encrypted connection, the frontend initially sends a GSSENCRequest message rather than a |
645 | | // 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 |
646 | | // dissatisfied with the response. To continue after G, using the GSSAPI C bindings as discussed in RFC2744 or equivalent, |
647 | | // perform a GSSAPI initialization by calling gss_init_sec_context() in a loop and sending the result to the server, |
648 | | // starting with an empty input and then with each result from the server, until it returns no output. When sending the |
649 | | // results of gss_init_sec_context() to the server, prepend the length of the message as a four byte integer in network |
650 | | // byte order. To continue after N, send the usual StartupMessage and proceed without encryption. (Alternatively, it is |
651 | | // permissible to issue an SSLRequest message after an N response to try to use SSL encryption instead of GSSAPI.) |
652 | | // Source: https://www.postgresql.org/docs/13/protocol-flow.html#id-1.10.5.7.11, GSSAPI Session Encryption |
653 | | |
654 | | // Password can be encrypted or in cleartext |
655 | 2.84k | pub fn parse_password_message(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
656 | 2.84k | let (i, identifier) = verify(be_u8, |&x| x == b'p')(i)?; |
657 | 2.82k | let (i, length) = parse_length(i)?; |
658 | 1.76k | let (i, password) = map_parser( |
659 | 1.76k | take(length - PGSQL_LENGTH_FIELD), |
660 | 1.76k | take_until1("\x00") |
661 | 1.76k | )(i)?; |
662 | 322 | Ok((i, PgsqlFEMessage::PasswordMessage( |
663 | 322 | RegularPacket{ |
664 | 322 | identifier, |
665 | 322 | length, |
666 | 322 | payload: password.to_vec(), |
667 | 322 | }))) |
668 | 2.84k | } |
669 | | |
670 | 320k | fn parse_simple_query(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
671 | 320k | let (i, identifier) = verify(be_u8, |&x| x == b'Q')(i)?; |
672 | 320k | let (i, length) = parse_length(i)?; |
673 | 317k | let (i, query) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until1("\x00"))(i)?; |
674 | 278k | Ok((i, PgsqlFEMessage::SimpleQuery(RegularPacket { |
675 | 278k | identifier, |
676 | 278k | length, |
677 | 278k | payload: query.to_vec(), |
678 | 278k | }))) |
679 | 320k | } |
680 | | |
681 | 1.26k | fn parse_cancel_request(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
682 | 1.26k | let (i, pid) = be_u32(i)?; |
683 | 854 | let (i, backend_key) = be_u32(i)?; |
684 | 625 | Ok((i, PgsqlFEMessage::CancelRequest(CancelRequestMessage { |
685 | 625 | pid, |
686 | 625 | backend_key, |
687 | 625 | }))) |
688 | 1.26k | } |
689 | | |
690 | 5.20M | fn parse_terminate_message(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
691 | 5.20M | let (i, identifier) = verify(be_u8, |&x| x == b'X')(i)?; |
692 | 5.20M | let (i, length) = parse_length(i)?; |
693 | 5.20M | Ok((i, PgsqlFEMessage::Terminate(TerminationMessage { identifier, length }))) |
694 | 5.20M | } |
695 | | |
696 | | // Messages that begin with 'p' but are not password ones are not parsed here |
697 | 6.39M | pub fn parse_request(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> { |
698 | 6.39M | let (i, tag) = peek(be_u8)(i)?; |
699 | 6.39M | let (i, message) = match tag { |
700 | 515k | b'\0' => pgsql_parse_startup_packet(i)?, |
701 | 320k | b'Q' => parse_simple_query(i)?, |
702 | 5.20M | b'X' => parse_terminate_message(i)?, |
703 | | _ => { |
704 | 347k | let (i, identifier) = be_u8(i)?; |
705 | 347k | let (i, length) = verify(be_u32, |&x| x > PGSQL_LENGTH_FIELD)(i)?; |
706 | 327k | let (i, payload) = take(length - PGSQL_LENGTH_FIELD)(i)?; |
707 | 191k | let unknown = PgsqlFEMessage::UnknownMessageType (RegularPacket{ |
708 | 191k | identifier, |
709 | 191k | length, |
710 | 191k | payload: payload.to_vec(), |
711 | 191k | }); |
712 | 191k | (i, unknown) |
713 | | } |
714 | | }; |
715 | 5.95M | Ok((i, message)) |
716 | 6.39M | } |
717 | | |
718 | 96.2k | fn pgsql_parse_authentication_message<'a>(i: &'a [u8]) -> IResult<&'a [u8], PgsqlBEMessage> { |
719 | 96.2k | let (i, identifier) = verify(be_u8, |&x| x == b'R')(i)?; |
720 | 96.2k | let (i, length) = verify(be_u32, |&x| x >= 8)(i)?; |
721 | 96.2k | let (i, auth_type) = be_u32(i)?; |
722 | 94.9k | let (i, message) = map_parser( |
723 | 94.9k | take(length - 8), |
724 | 88.7k | |b: &'a [u8]| { |
725 | 88.7k | match auth_type { |
726 | 5.83k | 0 => Ok((b, PgsqlBEMessage::AuthenticationOk( |
727 | 5.83k | AuthenticationMessage { |
728 | 5.83k | identifier, |
729 | 5.83k | length, |
730 | 5.83k | auth_type, |
731 | 5.83k | payload: b.to_vec(), |
732 | 5.83k | }))), |
733 | 2.65k | 3 => Ok((b, PgsqlBEMessage::AuthenticationCleartextPassword( |
734 | 2.65k | AuthenticationMessage { |
735 | 2.65k | identifier, |
736 | 2.65k | length, |
737 | 2.65k | auth_type, |
738 | 2.65k | payload: b.to_vec(), |
739 | 2.65k | }))), |
740 | | 5 => { |
741 | 797 | let (b, salt) = all_consuming(take(4_usize))(b)?; |
742 | 413 | Ok((b, PgsqlBEMessage::AuthenticationMD5Password( |
743 | 413 | AuthenticationMessage { |
744 | 413 | identifier, |
745 | 413 | length, |
746 | 413 | auth_type, |
747 | 413 | payload: salt.to_vec(), |
748 | 413 | }))) |
749 | | } |
750 | 71.9k | 9 => Ok((b, PgsqlBEMessage::AuthenticationSSPI( |
751 | 71.9k | AuthenticationMessage { |
752 | 71.9k | identifier, |
753 | 71.9k | length, |
754 | 71.9k | auth_type, |
755 | 71.9k | payload: b.to_vec(), |
756 | 71.9k | }))), |
757 | | // 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 ) |
758 | | 10 => { |
759 | 1.76k | let (b, auth_mechanisms) = parse_sasl_mechanisms(b)?; |
760 | 1.22k | Ok((b, PgsqlBEMessage::AuthenticationSASL( |
761 | 1.22k | AuthenticationSASLMechanismMessage { |
762 | 1.22k | identifier, |
763 | 1.22k | length, |
764 | 1.22k | auth_type, |
765 | 1.22k | auth_mechanisms, |
766 | 1.22k | }))) |
767 | | } |
768 | | 11 => { |
769 | 4.91k | Ok((b, PgsqlBEMessage::AuthenticationSASLContinue( |
770 | 4.91k | AuthenticationMessage { |
771 | 4.91k | identifier, |
772 | 4.91k | length, |
773 | 4.91k | auth_type, |
774 | 4.91k | payload: b.to_vec(), |
775 | 4.91k | }))) |
776 | | }, |
777 | | 12 => { |
778 | 902 | Ok((b, PgsqlBEMessage::AuthenticationSASLFinal( |
779 | 902 | AuthenticationMessage { |
780 | 902 | identifier, |
781 | 902 | length, |
782 | 902 | auth_type, |
783 | 902 | payload: b.to_vec(), |
784 | 902 | } |
785 | 902 | ))) |
786 | | } |
787 | | // TODO add other authentication messages |
788 | 25 | _ => return Err(Err::Error(make_error(i, ErrorKind::Switch))), |
789 | | } |
790 | 88.7k | } |
791 | 94.9k | )(i)?; |
792 | 87.8k | Ok((i, message)) |
793 | 96.2k | } |
794 | | |
795 | 16.6k | fn parse_parameter_status_message(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
796 | 16.6k | let (i, identifier) = verify(be_u8, |&x| x == b'S')(i)?; |
797 | 16.6k | let (i, length) = parse_length(i)?; |
798 | 16.6k | let (i, param) = map_parser(take(length - PGSQL_LENGTH_FIELD), pgsql_parse_generic_parameter)(i)?; |
799 | 9.64k | Ok((i, PgsqlBEMessage::ParameterStatus(ParameterStatusMessage { |
800 | 9.64k | identifier, |
801 | 9.64k | length, |
802 | 9.64k | param, |
803 | 9.64k | }))) |
804 | 16.6k | } |
805 | | |
806 | 282k | pub fn parse_ssl_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
807 | 282k | let (i, tag) = alt((char('N'), char('S')))(i)?; |
808 | 1.81k | Ok((i, PgsqlBEMessage::SSLResponse( |
809 | 1.81k | SSLResponseMessage::from(tag)) |
810 | 1.81k | )) |
811 | 282k | } |
812 | | |
813 | 61.0k | fn parse_backend_key_data_message(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
814 | 61.0k | let (i, identifier) = verify(be_u8, |&x| x == b'K')(i)?; |
815 | 61.0k | let (i, length) = verify(be_u32, |&x| x == 12)(i)?; |
816 | 60.9k | let (i, pid) = be_u32(i)?; |
817 | 59.9k | let (i, secret_key) = be_u32(i)?; |
818 | 58.8k | Ok((i, PgsqlBEMessage::BackendKeyData(BackendKeyDataMessage { |
819 | 58.8k | identifier, |
820 | 58.8k | length, |
821 | 58.8k | backend_pid: pid, |
822 | 58.8k | secret_key, |
823 | 58.8k | }))) |
824 | 61.0k | } |
825 | | |
826 | 834k | fn parse_command_complete(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
827 | 834k | let (i, identifier) = verify(be_u8, |&x| x == b'C')(i)?; |
828 | 834k | let (i, length) = parse_length(i)?; |
829 | 834k | let (i, payload) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until("\x00"))(i)?; |
830 | 819k | Ok((i, PgsqlBEMessage::CommandComplete(RegularPacket { |
831 | 819k | identifier, |
832 | 819k | length, |
833 | 819k | payload: payload.to_vec(), |
834 | 819k | }))) |
835 | 834k | } |
836 | | |
837 | 24.0k | fn parse_ready_for_query(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
838 | 24.0k | let (i, identifier) = verify(be_u8, |&x| x == b'Z')(i)?; |
839 | 24.0k | let (i, length) = verify(be_u32, |&x| x == 5)(i)?; |
840 | 24.0k | let (i, status) = verify(be_u8, |&x| x == b'I' || x == b'T' || x == b'E')(i)?; |
841 | 23.7k | Ok((i, PgsqlBEMessage::ReadyForQuery(ReadyForQueryMessage { |
842 | 23.7k | identifier, |
843 | 23.7k | length, |
844 | 23.7k | transaction_status: status, |
845 | 23.7k | }))) |
846 | 24.0k | } |
847 | | |
848 | 292k | fn parse_row_field(i: &[u8]) -> IResult<&[u8], RowField> { |
849 | 292k | let (i, field_name) = take_until1("\x00")(i)?; |
850 | 280k | let (i, _) = tag("\x00")(i)?; |
851 | 280k | let (i, table_oid) = be_u32(i)?; |
852 | 280k | let (i, column_index) = be_u16(i)?; |
853 | 279k | let (i, data_type_oid) = be_u32(i)?; |
854 | 278k | let (i, data_type_size) = be_i16(i)?; |
855 | 276k | let (i, type_modifier) = be_i32(i)?; |
856 | 276k | let (i, format_code) = be_u16(i)?; |
857 | 275k | Ok((i, RowField { |
858 | 275k | field_name: field_name.to_vec(), |
859 | 275k | table_oid, |
860 | 275k | column_index, |
861 | 275k | data_type_oid, |
862 | 275k | data_type_size, |
863 | 275k | type_modifier, |
864 | 275k | format_code, |
865 | 275k | })) |
866 | 292k | } |
867 | | |
868 | 72.1k | pub fn parse_row_description(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
869 | 72.1k | let (i, identifier) = verify(be_u8, |&x| x == b'T')(i)?; |
870 | 72.1k | let (i, length) = verify(be_u32, |&x| x > 6)(i)?; |
871 | 72.1k | let (i, field_count) = be_u16(i)?; |
872 | 71.1k | let (i, fields) = map_parser( |
873 | 71.1k | take(length - 6), |
874 | 71.1k | many_m_n(0, field_count.into(), parse_row_field) |
875 | 71.1k | )(i)?; |
876 | 12.4k | Ok((i, PgsqlBEMessage::RowDescription( |
877 | 12.4k | RowDescriptionMessage { |
878 | 12.4k | identifier, |
879 | 12.4k | length, |
880 | 12.4k | field_count, |
881 | 12.4k | fields, |
882 | 12.4k | }))) |
883 | 72.1k | } |
884 | | |
885 | 606k | fn parse_data_row_value(i: &[u8]) -> IResult<&[u8], ColumnFieldValue> { |
886 | 606k | let (i, value_length) = be_i32(i)?; |
887 | 601k | let (i, value) = cond(value_length >= 0, take(value_length as usize))(i)?; |
888 | 598k | Ok((i, ColumnFieldValue { |
889 | 598k | value_length, |
890 | | value: { |
891 | 598k | match value { |
892 | 18.6k | Some(data) => data.to_vec(), |
893 | 579k | None => [].to_vec(), |
894 | | } |
895 | | }, |
896 | | })) |
897 | 606k | } |
898 | | |
899 | | /// For each column, add up the data size. Return the total |
900 | 144k | fn add_up_data_size(columns: Vec<ColumnFieldValue>) -> u64 { |
901 | 144k | let mut data_size: u64 = 0; |
902 | 600k | for field in columns { |
903 | | // -1 value means data value is NULL, let's not add that up |
904 | 455k | if field.value_length > 0 { |
905 | 1.18k | data_size += field.value_length as u64; |
906 | 454k | } |
907 | | } |
908 | 144k | data_size |
909 | 144k | } |
910 | | |
911 | | // Currently, we don't store the actual DataRow messages, as those could easily become a burden, memory-wise |
912 | | // We use ConsolidatedDataRow to store info we still want to log: message size. |
913 | | // Later on, we calculate the number of lines the command actually returned by counting ConsolidatedDataRow messages |
914 | 182k | pub fn parse_consolidated_data_row(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
915 | 182k | let (i, identifier) = verify(be_u8, |&x| x == b'D')(i)?; |
916 | 182k | let (i, length) = verify(be_u32, |&x| x >= 6)(i)?; |
917 | 182k | let (i, field_count) = be_u16(i)?; |
918 | | // 6 here is for skipping length + field_count |
919 | 181k | let (i, rows) = map_parser(take(length - 6), many_m_n(0, field_count.into(), parse_data_row_value))(i)?; |
920 | 144k | Ok((i, PgsqlBEMessage::ConsolidatedDataRow( |
921 | 144k | ConsolidatedDataRowPacket { |
922 | 144k | identifier, |
923 | 144k | row_cnt: 1, |
924 | 144k | data_size: add_up_data_size(rows), |
925 | 144k | } |
926 | 144k | ))) |
927 | 182k | } |
928 | | |
929 | 6.33k | fn parse_sasl_mechanism(i: &[u8]) -> IResult<&[u8], SASLAuthenticationMechanism> { |
930 | 6.33k | let res: IResult<_, _, ()> = terminated(tag("SCRAM-SHA-256-PLUS"), tag("\x00"))(i); |
931 | 6.33k | if let Ok((i, _)) = res { |
932 | 2 | return Ok((i, SASLAuthenticationMechanism::ScramSha256Plus)); |
933 | 6.33k | } |
934 | 6.33k | let res: IResult<_, _, ()> = terminated(tag("SCRAM-SHA-256"), tag("\x00"))(i); |
935 | 6.33k | if let Ok((i, _)) = res { |
936 | 4.55k | return Ok((i, SASLAuthenticationMechanism::ScramSha256)); |
937 | 1.77k | } |
938 | 1.77k | return Err(Err::Error(make_error(i, ErrorKind::Alt))); |
939 | 6.33k | } |
940 | | |
941 | 1.76k | fn parse_sasl_mechanisms(i: &[u8]) -> IResult<&[u8], Vec<SASLAuthenticationMechanism>> { |
942 | 1.76k | terminated(many1(parse_sasl_mechanism), tag("\x00"))(i) |
943 | 1.76k | } |
944 | | |
945 | 13.4k | pub fn parse_error_response_code(i: &[u8]) -> IResult<&[u8], PgsqlErrorNoticeMessageField> { |
946 | 13.4k | let (i, _field_type) = char('C')(i)?; |
947 | 13.4k | let (i, field_value) = map_parser(take(6_usize), alphanumeric1)(i)?; |
948 | 10.1k | Ok((i, PgsqlErrorNoticeMessageField{ |
949 | 10.1k | field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
950 | 10.1k | field_value: field_value.to_vec(), |
951 | 10.1k | })) |
952 | 13.4k | } |
953 | | |
954 | | // Parse an error response with non-localizeable severity message. |
955 | | // Possible values: ERROR, FATAL, or PANIC |
956 | 6.95k | pub fn parse_error_response_severity(i: &[u8]) -> IResult<&[u8], PgsqlErrorNoticeMessageField> { |
957 | 6.95k | let (i, field_type) = char('V')(i)?; |
958 | 6.95k | let (i, field_value) = alt((tag("ERROR"), tag("FATAL"), tag("PANIC")))(i)?; |
959 | 4.97k | let (i, _) = tag("\x00")(i)?; |
960 | 4.27k | Ok((i, PgsqlErrorNoticeMessageField{ |
961 | 4.27k | field_type: PgsqlErrorNoticeFieldType::from(field_type), |
962 | 4.27k | field_value: field_value.to_vec(), |
963 | 4.27k | })) |
964 | 6.95k | } |
965 | | |
966 | | // The non-localizable version of Severity field has different values, |
967 | | // in case of a notice: 'WARNING', 'NOTICE', 'DEBUG', 'INFO' or 'LOG' |
968 | 5.85k | pub fn parse_notice_response_severity(i: &[u8]) -> IResult<&[u8], PgsqlErrorNoticeMessageField> { |
969 | 5.85k | let (i, field_type) = char('V')(i)?; |
970 | 5.85k | let (i, field_value) = alt(( |
971 | 5.85k | tag("WARNING"), |
972 | 5.85k | tag("NOTICE"), |
973 | 5.85k | tag("DEBUG"), |
974 | 5.85k | tag("INFO"), |
975 | 5.85k | tag("LOG")))(i)?; |
976 | 3.64k | let (i, _) = tag("\x00")(i)?; |
977 | 1.98k | Ok((i, PgsqlErrorNoticeMessageField{ |
978 | 1.98k | field_type: PgsqlErrorNoticeFieldType::from(field_type), |
979 | 1.98k | field_value: field_value.to_vec(), |
980 | 1.98k | })) |
981 | 5.85k | } |
982 | | |
983 | 2.14M | pub fn parse_error_response_field( |
984 | 2.14M | i: &[u8], is_err_msg: bool, |
985 | 2.14M | ) -> IResult<&[u8], PgsqlErrorNoticeMessageField> { |
986 | 2.14M | let (i, field_type) = peek(be_u8)(i)?; |
987 | 2.14M | let (i, data) = match field_type { |
988 | | b'V' => { |
989 | 12.8k | if is_err_msg { |
990 | 6.95k | parse_error_response_severity(i)? |
991 | | } else { |
992 | 5.85k | parse_notice_response_severity(i)? |
993 | | } |
994 | | } |
995 | 13.4k | b'C' => parse_error_response_code(i)?, |
996 | | _ => { |
997 | 2.11M | let (i, field_type) = be_u8(i)?; |
998 | 2.11M | let (i, field_value) = take_until("\x00")(i)?; |
999 | 2.11M | let (i, _just_tag) = tag("\x00")(i)?; |
1000 | 2.11M | let message = PgsqlErrorNoticeMessageField { |
1001 | 2.11M | field_type: PgsqlErrorNoticeFieldType::from(field_type), |
1002 | 2.11M | field_value: field_value.to_vec(), |
1003 | 2.11M | }; |
1004 | 2.11M | return Ok((i, message)); |
1005 | | } |
1006 | | }; |
1007 | 16.3k | Ok((i, data)) |
1008 | 2.14M | } |
1009 | | |
1010 | 39.2k | pub fn parse_error_notice_fields(i: &[u8], is_err_msg: bool) -> IResult<&[u8], Vec<PgsqlErrorNoticeMessageField>> { |
1011 | 2.14M | let (i, data) = many_till(|b| parse_error_response_field(b, is_err_msg), tag("\x00"))(i)?; |
1012 | 18.5k | Ok((i, data.0)) |
1013 | 39.2k | } |
1014 | | |
1015 | 115k | fn pgsql_parse_error_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
1016 | 115k | let (i, identifier) = verify(be_u8, |&x| x == b'E')(i)?; |
1017 | 115k | let (i, length) = verify(be_u32, |&x| x > 10)(i)?; |
1018 | 115k | let (i, message_body) = map_parser( |
1019 | 115k | take(length - PGSQL_LENGTH_FIELD), |
1020 | 27.4k | |b| parse_error_notice_fields(b, true) |
1021 | 115k | )(i)?; |
1022 | | |
1023 | 12.6k | Ok((i, PgsqlBEMessage::ErrorResponse(ErrorNoticeMessage { |
1024 | 12.6k | identifier, |
1025 | 12.6k | length, |
1026 | 12.6k | message_body, |
1027 | 12.6k | }))) |
1028 | 115k | } |
1029 | | |
1030 | 17.9k | fn pgsql_parse_notice_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
1031 | 17.9k | let (i, identifier) = verify(be_u8, |&x| x == b'N')(i)?; |
1032 | 17.9k | let (i, length) = verify(be_u32, |&x| x > 10)(i)?; |
1033 | 17.9k | let (i, message_body) = map_parser( |
1034 | 17.9k | take(length - PGSQL_LENGTH_FIELD), |
1035 | 11.7k | |b| parse_error_notice_fields(b, false) |
1036 | 17.9k | )(i)?; |
1037 | 5.90k | Ok((i, PgsqlBEMessage::NoticeResponse(ErrorNoticeMessage { |
1038 | 5.90k | identifier, |
1039 | 5.90k | length, |
1040 | 5.90k | message_body, |
1041 | 5.90k | }))) |
1042 | 17.9k | } |
1043 | | |
1044 | 51.1k | fn parse_notification_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
1045 | 51.1k | let (i, identifier) = verify(be_u8, |&x| x == b'A')(i)?; |
1046 | | // length (u32) + pid (u32) + at least one byte, for we have two str fields |
1047 | 51.1k | let (i, length) = verify(be_u32, |&x| x > 9)(i)?; |
1048 | 51.1k | let (i, data) = map_parser( |
1049 | 51.1k | take(length - PGSQL_LENGTH_FIELD), |
1050 | 7.29k | |b| { |
1051 | 7.29k | let (b, pid) = be_u32(b)?; |
1052 | 7.29k | let (b, channel_name) = take_until_and_consume(b"\x00")(b)?; |
1053 | 6.23k | let (b, payload) = take_until_and_consume(b"\x00")(b)?; |
1054 | 5.81k | Ok((b, (pid, channel_name, payload))) |
1055 | 51.1k | })(i)?; |
1056 | 5.81k | let msg = PgsqlBEMessage::NotificationResponse(NotificationResponse{ |
1057 | 5.81k | identifier, |
1058 | 5.81k | length, |
1059 | 5.81k | pid: data.0, |
1060 | 5.81k | channel_name: data.1.to_vec(), |
1061 | 5.81k | payload: data.2.to_vec(), |
1062 | 5.81k | }); |
1063 | 5.81k | Ok((i, msg)) |
1064 | 51.1k | } |
1065 | | |
1066 | 1.67M | pub fn pgsql_parse_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage> { |
1067 | 1.67M | let (i, pseudo_header) = peek(tuple((be_u8, be_u32)))(i)?; |
1068 | 1.18M | let (i, message) = |
1069 | 1.62M | match pseudo_header.0 { |
1070 | 115k | b'E' => pgsql_parse_error_response(i)?, |
1071 | 61.0k | b'K' => parse_backend_key_data_message(i)?, |
1072 | 17.9k | b'N' => pgsql_parse_notice_response(i)?, |
1073 | 96.2k | b'R' => pgsql_parse_authentication_message(i)?, |
1074 | 16.6k | b'S' => parse_parameter_status_message(i)?, |
1075 | 834k | b'C' => parse_command_complete(i)?, |
1076 | 24.0k | b'Z' => parse_ready_for_query(i)?, |
1077 | 72.1k | b'T' => parse_row_description(i)?, |
1078 | 51.1k | b'A' => parse_notification_response(i)?, |
1079 | 182k | b'D' => parse_consolidated_data_row(i)?, |
1080 | | _ => { |
1081 | 156k | let (i, identifier) = be_u8(i)?; |
1082 | 156k | let (i, length) = verify(be_u32, |&x| x > PGSQL_LENGTH_FIELD)(i)?; |
1083 | 156k | let (i, payload) = take(length - PGSQL_LENGTH_FIELD)(i)?; |
1084 | 5.38k | let unknown = PgsqlBEMessage::UnknownMessageType (RegularPacket{ |
1085 | 5.38k | identifier, |
1086 | 5.38k | length, |
1087 | 5.38k | payload: payload.to_vec(), |
1088 | 5.38k | }); |
1089 | 5.38k | (i, unknown) |
1090 | | } |
1091 | | |
1092 | | }; |
1093 | 1.18M | Ok((i, message)) |
1094 | 1.67M | } |
1095 | | |
1096 | | #[cfg(test)] |
1097 | | #[allow(clippy::vec_init_then_push)] |
1098 | | mod tests { |
1099 | | |
1100 | | use super::*; |
1101 | | use nom7::Needed; |
1102 | | |
1103 | | impl ErrorNoticeMessage { |
1104 | | pub fn new(identifier: u8, length: u32) -> Self { |
1105 | | ErrorNoticeMessage { |
1106 | | identifier, |
1107 | | length, |
1108 | | message_body: Vec::new(), |
1109 | | } |
1110 | | } |
1111 | | } |
1112 | | |
1113 | | #[test] |
1114 | | fn test_parse_request() { |
1115 | | // An SSLRequest |
1116 | | let buf: &[u8] = &[0x00, 0x00, 0x00, 0x08, 0x04, 0xd2, 0x16, 0x2f]; |
1117 | | let ssl_request = DummyStartupPacket { |
1118 | | length: 8, |
1119 | | proto_major: PGSQL_DUMMY_PROTO_MAJOR, |
1120 | | proto_minor: PGSQL_DUMMY_PROTO_MINOR_SSL, |
1121 | | }; |
1122 | | let request_ok = PgsqlFEMessage::SSLRequest(ssl_request); |
1123 | | |
1124 | | let (_remainder, result) = parse_request(buf).unwrap(); |
1125 | | assert_eq!(result, request_ok); |
1126 | | |
1127 | | // incomplete message |
1128 | | let result = parse_request(&buf[0..7]); |
1129 | | assert!(result.is_err()); |
1130 | | |
1131 | | // Same request, but length is wrong |
1132 | | let buf: &[u8] = &[0x00, 0x00, 0x00, 0x07, 0x04, 0xd2, 0x16, 0x2f]; |
1133 | | let result = parse_request(buf); |
1134 | | assert!(result.is_err()); |
1135 | | |
1136 | | let buf: &[u8] = &[ |
1137 | | /* Length 85 */ 0x00, 0x00, 0x00, 0x55, /* Proto version */ 0x00, 0x03, 0x00, |
1138 | | 0x00, /* user */ 0x75, 0x73, 0x65, 0x72, 0x00, /* [value] rep */ 0x72, 0x65, |
1139 | | 0x70, 0x00, /* database */ 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, |
1140 | | /* [optional] */ 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, |
1141 | | /* replication replication true application_name walreceiver */ |
1142 | | 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, |
1143 | | 0x69, 0x6f, 0x6e, 0x00, 0x74, 0x72, 0x75, 0x65, 0x00, 0x61, 0x70, 0x70, 0x6c, 0x69, |
1144 | | 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x77, 0x61, |
1145 | | 0x6c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x00, 0x00, |
1146 | | ]; |
1147 | | let result = parse_request(buf); |
1148 | | match result { |
1149 | | Ok((remainder, _message)) => { |
1150 | | // there should be nothing left |
1151 | | assert_eq!(remainder.len(), 0); |
1152 | | } |
1153 | | Err(Err::Error(err)) => { |
1154 | | panic!("Result should not be an error: {:?}.", err.code); |
1155 | | } |
1156 | | Err(Err::Incomplete(_)) => { |
1157 | | panic!("Result should not have been incomplete."); |
1158 | | } |
1159 | | _ => { |
1160 | | panic!("Unexpected behavior!"); |
1161 | | } |
1162 | | } |
1163 | | |
1164 | | // A valid startup message/request without optional parameters |
1165 | | // ...&....user.oryx.database.mailstore.. |
1166 | | let buf: &[u8] = &[ |
1167 | | 0x00, 0x00, 0x00, 0x26, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f, |
1168 | | 0x72, 0x79, 0x78, 0x00, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x6d, |
1169 | | 0x61, 0x69, 0x6c, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x00, 0x00, |
1170 | | ]; |
1171 | | let user = PgsqlParameter { |
1172 | | name: PgsqlParameters::User, |
1173 | | value: br#"oryx"#.to_vec(), |
1174 | | }; |
1175 | | let database = PgsqlParameter { |
1176 | | name: PgsqlParameters::Database, |
1177 | | value: br#"mailstore"#.to_vec(), |
1178 | | }; |
1179 | | let mut database_param: Vec<PgsqlParameter> = Vec::new(); |
1180 | | database_param.push(database); |
1181 | | let params = PgsqlStartupParameters { |
1182 | | user, |
1183 | | optional_params: Some(database_param), |
1184 | | }; |
1185 | | let expected_result = PgsqlFEMessage::StartupMessage(StartupPacket { |
1186 | | length: 38, |
1187 | | proto_major: 3, |
1188 | | proto_minor: 0, |
1189 | | params, |
1190 | | }); |
1191 | | let result = parse_request(buf); |
1192 | | match result { |
1193 | | Ok((remainder, message)) => { |
1194 | | assert_eq!(message, expected_result); |
1195 | | assert_eq!(remainder.len(), 0); |
1196 | | } |
1197 | | Err(Err::Error(err)) => { |
1198 | | panic!("Shouldn't be error: {:?}", err.code); |
1199 | | } |
1200 | | Err(Err::Incomplete(needed)) => { |
1201 | | panic!("Should not be Incomplete! Needed: {:?}", needed); |
1202 | | } |
1203 | | _ => { |
1204 | | panic!("Unexpected behavior"); |
1205 | | } |
1206 | | } |
1207 | | |
1208 | | // A valid startup message/request without any optional parameters |
1209 | | // ........user.oryx.. |
1210 | | let buf: &[u8] = &[ |
1211 | | 0x00, 0x00, 0x00, 0x13, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f, |
1212 | | 0x72, 0x79, 0x78, 0x00, 0x00, |
1213 | | ]; |
1214 | | let user = PgsqlParameter { |
1215 | | name: PgsqlParameters::User, |
1216 | | value: br#"oryx"#.to_vec(), |
1217 | | }; |
1218 | | let params = PgsqlStartupParameters { |
1219 | | user, |
1220 | | optional_params: None, |
1221 | | }; |
1222 | | let expected_result = PgsqlFEMessage::StartupMessage(StartupPacket { |
1223 | | length: 19, |
1224 | | proto_major: 3, |
1225 | | proto_minor: 0, |
1226 | | params, |
1227 | | }); |
1228 | | let result = parse_request(buf); |
1229 | | match result { |
1230 | | Ok((remainder, message)) => { |
1231 | | assert_eq!(message, expected_result); |
1232 | | assert_eq!(remainder.len(), 0); |
1233 | | } |
1234 | | Err(Err::Error(err)) => { |
1235 | | panic!("Shouldn't be error: {:?}", err.code); |
1236 | | } |
1237 | | Err(Err::Incomplete(needed)) => { |
1238 | | panic!("Should not be Incomplete! Needed: {:?}", needed); |
1239 | | } |
1240 | | _ => { |
1241 | | panic!("Unexpected behavior"); |
1242 | | } |
1243 | | } |
1244 | | |
1245 | | // A startup message/request with length off by one |
1246 | | let buf: &[u8] = &[ |
1247 | | 0x00, 0x00, 0x00, 0x12, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f, |
1248 | | 0x72, 0x79, 0x78, 0x00, 0x00, |
1249 | | ]; |
1250 | | let result = parse_request(buf); |
1251 | | assert!(result.is_err()); |
1252 | | |
1253 | | // A startup message/request with bad length |
1254 | | let buf: &[u8] = &[ |
1255 | | 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f, |
1256 | | 0x72, 0x79, 0x78, 0x00, 0x00, |
1257 | | ]; |
1258 | | let result = parse_request(buf); |
1259 | | assert!(result.is_err()); |
1260 | | |
1261 | | // A startup message/request with corrupted user param |
1262 | | let buf: &[u8] = &[ |
1263 | | 0x00, 0x00, 0x00, 0x013, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x00, 0x6f, 0x72, |
1264 | | 0x79, 0x78, 0x00, 0x00, |
1265 | | ]; |
1266 | | let result = parse_request(buf); |
1267 | | assert!(result.is_err()); |
1268 | | |
1269 | | // A startup message/request missing the terminator |
1270 | | let buf: &[u8] = &[ |
1271 | | 0x00, 0x00, 0x00, 0x013, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f, |
1272 | | 0x72, 0x79, 0x78, 0x00, |
1273 | | ]; |
1274 | | let result = parse_request(buf); |
1275 | | assert!(result.is_err()); |
1276 | | |
1277 | | // A termination message |
1278 | | let buf: &[u8] = &[0x58, 0x00, 0x00, 0x00, 0x04]; |
1279 | | let result = parse_request(buf); |
1280 | | assert!(result.is_ok()); |
1281 | | |
1282 | | let result = parse_request(&buf[0..3]); |
1283 | | assert!(result.is_err()); |
1284 | | |
1285 | | } |
1286 | | |
1287 | | #[test] |
1288 | | fn test_cancel_request_message() { |
1289 | | // A cancel request message |
1290 | | let buf: &[u8] = &[ |
1291 | | 0x00, 0x00, 0x00, 0x10, // length: 16 (fixed) |
1292 | | 0x04, 0xd2, 0x16, 0x2e, // 1234.5678 - identifies a cancel request |
1293 | | 0x00, 0x00, 0x76, 0x31, // PID: 30257 |
1294 | | 0x23, 0x84, 0xf7, 0x2d]; // Backend key: 595916589 |
1295 | | let result = parse_cancel_request(buf); |
1296 | | assert!(result.is_ok()); |
1297 | | |
1298 | | let result = parse_cancel_request(&buf[0..3]); |
1299 | | assert!(result.is_err()); |
1300 | | |
1301 | | let result = pgsql_parse_startup_packet(buf); |
1302 | | assert!(result.is_ok()); |
1303 | | |
1304 | | let fail_result = pgsql_parse_startup_packet(&buf[0..3]); |
1305 | | assert!(fail_result.is_err()); |
1306 | | |
1307 | | let result = parse_request(buf); |
1308 | | assert!(result.is_ok()); |
1309 | | |
1310 | | let fail_result = parse_request(&buf[0..3]); |
1311 | | assert!(fail_result.is_err()); |
1312 | | } |
1313 | | |
1314 | | |
1315 | | |
1316 | | #[test] |
1317 | | fn test_parse_error_response_code() { |
1318 | | let buf: &[u8] = &[0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00]; |
1319 | | let value_str = "28000".as_bytes(); |
1320 | | let ok_res = PgsqlErrorNoticeMessageField { |
1321 | | field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
1322 | | field_value: value_str.to_vec(), |
1323 | | }; |
1324 | | let result = parse_error_response_code(buf); |
1325 | | assert!(result.is_ok()); |
1326 | | |
1327 | | let (remainder, result) = parse_error_response_code(buf).unwrap(); |
1328 | | assert_eq!(result, ok_res); |
1329 | | assert_eq!(remainder.len(), 0); |
1330 | | |
1331 | | let result = parse_error_response_code(&buf[0..5]); |
1332 | | assert!(result.is_err()); |
1333 | | } |
1334 | | |
1335 | | #[test] |
1336 | | fn test_parse_password_messages() { |
1337 | | // A password message (MD5) |
1338 | | let buf: &[u8] = &[ |
1339 | | 0x70, 0x00, 0x00, 0x00, 0x28, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30, |
1340 | | 0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65, |
1341 | | 0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00, |
1342 | | ]; |
1343 | | let ok_result = PgsqlFEMessage::PasswordMessage(RegularPacket { |
1344 | | identifier: b'p', |
1345 | | length: 40, |
1346 | | payload: br#"md5ceffc01dcde7541829deef6b5e9c9142"#.to_vec(), |
1347 | | }); |
1348 | | let (_remainder, result) = parse_password_message(buf).unwrap(); |
1349 | | assert_eq!(result, ok_result); |
1350 | | |
1351 | | // Length is off by one here |
1352 | | let buf: &[u8] = &[ |
1353 | | 0x70, 0x00, 0x00, 0x00, 0x27, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30, |
1354 | | 0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65, |
1355 | | 0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00, |
1356 | | ]; |
1357 | | let result = parse_password_message(buf); |
1358 | | assert!(result.is_err()); |
1359 | | |
1360 | | // Length also off by one, but now bigger than it should |
1361 | | let buf: &[u8] = &[ |
1362 | | 0x70, 0x00, 0x00, 0x00, 0x29, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30, |
1363 | | 0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65, |
1364 | | 0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00, |
1365 | | ]; |
1366 | | let result = parse_password_message(buf); |
1367 | | assert!(result.is_err()); |
1368 | | |
1369 | | // Incomplete payload |
1370 | | let buf: &[u8] = &[ |
1371 | | 0x70, 0x00, 0x00, 0x00, 0x28, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30, |
1372 | | 0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65, |
1373 | | 0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, |
1374 | | ]; |
1375 | | let result = parse_password_message(buf); |
1376 | | assert!(result.is_err()); |
1377 | | } |
1378 | | |
1379 | | #[test] |
1380 | | fn test_parse_error_response_field() { |
1381 | | // VFATAL |
1382 | | let input: &[u8] = &[0x56, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00]; |
1383 | | |
1384 | | let value_str = "FATAL".as_bytes(); |
1385 | | let ok_res = PgsqlErrorNoticeMessageField { |
1386 | | field_type: PgsqlErrorNoticeFieldType::SeverityNonLocalizable, |
1387 | | field_value: value_str.to_vec(), |
1388 | | }; |
1389 | | |
1390 | | let (remainder, result) = parse_error_response_field(input, true).unwrap(); |
1391 | | assert_eq!(result, ok_res); |
1392 | | assert_eq!(remainder.len(), 0); |
1393 | | |
1394 | | // "Mno pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off " |
1395 | | let input: &[u8] = &[ |
1396 | | 0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e, |
1397 | | 0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, |
1398 | | 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e, |
1399 | | 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f, |
1400 | | 0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30, |
1401 | | 0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65, |
1402 | | 0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, |
1403 | | ]; |
1404 | | |
1405 | | let value_str = br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#; |
1406 | | let ok_res = PgsqlErrorNoticeMessageField { |
1407 | | field_type: PgsqlErrorNoticeFieldType::Message, |
1408 | | field_value: value_str.to_vec(), |
1409 | | }; |
1410 | | |
1411 | | let (remainder, result) = parse_error_response_field(input, true).unwrap(); |
1412 | | assert_eq!(result, ok_res); |
1413 | | assert_eq!(remainder.len(), 0); |
1414 | | |
1415 | | // if incomplete, here we should get an error |
1416 | | let result = parse_error_response_field(&input[0..12], true); |
1417 | | assert!(result.is_err()); |
1418 | | |
1419 | | // C28000 |
1420 | | let input: &[u8] = &[0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00]; |
1421 | | let value_str = "28000".as_bytes(); |
1422 | | let ok_res = PgsqlErrorNoticeMessageField { |
1423 | | field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
1424 | | field_value: value_str.to_vec(), |
1425 | | }; |
1426 | | let (remainder, result) = parse_error_response_field(input, true).unwrap(); |
1427 | | assert_eq!(result, ok_res); |
1428 | | assert_eq!(remainder.len(), 0); |
1429 | | } |
1430 | | |
1431 | | // After sending AuthenticationOk, the backend will send a series of messages with parameters, a backend key message, and finally a ready for query message |
1432 | | #[test] |
1433 | | fn test_parse_startup_phase_wrapup() { |
1434 | | // S .application_name psql |
1435 | | let buf: &[u8] = &[ |
1436 | | 0x53, 0x00, 0x00, 0x00, 0x1a, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, |
1437 | | 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x70, 0x73, 0x71, 0x6c, 0x00, |
1438 | | ]; |
1439 | | |
1440 | | let ok_res = PgsqlBEMessage::ParameterStatus(ParameterStatusMessage { |
1441 | | identifier: b'S', |
1442 | | length: 26, |
1443 | | param: PgsqlParameter { |
1444 | | name: PgsqlParameters::ApplicationName, |
1445 | | value: br#"psql"#.to_vec(), |
1446 | | }, |
1447 | | }); |
1448 | | |
1449 | | let (_remainder, result) = parse_parameter_status_message(buf).unwrap(); |
1450 | | assert_eq!(result, ok_res); |
1451 | | |
1452 | | let result = pgsql_parse_response(buf); |
1453 | | match result { |
1454 | | Ok((_remainder, message)) => { |
1455 | | assert_eq!(message, ok_res); |
1456 | | } |
1457 | | Err(Err::Error(err)) => { |
1458 | | panic!( |
1459 | | "Shouldn't be err {:?}, expected Ok(_). Remainder is: {:?} ", |
1460 | | err.code, err.input |
1461 | | ); |
1462 | | } |
1463 | | Err(Err::Incomplete(needed)) => { |
1464 | | panic!("Should not be incomplete {:?}, expected Ok(_)", needed); |
1465 | | } |
1466 | | _ => panic!("Unexpected behavior, expected Ok(_)"), |
1467 | | } |
1468 | | |
1469 | | // S .integer_datetimes on |
1470 | | let buf: &[u8] = &[ |
1471 | | 0x53, 0x00, 0x00, 0x00, 0x19, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x64, |
1472 | | 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x00, 0x6f, 0x6e, 0x00, |
1473 | | ]; |
1474 | | let result = parse_parameter_status_message(buf); |
1475 | | assert!(result.is_ok()); |
1476 | | |
1477 | | // K =.... // PID 61 Key 3152142766 |
1478 | | let buf: &[u8] = &[ |
1479 | | 0x4b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0xe1, 0xe1, 0xae, |
1480 | | ]; |
1481 | | |
1482 | | let result = parse_backend_key_data_message(buf); |
1483 | | assert!(result.is_ok()); |
1484 | | |
1485 | | // Z .I |
1486 | | let buf: &[u8] = &[0x5a, 0x00, 0x00, 0x00, 0x05, 0x49]; |
1487 | | let result = parse_ready_for_query(buf); |
1488 | | assert!(result.is_ok()); |
1489 | | } |
1490 | | |
1491 | | #[test] |
1492 | | fn test_parse_error_notice_fields() { |
1493 | | let input: &[u8] = &[0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x00]; |
1494 | | |
1495 | | let field1 = PgsqlErrorNoticeMessageField { |
1496 | | field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable, |
1497 | | field_value: br#"FATAL"#.to_vec(), |
1498 | | }; |
1499 | | let field2 = PgsqlErrorNoticeMessageField { |
1500 | | field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
1501 | | field_value: br#"28000"#.to_vec(), |
1502 | | }; |
1503 | | let field3 = PgsqlErrorNoticeMessageField{ |
1504 | | field_type: PgsqlErrorNoticeFieldType::Message, |
1505 | | field_value: br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#.to_vec(), |
1506 | | }; |
1507 | | // let field4 = PgsqlErrorNoticeMessageField { |
1508 | | // field_type: PgsqlErrorNoticeFieldType::TerminatorToken, |
1509 | | // field_value: br#""#.to_vec(), |
1510 | | // }; |
1511 | | |
1512 | | let mut ok_res: Vec<PgsqlErrorNoticeMessageField> = Vec::new(); |
1513 | | ok_res.push(field1); |
1514 | | // ok_res.push(field4); |
1515 | | |
1516 | | let (remainder, result) = parse_error_notice_fields(input, true).unwrap(); |
1517 | | assert_eq!(result, ok_res); |
1518 | | assert_eq!(remainder.len(), 0); |
1519 | | // ok_res.pop(); |
1520 | | |
1521 | | ok_res.push(field2); |
1522 | | ok_res.push(field3); |
1523 | | |
1524 | | // let field4 = PgsqlErrorNoticeMessageField { |
1525 | | // field_type: PgsqlErrorNoticeFieldType::TerminatorToken, |
1526 | | // field_value: br#""#.to_vec(), |
1527 | | // }; |
1528 | | |
1529 | | // ok_res.push(field4); |
1530 | | |
1531 | | let input: &[u8] = &[ |
1532 | | 0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00, |
1533 | | 0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e, |
1534 | | 0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, |
1535 | | 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e, |
1536 | | 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f, |
1537 | | 0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30, |
1538 | | 0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65, |
1539 | | 0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, 0x00, |
1540 | | ]; |
1541 | | |
1542 | | let (remainder, result) = parse_error_notice_fields(input, true).unwrap(); |
1543 | | assert_eq!(result, ok_res); |
1544 | | assert_eq!(remainder.len(), 0); |
1545 | | |
1546 | | let input: &[u8] = &[ |
1547 | | 0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00, |
1548 | | 0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e, |
1549 | | 0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, |
1550 | | 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e, |
1551 | | 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f, |
1552 | | 0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30, |
1553 | | 0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65, |
1554 | | 0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, 0x46, 0x61, |
1555 | | 0x75, 0x74, 0x68, 0x2e, 0x63, 0x00, 0x4c, 0x34, 0x38, 0x31, 0x00, 0x52, 0x43, 0x6c, |
1556 | | 0x69, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, |
1557 | | 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, |
1558 | | ]; |
1559 | | |
1560 | | let result = parse_error_notice_fields(input, true); |
1561 | | assert!(result.is_ok()); |
1562 | | |
1563 | | let result = parse_error_notice_fields(&input[0..12], true); |
1564 | | match result { |
1565 | | Ok((_remainder, _message)) => panic!("Result should not be ok, but incomplete."), |
1566 | | |
1567 | | Err(Err::Error(err)) => { |
1568 | | panic!("Shouldn't be error: {:?}", err.code); |
1569 | | } |
1570 | | Err(Err::Incomplete(needed)) => { |
1571 | | assert_eq!(needed, Needed::new(2)); |
1572 | | } |
1573 | | _ => panic!("Unexpected behavior."), |
1574 | | } |
1575 | | } |
1576 | | |
1577 | | #[test] |
1578 | | fn test_parse_error_notice_response() { |
1579 | | // test case buffer |
1580 | | let buf: &[u8] = &[ |
1581 | | /* identifier */ 0x45, /* length */ 0x00, 0x00, 0x00, 0x96, |
1582 | | /* Severity */ 0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, /* Code */ 0x43, |
1583 | | 0x32, 0x38, 0x30, 0x30, 0x30, 0x00, /* Message */ 0x4d, 0x6e, 0x6f, 0x20, 0x70, |
1584 | | 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x65, 0x6e, 0x74, |
1585 | | 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, |
1586 | | 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, |
1587 | | 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x22, 0x31, |
1588 | | 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30, 0x2e, 0x31, 0x31, 0x22, 0x2c, |
1589 | | 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65, 0x70, 0x22, 0x2c, 0x20, 0x53, |
1590 | | 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, /* File */ 0x46, 0x61, 0x75, 0x74, 0x68, |
1591 | | 0x2e, 0x63, 0x00, /* Line */ 0x4c, 0x34, 0x38, 0x31, 0x00, |
1592 | | /* Routine */ 0x52, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68, |
1593 | | 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, |
1594 | | ]; |
1595 | | |
1596 | | // expected result |
1597 | | let field1 = PgsqlErrorNoticeMessageField { |
1598 | | field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable, |
1599 | | field_value: br#"FATAL"#.to_vec(), |
1600 | | }; |
1601 | | let field2 = PgsqlErrorNoticeMessageField { |
1602 | | field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
1603 | | field_value: br#"28000"#.to_vec(), |
1604 | | }; |
1605 | | let field3 = PgsqlErrorNoticeMessageField { |
1606 | | field_type: PgsqlErrorNoticeFieldType::Message, |
1607 | | field_value: br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#.to_vec(), |
1608 | | }; |
1609 | | let field4 = PgsqlErrorNoticeMessageField { |
1610 | | field_type: PgsqlErrorNoticeFieldType::File, |
1611 | | field_value: br#"auth.c"#.to_vec(), |
1612 | | }; |
1613 | | let field5 = PgsqlErrorNoticeMessageField { |
1614 | | field_type: PgsqlErrorNoticeFieldType::Line, |
1615 | | field_value: br#"481"#.to_vec(), |
1616 | | }; |
1617 | | let field6 = PgsqlErrorNoticeMessageField { |
1618 | | field_type: PgsqlErrorNoticeFieldType::Routine, |
1619 | | field_value: br#"ClientAuthentication"#.to_vec(), |
1620 | | }; |
1621 | | |
1622 | | let mut payload = ErrorNoticeMessage::new(b'E', 150); |
1623 | | payload.message_body.push(field1); |
1624 | | payload.message_body.push(field2); |
1625 | | payload.message_body.push(field3); |
1626 | | payload.message_body.push(field4); |
1627 | | payload.message_body.push(field5); |
1628 | | payload.message_body.push(field6); |
1629 | | |
1630 | | let ok_res = PgsqlBEMessage::ErrorResponse(payload); |
1631 | | |
1632 | | let result = pgsql_parse_response(buf); |
1633 | | match result { |
1634 | | Ok((remainder, message)) => { |
1635 | | assert_eq!(message, ok_res); |
1636 | | assert_eq!(remainder.len(), 0); |
1637 | | } |
1638 | | Err(Err::Error(err)) => { |
1639 | | panic!("Shouldn't be error: {:?}", err.code); |
1640 | | } |
1641 | | Err(Err::Incomplete(needed)) => { |
1642 | | panic!("Should not be Incomplete! Needed: {:?}", needed); |
1643 | | } |
1644 | | _ => { |
1645 | | panic!("Unexpected behavior"); |
1646 | | } |
1647 | | } |
1648 | | |
1649 | | let result_incomplete = pgsql_parse_response(&buf[0..22]); |
1650 | | match result_incomplete { |
1651 | | Err(Err::Incomplete(needed)) => { |
1652 | | // parser first tries to take whole message (length + identifier = 151), but buffer is incomplete |
1653 | | assert_eq!(needed, Needed::new(129)); |
1654 | | } |
1655 | | _ => { |
1656 | | panic!("Unexpected behavior. Should be incomplete."); |
1657 | | } |
1658 | | } |
1659 | | |
1660 | | //repeat for different case-scenarios: |
1661 | | // - message is valid |
1662 | | // some invalid character |
1663 | | } |
1664 | | |
1665 | | #[test] |
1666 | | fn test_parse_notice_response() { |
1667 | | // N .SDEBUG VDEBUG C23505 Mduplicate key value violates unique constraint "unique_a" DKey (a)=(mea5) already exists. Fnbtinsert.c L397 R_bt_check_unique |
1668 | | let buf: &[u8] = &[ |
1669 | | 0x4e, 0x00, 0x00, 0x00, 0x99, 0x53, 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x56, 0x44, |
1670 | | 0x45, 0x42, 0x55, 0x47, 0x00, 0x43, 0x32, 0x33, 0x35, 0x30, 0x35, 0x00, 0x4d, 0x64, |
1671 | | 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x76, |
1672 | | 0x61, 0x6c, 0x75, 0x65, 0x20, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20, |
1673 | | 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, |
1674 | | 0x69, 0x6e, 0x74, 0x20, 0x22, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x61, 0x22, |
1675 | | 0x00, 0x44, 0x4b, 0x65, 0x79, 0x20, 0x28, 0x61, 0x29, 0x3d, 0x28, 0x6d, 0x65, 0x61, |
1676 | | 0x35, 0x29, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x65, 0x78, 0x69, |
1677 | | 0x73, 0x74, 0x73, 0x2e, 0x00, 0x46, 0x6e, 0x62, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72, |
1678 | | 0x74, 0x2e, 0x63, 0x00, 0x4c, 0x33, 0x39, 0x37, 0x00, 0x52, 0x5f, 0x62, 0x74, 0x5f, |
1679 | | 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x00, 0x00, |
1680 | | ]; |
1681 | | |
1682 | | // expected result |
1683 | | let field1 = PgsqlErrorNoticeMessageField { |
1684 | | field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable, |
1685 | | field_value: br#"DEBUG"#.to_vec(), |
1686 | | }; |
1687 | | let field2 = PgsqlErrorNoticeMessageField { |
1688 | | field_type: PgsqlErrorNoticeFieldType::SeverityNonLocalizable, |
1689 | | field_value: br#"DEBUG"#.to_vec(), |
1690 | | }; |
1691 | | let field3 = PgsqlErrorNoticeMessageField { |
1692 | | field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode, |
1693 | | field_value: br#"23505"#.to_vec(), |
1694 | | }; |
1695 | | let field4 = PgsqlErrorNoticeMessageField { |
1696 | | field_type: PgsqlErrorNoticeFieldType::Message, |
1697 | | field_value: br#"duplicate key value violates unique constraint "unique_a""#.to_vec(), |
1698 | | }; |
1699 | | let field5 = PgsqlErrorNoticeMessageField { |
1700 | | field_type: PgsqlErrorNoticeFieldType::Detail, |
1701 | | field_value: br#"Key (a)=(mea5) already exists."#.to_vec(), |
1702 | | }; |
1703 | | let field6 = PgsqlErrorNoticeMessageField { |
1704 | | field_type: PgsqlErrorNoticeFieldType::File, |
1705 | | field_value: br#"nbtinsert.c"#.to_vec(), |
1706 | | }; |
1707 | | let field7 = PgsqlErrorNoticeMessageField { |
1708 | | field_type: PgsqlErrorNoticeFieldType::Line, |
1709 | | field_value: br#"397"#.to_vec(), |
1710 | | }; |
1711 | | let field8 = PgsqlErrorNoticeMessageField { |
1712 | | field_type: PgsqlErrorNoticeFieldType::Routine, |
1713 | | field_value: br#"_bt_check_unique"#.to_vec(), |
1714 | | }; |
1715 | | |
1716 | | let mut payload = ErrorNoticeMessage::new(b'N', 153); |
1717 | | payload.message_body.push(field1); |
1718 | | payload.message_body.push(field2); |
1719 | | payload.message_body.push(field3); |
1720 | | payload.message_body.push(field4); |
1721 | | payload.message_body.push(field5); |
1722 | | payload.message_body.push(field6); |
1723 | | payload.message_body.push(field7); |
1724 | | payload.message_body.push(field8); |
1725 | | |
1726 | | let ok_res = PgsqlBEMessage::NoticeResponse(payload); |
1727 | | |
1728 | | let result = pgsql_parse_response(buf); |
1729 | | match result { |
1730 | | Ok((remainder, message)) => { |
1731 | | assert_eq!(message, ok_res); |
1732 | | assert_eq!(remainder.len(), 0); |
1733 | | } |
1734 | | Err(Err::Error(err)) => { |
1735 | | panic!("Shouldn't be error: {:?}", err.code); |
1736 | | } |
1737 | | Err(Err::Incomplete(needed)) => { |
1738 | | panic!("Should not be Incomplete! Needed: {:?} ", needed); |
1739 | | } |
1740 | | _ => { |
1741 | | panic!("Unexpected behavior"); |
1742 | | } |
1743 | | } |
1744 | | } |
1745 | | |
1746 | | #[test] |
1747 | | fn test_parse_sasl_authentication_message() { |
1748 | | let buf: &[u8] = &[ |
1749 | | /* identifier R */ 0x52, /* length 28 */ 0x00, 0x00, 0x00, 0x1c, |
1750 | | /* auth_type */ 0x00, 0x00, 0x00, 0x0a, /* SCRAM-SHA-256-PLUS */ 0x53, 0x43, |
1751 | | 0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x50, 0x4c, |
1752 | | 0x55, 0x53, 0x00, 0x00, |
1753 | | ]; |
1754 | | let mechanism = vec![SASLAuthenticationMechanism::ScramSha256Plus]; |
1755 | | let ok_res = PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage { |
1756 | | identifier: b'R', |
1757 | | length: 28, |
1758 | | auth_type: 10, |
1759 | | auth_mechanisms: mechanism, |
1760 | | }); |
1761 | | |
1762 | | let result = pgsql_parse_response(buf); |
1763 | | match result { |
1764 | | Ok((remainder, message)) => { |
1765 | | assert_eq!(message, ok_res); |
1766 | | assert_eq!(remainder.len(), 0); |
1767 | | } |
1768 | | Err(Err::Error(err)) => { |
1769 | | panic!("Shouldn't be error: {:?}", err.code); |
1770 | | } |
1771 | | Err(Err::Incomplete(needed)) => { |
1772 | | panic!("Should not be Incomplete! Needed: {:?}", needed); |
1773 | | } |
1774 | | _ => { |
1775 | | panic!("Unexpected behavior"); |
1776 | | } |
1777 | | } |
1778 | | |
1779 | | let buf: &[u8] = &[ |
1780 | | /* identifier R */ 0x52, /* length 42 */ 0x00, 0x00, 0x00, 0x2a, |
1781 | | /* auth_type */ 0x00, 0x00, 0x00, 0x0a, /* SCRAM-SHA-256-PLUS */ 0x53, 0x43, |
1782 | | 0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x50, 0x4c, |
1783 | | 0x55, 0x53, 0x00, /* SCRAM-SHA-256 */ 0x53, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x53, |
1784 | | 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x00, 0x00, |
1785 | | ]; |
1786 | | let mechanism = vec![ |
1787 | | SASLAuthenticationMechanism::ScramSha256Plus, |
1788 | | SASLAuthenticationMechanism::ScramSha256, |
1789 | | ]; |
1790 | | let ok_res = PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage { |
1791 | | identifier: b'R', |
1792 | | length: 42, |
1793 | | auth_type: 10, |
1794 | | auth_mechanisms: mechanism, |
1795 | | }); |
1796 | | |
1797 | | let result = pgsql_parse_response(buf); |
1798 | | match result { |
1799 | | Ok((remainder, message)) => { |
1800 | | assert_eq!(message, ok_res); |
1801 | | assert_eq!(remainder.len(), 0); |
1802 | | } |
1803 | | Err(Err::Error(err)) => { |
1804 | | panic!("Shouldn't be error: {:?}", err.code); |
1805 | | } |
1806 | | Err(Err::Incomplete(needed)) => { |
1807 | | panic!("Should not be Incomplete! Needed: {:?}", needed); |
1808 | | } |
1809 | | _ => { |
1810 | | panic!("Unexpected behavior"); |
1811 | | } |
1812 | | } |
1813 | | |
1814 | | let incomplete_result = pgsql_parse_response(&buf[0..27]); |
1815 | | match incomplete_result { |
1816 | | Ok((_remainder, _message)) => panic!("Should not be Ok(_), expected Incomplete!"), |
1817 | | Err(Err::Error(err)) => { |
1818 | | panic!("Should not be error {:?}, expected Incomplete!", err.code) |
1819 | | } |
1820 | | Err(Err::Incomplete(needed)) => assert_eq!(needed, Needed::new(16)), |
1821 | | _ => panic!("Unexpected behavior, expected Incomplete."), |
1822 | | } |
1823 | | } |
1824 | | |
1825 | | #[test] |
1826 | | fn test_parse_sasl_continue_authentication_message() { |
1827 | | // As found in: https://blog.hackeriet.no/Better-password-hashing-in-PostgreSQL/ |
1828 | | let buf: &[u8] = &[ |
1829 | | /* 'R' */ 0x52, /* 92 */ 0x00, 0x00, 0x00, 0x5c, /* 11 */ 0x00, 0x00, |
1830 | | 0x00, 0x0b, /* challenge data*/ 0x72, 0x3d, 0x2f, 0x7a, 0x2b, 0x67, 0x69, 0x5a, |
1831 | | 0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41, 0x65, 0x48, 0x72, |
1832 | | 0x37, 0x63, 0x76, 0x70, 0x71, 0x56, 0x33, 0x75, 0x6f, 0x37, 0x47, 0x2f, 0x62, 0x4a, |
1833 | | 0x42, 0x49, 0x4a, 0x4f, 0x33, 0x70, 0x6a, 0x56, 0x4d, 0x37, 0x74, 0x33, 0x6e, 0x67, |
1834 | | 0x2c, 0x73, 0x3d, 0x34, 0x55, 0x56, 0x36, 0x38, 0x62, 0x49, 0x6b, 0x43, 0x38, 0x66, |
1835 | | 0x39, 0x2f, 0x58, 0x38, 0x78, 0x48, 0x37, 0x61, 0x50, 0x68, 0x67, 0x3d, 0x3d, 0x2c, |
1836 | | 0x69, 0x3d, 0x34, 0x30, 0x39, 0x36, |
1837 | | ]; |
1838 | | |
1839 | | let ok_res = PgsqlBEMessage::AuthenticationSASLContinue( |
1840 | | AuthenticationMessage { |
1841 | | identifier: b'R', |
1842 | | length: 92, |
1843 | | auth_type: 11, |
1844 | | payload: br#"r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,s=4UV68bIkC8f9/X8xH7aPhg==,i=4096"#.to_vec(), |
1845 | | }); |
1846 | | |
1847 | | let result = pgsql_parse_response(buf); |
1848 | | match result { |
1849 | | Ok((remainder, message)) => { |
1850 | | assert_eq!(message, ok_res); |
1851 | | assert_eq!(remainder.len(), 0); |
1852 | | } |
1853 | | Err(Err::Error(err)) => { |
1854 | | panic!("Shouldn't be error {:?} expected Ok(_)", err.code) |
1855 | | } |
1856 | | Err(Err::Incomplete(needed)) => { |
1857 | | panic!("shouldn't be incomplete {:?}, expected Ok(_)", needed) |
1858 | | } |
1859 | | _ => panic!("Unexpected behavior, expected Ok(_)"), |
1860 | | } |
1861 | | |
1862 | | let result_incomplete = pgsql_parse_response(&buf[0..31]); |
1863 | | match result_incomplete { |
1864 | | Ok((_remainder, _message)) => panic!("Should not be Ok(_), expected Incomplete!"), |
1865 | | Err(Err::Error(err)) => { |
1866 | | panic!("Shouldn't be error {:?} expected Incomplete!", err.code) |
1867 | | } |
1868 | | Err(Err::Incomplete(needed)) => { |
1869 | | assert_eq!(needed, Needed::new(62)); |
1870 | | } |
1871 | | _ => panic!("Unexpected behavior, expected Ok(_)"), |
1872 | | } |
1873 | | } |
1874 | | |
1875 | | #[test] |
1876 | | fn test_parse_sasl_final_authentication_message() { |
1877 | | let buf: &[u8] = &[ |
1878 | | /* R */ 0x52, /* 54 */ 0x00, 0x00, 0x00, 0x36, /* 12 */ 0x00, 0x00, |
1879 | | 0x00, 0x0c, /* signature */ 0x76, 0x3d, 0x64, 0x31, 0x50, 0x58, 0x61, 0x38, 0x54, |
1880 | | 0x4b, 0x46, 0x50, 0x5a, 0x72, 0x52, 0x33, 0x4d, 0x42, 0x52, 0x6a, 0x4c, 0x79, 0x33, |
1881 | | 0x2b, 0x4a, 0x36, 0x79, 0x78, 0x72, 0x66, 0x77, 0x2f, 0x7a, 0x7a, 0x70, 0x38, 0x59, |
1882 | | 0x54, 0x39, 0x65, 0x78, 0x56, 0x37, 0x73, 0x38, 0x3d, |
1883 | | ]; |
1884 | | let ok_res = PgsqlBEMessage::AuthenticationSASLFinal(AuthenticationMessage { |
1885 | | identifier: b'R', |
1886 | | length: 54, |
1887 | | auth_type: 12, |
1888 | | payload: br#"v=d1PXa8TKFPZrR3MBRjLy3+J6yxrfw/zzp8YT9exV7s8="#.to_vec(), |
1889 | | }); |
1890 | | |
1891 | | let result = pgsql_parse_response(buf); |
1892 | | match result { |
1893 | | Ok((remainder, message)) => { |
1894 | | assert_eq!(message, ok_res); |
1895 | | assert_eq!(remainder.len(), 0); |
1896 | | } |
1897 | | Err(Err::Error(err)) => { |
1898 | | panic!("Shouldn't be error {:?}, expected Ok(_)", err.code); |
1899 | | } |
1900 | | Err(Err::Incomplete(needed)) => { |
1901 | | panic!("Shouldn't be incomplete {:?}, expected OK(_)", needed); |
1902 | | } |
1903 | | _ => panic!("Unexpected behavior, expected Ok(_)"), |
1904 | | } |
1905 | | |
1906 | | let result_incomplete = pgsql_parse_response(&buf[0..34]); |
1907 | | match result_incomplete { |
1908 | | Err(Err::Incomplete(needed)) => { |
1909 | | assert_eq!(needed, Needed::new(21)); |
1910 | | } |
1911 | | _ => panic!("Unexpected behavior, expected incomplete."), |
1912 | | } |
1913 | | |
1914 | | let bad_buf: &[u8] = &[ |
1915 | | /* ` */ 0x60, /* 54 */ 0x00, 0x00, 0x00, 0x36, /* 12 */ 0x00, 0x00, |
1916 | | 0x00, 0x0c, /* signature */ 0x76, 0x3d, 0x64, 0x31, 0x50, 0x58, 0x61, 0x38, 0x54, |
1917 | | 0x4b, 0x46, 0x50, 0x5a, 0x72, 0x52, 0x33, 0x4d, 0x42, 0x52, 0x6a, 0x4c, 0x79, 0x33, |
1918 | | 0x2b, 0x4a, 0x36, 0x79, 0x78, 0x72, 0x66, 0x77, 0x2f, 0x7a, 0x7a, 0x70, 0x38, 0x59, |
1919 | | 0x54, 0x39, 0x65, 0x78, 0x56, 0x37, 0x73, 0x38, 0x3d, |
1920 | | ]; |
1921 | | let (remainder, result) = pgsql_parse_response(bad_buf).expect("parsing sasl final response failed"); |
1922 | | let res = PgsqlBEMessage::UnknownMessageType(RegularPacket { |
1923 | | identifier: b'`', |
1924 | | length: 54, |
1925 | | payload: bad_buf[5..].to_vec(), |
1926 | | }); |
1927 | | assert_eq!(result, res); |
1928 | | assert!(remainder.is_empty()); |
1929 | | } |
1930 | | |
1931 | | #[test] |
1932 | | fn test_parse_sasl_frontend_messages() { |
1933 | | // SASL Initial Response |
1934 | | // (as seen in https://blog.hackeriet.no/Better-password-hashing-in-PostgreSQL/) |
1935 | | let buf: &[u8] = &[ |
1936 | | /* p */ 0x70, /* 54 */ 0x00, 0x00, 0x00, 0x36, |
1937 | | /* sasl mechanism */ 0x53, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d, |
1938 | | 0x32, 0x35, 0x36, 0x00, /* 32 */ 0x00, 0x00, 0x00, 0x20, |
1939 | | /* FE 1st msg */ 0x6e, 0x2c, 0x2c, 0x6e, 0x3d, 0x2c, 0x72, 0x3d, 0x2f, 0x7a, 0x2b, |
1940 | | 0x67, 0x69, 0x5a, 0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41, |
1941 | | 0x65, 0x48, 0x72, 0x37, 0x63, 0x76, 0x70, |
1942 | | ]; |
1943 | | let ok_res = PgsqlFEMessage::SASLInitialResponse(SASLInitialResponsePacket { |
1944 | | identifier: b'p', |
1945 | | length: 54, |
1946 | | auth_mechanism: SASLAuthenticationMechanism::ScramSha256, |
1947 | | param_length: 32, |
1948 | | sasl_param: br#"n,,n=,r=/z+giZiTxAH7r8sNAeHr7cvp"#.to_vec(), |
1949 | | }); |
1950 | | |
1951 | | let result = parse_sasl_initial_response(buf); |
1952 | | match result { |
1953 | | Ok((remainder, message)) => { |
1954 | | assert_eq!(message, ok_res); |
1955 | | assert_eq!(remainder.len(), 0); |
1956 | | } |
1957 | | Err(Err::Error(err)) => { |
1958 | | panic!("Shouldn't be error {:?}, expected Ok(_)", err.code) |
1959 | | } |
1960 | | Err(Err::Incomplete(needed)) => { |
1961 | | panic!("Shouldn't be incomplete: {:?}, expected Ok(_)", needed) |
1962 | | } |
1963 | | _ => panic!("Unexpected behavior, expected Ok(_)"), |
1964 | | } |
1965 | | |
1966 | | let buf: &[u8] = &[ |
1967 | | /* p */ 0x70, /* 108 */ 0x00, 0x00, 0x00, 0x6c, /* final msg*/ 0x63, |
1968 | | 0x3d, 0x62, 0x69, 0x77, 0x73, 0x2c, 0x72, 0x3d, 0x2f, 0x7a, 0x2b, 0x67, 0x69, 0x5a, |
1969 | | 0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41, 0x65, 0x48, 0x72, |
1970 | | 0x37, 0x63, 0x76, 0x70, 0x71, 0x56, 0x33, 0x75, 0x6f, 0x37, 0x47, 0x2f, 0x62, 0x4a, |
1971 | | 0x42, 0x49, 0x4a, 0x4f, 0x33, 0x70, 0x6a, 0x56, 0x4d, 0x37, 0x74, 0x33, 0x6e, 0x67, |
1972 | | 0x2c, 0x70, 0x3d, 0x41, 0x46, 0x70, 0x53, 0x59, 0x48, 0x2f, 0x4b, 0x2f, 0x38, 0x62, |
1973 | | 0x75, 0x78, 0x31, 0x6d, 0x52, 0x50, 0x55, 0x77, 0x78, 0x54, 0x65, 0x38, 0x6c, 0x42, |
1974 | | 0x75, 0x49, 0x50, 0x45, 0x79, 0x68, 0x69, 0x2f, 0x37, 0x55, 0x46, 0x50, 0x51, 0x70, |
1975 | | 0x53, 0x72, 0x34, 0x41, 0x3d, |
1976 | | ]; |
1977 | | |
1978 | | let ok_res = PgsqlFEMessage::SASLResponse( |
1979 | | RegularPacket { |
1980 | | identifier: b'p', |
1981 | | length: 108, |
1982 | | payload: br#"c=biws,r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,p=AFpSYH/K/8bux1mRPUwxTe8lBuIPEyhi/7UFPQpSr4A="#.to_vec(), |
1983 | | }); |
1984 | | |
1985 | | let result = parse_sasl_response(buf); |
1986 | | match result { |
1987 | | Ok((_remainder, message)) => { |
1988 | | assert_eq!(message, ok_res); |
1989 | | } |
1990 | | Err(Err::Error(err)) => { |
1991 | | panic!("Shouldn't be error: {:?} expected Ok(_)", err.code) |
1992 | | } |
1993 | | Err(Err::Incomplete(needed)) => { |
1994 | | panic!("Shouldn't be incomplete: {:?}, expected Ok(_)", needed) |
1995 | | } |
1996 | | _ => panic!("Unexpected behavior, should be Ok(_)"), |
1997 | | } |
1998 | | } |
1999 | | |
2000 | | // Test messages with fixed formats, like AuthenticationSSPI |
2001 | | #[test] |
2002 | | fn test_parse_simple_authentication_requests() { |
2003 | | let buf: &[u8] = &[ |
2004 | | /* R */ 0x52, /* 8 */ 0x00, 0x00, 0x00, 0x08, /* 9 */ 0x00, 0x00, 0x00, |
2005 | | 0x09, |
2006 | | ]; |
2007 | | |
2008 | | let ok_res = PgsqlBEMessage::AuthenticationSSPI(AuthenticationMessage { |
2009 | | identifier: b'R', |
2010 | | length: 8, |
2011 | | auth_type: 9, |
2012 | | payload: Vec::<u8>::new(), |
2013 | | }); |
2014 | | |
2015 | | let (_remainder, result) = pgsql_parse_response(buf).unwrap(); |
2016 | | assert_eq!(result, ok_res); |
2017 | | } |
2018 | | |
2019 | | #[test] |
2020 | | fn test_parse_response() { |
2021 | | // An SSL response - N |
2022 | | let buf: &[u8] = &[0x4e]; |
2023 | | let response_ok = PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLRejected); |
2024 | | let (_remainder, result) = parse_ssl_response(buf).unwrap(); |
2025 | | assert_eq!(result, response_ok); |
2026 | | |
2027 | | // An SSL response - S |
2028 | | let buf: &[u8] = &[0x53]; |
2029 | | let response_ok = PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLAccepted); |
2030 | | |
2031 | | let (_remainder, result) = parse_ssl_response(buf).unwrap(); |
2032 | | assert_eq!(result, response_ok); |
2033 | | |
2034 | | // Not an SSL response |
2035 | | let buf: &[u8] = &[0x52]; |
2036 | | let result = parse_ssl_response(buf); |
2037 | | assert!(result.is_err()); |
2038 | | |
2039 | | // - auth MD5 |
2040 | | let buf: &[u8] = &[ |
2041 | | 0x52, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x05, 0xf2, 0x11, 0xa3, 0xed, |
2042 | | ]; |
2043 | | let ok_res = PgsqlBEMessage::AuthenticationMD5Password(AuthenticationMessage { |
2044 | | identifier: b'R', |
2045 | | length: 12, |
2046 | | auth_type: 5, |
2047 | | payload: vec![0xf2, 0x11, 0xa3, 0xed], |
2048 | | }); |
2049 | | let result = pgsql_parse_response(buf); |
2050 | | match result { |
2051 | | Ok((remainder, message)) => { |
2052 | | assert_eq!(message, ok_res); |
2053 | | assert_eq!(remainder.len(), 0); |
2054 | | } |
2055 | | Err(Err::Error(err)) => { |
2056 | | panic!("Shouldn't be error: {:?}", err.code); |
2057 | | } |
2058 | | Err(Err::Incomplete(needed)) => { |
2059 | | panic!("Should not be Incomplete! Needed: {:?}", needed); |
2060 | | } |
2061 | | _ => { |
2062 | | panic!("Unexpected behavior"); |
2063 | | } |
2064 | | } |
2065 | | |
2066 | | // - auth clear text... |
2067 | | let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03]; |
2068 | | let ok_res = PgsqlBEMessage::AuthenticationCleartextPassword(AuthenticationMessage { |
2069 | | identifier: b'R', |
2070 | | length: 8, |
2071 | | auth_type: 3, |
2072 | | payload: Vec::<u8>::new(), |
2073 | | }); |
2074 | | let result = pgsql_parse_response(buf); |
2075 | | match result { |
2076 | | Ok((remainder, message)) => { |
2077 | | assert_eq!(remainder.len(), 0); |
2078 | | assert_eq!(message, ok_res); |
2079 | | } |
2080 | | Err(Err::Error(err)) => { |
2081 | | panic!("Shouldn't be error: {:?}", err.code); |
2082 | | } |
2083 | | Err(Err::Incomplete(needed)) => { |
2084 | | panic!("Should not be incomplete. Needed {:?}", needed); |
2085 | | } |
2086 | | _ => { |
2087 | | panic!("Unexpected behavior"); |
2088 | | } |
2089 | | } |
2090 | | |
2091 | | let result = pgsql_parse_response(&buf[0..6]); |
2092 | | assert!(result.is_err()); |
2093 | | |
2094 | | let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03]; |
2095 | | let result = pgsql_parse_response(buf); |
2096 | | assert!(result.is_err()); |
2097 | | |
2098 | | // - auth Ok |
2099 | | let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00]; |
2100 | | let ok_res = PgsqlBEMessage::AuthenticationOk(AuthenticationMessage { |
2101 | | identifier: b'R', |
2102 | | length: 8, |
2103 | | auth_type: 0, |
2104 | | payload: Vec::<u8>::new(), |
2105 | | }); |
2106 | | let result = pgsql_parse_response(buf); |
2107 | | match result { |
2108 | | Ok((remainder, message)) => { |
2109 | | assert_eq!(message, ok_res); |
2110 | | assert_eq!(remainder.len(), 0); |
2111 | | } |
2112 | | Err(Err::Error(err)) => { |
2113 | | panic!("Should not be error {:?}", err.code); |
2114 | | } |
2115 | | Err(Err::Incomplete(needed)) => { |
2116 | | panic!("Should not be incomplete. Needed: {:?}", needed); |
2117 | | } |
2118 | | _ => { |
2119 | | panic!("Unexpected behavior!"); |
2120 | | } |
2121 | | } |
2122 | | |
2123 | | //A series of response messages from the backend: |
2124 | | // R · S ·application_name |
2125 | | // S ·client_encoding UTF8 S ·DateStyle ISO, MDY |
2126 | | // S &default_transaction_read_only off S ·in_hot_standby off |
2127 | | // S ·integer_datetimes on S ·IntervalStyle postgres |
2128 | | // S ·is_superuser off S ·server_encoding UTF8 |
2129 | | // S ·server_version 14.5 S "session_authorization ctfpost |
2130 | | // S #standard_conforming_strings on S ·TimeZone Europe/Paris |
2131 | | // K ···O··Z ·I |
2132 | | let buf = &[ |
2133 | | 0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, |
2134 | | 0x00, 0x53, 0x00, 0x00, 0x00, 0x16, 0x61, 0x70, |
2135 | | 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, |
2136 | | 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, |
2137 | | 0x53, 0x00, 0x00, 0x00, 0x19, 0x63, 0x6c, 0x69, |
2138 | | 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x6e, 0x63, 0x6f, |
2139 | | 0x64, 0x69, 0x6e, 0x67, 0x00, 0x55, 0x54, 0x46, |
2140 | | 0x38, 0x00, 0x53, 0x00, 0x00, 0x00, 0x17, 0x44, |
2141 | | 0x61, 0x74, 0x65, 0x53, 0x74, 0x79, 0x6c, 0x65, |
2142 | | 0x00, 0x49, 0x53, 0x4f, 0x2c, 0x20, 0x4d, 0x44, |
2143 | | 0x59, 0x00, 0x53, 0x00, 0x00, 0x00, 0x26, 0x64, |
2144 | | 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x74, |
2145 | | 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, |
2146 | | 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, |
2147 | | 0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x66, 0x66, |
2148 | | 0x00, 0x53, 0x00, 0x00, 0x00, 0x17, 0x69, 0x6e, |
2149 | | 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x61, |
2150 | | 0x6e, 0x64, 0x62, 0x79, 0x00, 0x6f, 0x66, 0x66, |
2151 | | 0x00, 0x53, 0x00, 0x00, 0x00, 0x19, 0x69, 0x6e, |
2152 | | 0x74, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x64, 0x61, |
2153 | | 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x00, |
2154 | | 0x6f, 0x6e, 0x00, 0x53, 0x00, 0x00, 0x00, 0x1b, |
2155 | | 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, |
2156 | | 0x53, 0x74, 0x79, 0x6c, 0x65, 0x00, 0x70, 0x6f, |
2157 | | 0x73, 0x74, 0x67, 0x72, 0x65, 0x73, 0x00, 0x53, |
2158 | | 0x00, 0x00, 0x00, 0x15, 0x69, 0x73, 0x5f, 0x73, |
2159 | | 0x75, 0x70, 0x65, 0x72, 0x75, 0x73, 0x65, 0x72, |
2160 | | 0x00, 0x6f, 0x66, 0x66, 0x00, 0x53, 0x00, 0x00, |
2161 | | 0x00, 0x19, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, |
2162 | | 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, |
2163 | | 0x67, 0x00, 0x55, 0x54, 0x46, 0x38, 0x00, 0x53, |
2164 | | 0x00, 0x00, 0x00, 0x18, 0x73, 0x65, 0x72, 0x76, |
2165 | | 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, |
2166 | | 0x6f, 0x6e, 0x00, 0x31, 0x34, 0x2e, 0x35, 0x00, |
2167 | | 0x53, 0x00, 0x00, 0x00, 0x22, 0x73, 0x65, 0x73, |
2168 | | 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x75, 0x74, |
2169 | | 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, |
2170 | | 0x6f, 0x6e, 0x00, 0x63, 0x74, 0x66, 0x70, 0x6f, |
2171 | | 0x73, 0x74, 0x00, 0x53, 0x00, 0x00, 0x00, 0x23, |
2172 | | 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, |
2173 | | 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, |
2174 | | 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69, |
2175 | | 0x6e, 0x67, 0x73, 0x00, 0x6f, 0x6e, 0x00, 0x53, |
2176 | | 0x00, 0x00, 0x00, 0x1a, 0x54, 0x69, 0x6d, 0x65, |
2177 | | 0x5a, 0x6f, 0x6e, 0x65, 0x00, 0x45, 0x75, 0x72, |
2178 | | 0x6f, 0x70, 0x65, 0x2f, 0x50, 0x61, 0x72, 0x69, |
2179 | | 0x73, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0c, 0x00, |
2180 | | 0x00, 0x0b, 0x8d, 0xcf, 0x4f, 0xb6, 0xcf, 0x5a, |
2181 | | 0x00, 0x00, 0x00, 0x05, 0x49 |
2182 | | ]; |
2183 | | |
2184 | | let result = pgsql_parse_response(buf); |
2185 | | assert!(result.is_ok()); |
2186 | | } |
2187 | | |
2188 | | #[test] |
2189 | | fn test_parse_row_description() { |
2190 | | // RowDescription message |
2191 | | // T .. |
2192 | | // source @ . ....... version @ . ....... sid @ . . ..... |
2193 | | let buffer: &[u8] = &[ |
2194 | | 0x54, 0x00, 0x00, 0x00, 0x50, 0x00, 0x03, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00, |
2195 | | 0x00, 0x00, 0x40, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff, |
2196 | | 0xff, 0xff, 0x00, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, |
2197 | | 0x40, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
2198 | | 0x00, 0x00, 0x73, 0x69, 0x64, 0x00, 0x00, 0x00, 0x40, 0x09, 0x00, 0x03, 0x00, 0x00, |
2199 | | 0x00, 0x14, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, |
2200 | | ]; |
2201 | | |
2202 | | let field1 = RowField { |
2203 | | field_name: br#"source"#.to_vec(), |
2204 | | table_oid: 16393, |
2205 | | column_index: 1, |
2206 | | data_type_oid: 25, |
2207 | | data_type_size: -1, |
2208 | | type_modifier: -1, |
2209 | | format_code: 0, |
2210 | | }; |
2211 | | |
2212 | | let field2 = RowField { |
2213 | | field_name: br#"version"#.to_vec(), |
2214 | | table_oid: 16393, |
2215 | | column_index: 2, |
2216 | | data_type_oid: 25, |
2217 | | data_type_size: -1, |
2218 | | type_modifier: -1, |
2219 | | format_code: 0, |
2220 | | }; |
2221 | | |
2222 | | let field3 = RowField { |
2223 | | field_name: br#"sid"#.to_vec(), |
2224 | | table_oid: 16393, |
2225 | | column_index: 3, |
2226 | | data_type_oid: 20, |
2227 | | data_type_size: 8, |
2228 | | type_modifier: -1, |
2229 | | format_code: 0, |
2230 | | }; |
2231 | | |
2232 | | let mut fields_vec = Vec::<RowField>::new(); |
2233 | | fields_vec.push(field1); |
2234 | | fields_vec.push(field2); |
2235 | | fields_vec.push(field3); |
2236 | | |
2237 | | let ok_res = PgsqlBEMessage::RowDescription(RowDescriptionMessage { |
2238 | | identifier: b'T', |
2239 | | length: 80, |
2240 | | field_count: 3, |
2241 | | fields: fields_vec, |
2242 | | }); |
2243 | | |
2244 | | let result = parse_row_description(buffer); |
2245 | | |
2246 | | match result { |
2247 | | Ok((rem, response)) => { |
2248 | | assert_eq!(response, ok_res); |
2249 | | assert!(rem.is_empty()); |
2250 | | } |
2251 | | Err(Err::Incomplete(needed)) => { |
2252 | | panic!("Should not be Incomplete! Needed: {:?}", needed); |
2253 | | } |
2254 | | Err(Err::Error(err)) => { |
2255 | | println!("Remainder is: {:?}", err.input); |
2256 | | panic!("Shouldn't be error: {:?}", err.code); |
2257 | | } |
2258 | | _ => { |
2259 | | panic!("Unexpected behavior"); |
2260 | | } |
2261 | | } |
2262 | | } |
2263 | | |
2264 | | #[test] |
2265 | | fn test_parse_data_row() { |
2266 | | let buffer: &[u8] = &[ |
2267 | | 0x44, 0x00, 0x00, 0x00, 0x23, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x65, 0x74, 0x2f, |
2268 | | 0x6f, 0x70, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x03, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00, |
2269 | | 0x07, 0x32, 0x30, 0x32, 0x31, 0x37, 0x30, 0x31, |
2270 | | ]; |
2271 | | |
2272 | | let result = parse_consolidated_data_row(buffer); |
2273 | | assert!(result.is_ok()); |
2274 | | } |
2275 | | |
2276 | | #[test] |
2277 | | fn test_command_complete() { |
2278 | | let buffer: &[u8] = &[ |
2279 | | 0x43, 0x00, 0x00, 0x00, 0x0d, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x20, 0x33, 0x00, |
2280 | | ]; |
2281 | | |
2282 | | let ok_res = PgsqlBEMessage::CommandComplete(RegularPacket { |
2283 | | identifier: b'C', |
2284 | | length: 13, |
2285 | | payload: b"SELECT 3".to_vec(), |
2286 | | }); |
2287 | | |
2288 | | let result = pgsql_parse_response(buffer); |
2289 | | |
2290 | | match result { |
2291 | | Ok((rem, message)) => { |
2292 | | assert_eq!(ok_res, message); |
2293 | | assert!(rem.is_empty()); |
2294 | | } |
2295 | | Err(Err::Incomplete(needed)) => { |
2296 | | panic!( |
2297 | | "Shouldn't be Incomplete! Expected Ok(). Needed: {:?}", |
2298 | | needed |
2299 | | ); |
2300 | | } |
2301 | | Err(Err::Error(err)) => { |
2302 | | println!("Unparsed slice: {:?}", err.input); |
2303 | | panic!("Shouldn't be Error: {:?}, expected Ok()", err.code); |
2304 | | } |
2305 | | _ => { |
2306 | | panic!("Unexpected behavior, should be Ok()"); |
2307 | | } |
2308 | | } |
2309 | | } |
2310 | | |
2311 | | #[test] |
2312 | | fn test_parse_notification_response() { |
2313 | | // Handcrafted notification response message, based on documentation specification |
2314 | | // identifier: 'A' |
2315 | | // length: 39 |
2316 | | // pid: 61 |
2317 | | // channel_name: test_channel |
2318 | | // payload: Test notification |
2319 | | let buf: &[u8] = &[ |
2320 | | 0x41, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3d, // test_channel |
2321 | | 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x00, |
2322 | | // Test notification |
2323 | | 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, |
2324 | | 0x69, 0x6f, 0x6e, 0x00, |
2325 | | ]; |
2326 | | |
2327 | | let ok_res = PgsqlBEMessage::NotificationResponse(NotificationResponse { |
2328 | | identifier: b'A', |
2329 | | length: 39, |
2330 | | pid: 61, |
2331 | | channel_name: br#"test_channel"#.to_vec(), |
2332 | | payload: br#"Test notification"#.to_vec(), |
2333 | | }); |
2334 | | |
2335 | | let (_rem, result) = pgsql_parse_response(buf).unwrap(); |
2336 | | |
2337 | | assert_eq!(ok_res, result); |
2338 | | } |
2339 | | } |