Line data Source code
1 : //! Postgres protocol messages serialization-deserialization. See
2 : //! <https://www.postgresql.org/docs/devel/protocol-message-formats.html>
3 : //! on message formats.
4 : #![deny(clippy::undocumented_unsafe_blocks)]
5 :
6 : pub mod framed;
7 :
8 : use byteorder::{BigEndian, ReadBytesExt};
9 : use bytes::{Buf, BufMut, Bytes, BytesMut};
10 : use itertools::Itertools;
11 : use serde::{Deserialize, Serialize};
12 : use std::{borrow::Cow, fmt, io, str};
13 :
14 : // re-export for use in utils pageserver_feedback.rs
15 : pub use postgres_protocol::PG_EPOCH;
16 :
17 : pub type Oid = u32;
18 : pub type SystemId = u64;
19 :
20 : pub const INT8_OID: Oid = 20;
21 : pub const INT4_OID: Oid = 23;
22 : pub const TEXT_OID: Oid = 25;
23 :
24 : #[derive(Debug)]
25 : pub enum FeMessage {
26 : // Simple query.
27 : Query(Bytes),
28 : // Extended query protocol.
29 : Parse(FeParseMessage),
30 : Describe(FeDescribeMessage),
31 : Bind(FeBindMessage),
32 : Execute(FeExecuteMessage),
33 : Close(FeCloseMessage),
34 : Sync,
35 : Terminate,
36 : CopyData(Bytes),
37 : CopyDone,
38 : CopyFail,
39 : PasswordMessage(Bytes),
40 : }
41 :
42 : #[derive(Clone, Copy, PartialEq, PartialOrd)]
43 : pub struct ProtocolVersion(u32);
44 :
45 : impl ProtocolVersion {
46 0 : pub const fn new(major: u16, minor: u16) -> Self {
47 0 : Self((major as u32) << 16 | minor as u32)
48 0 : }
49 0 : pub const fn minor(self) -> u16 {
50 0 : self.0 as u16
51 0 : }
52 24 : pub const fn major(self) -> u16 {
53 24 : (self.0 >> 16) as u16
54 24 : }
55 : }
56 :
57 : impl fmt::Debug for ProtocolVersion {
58 0 : fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 0 : f.debug_list()
60 0 : .entry(&self.major())
61 0 : .entry(&self.minor())
62 0 : .finish()
63 0 : }
64 : }
65 :
66 : #[derive(Debug)]
67 : pub enum FeStartupPacket {
68 : CancelRequest(CancelKeyData),
69 : SslRequest {
70 : direct: bool,
71 : },
72 : GssEncRequest,
73 : StartupMessage {
74 : version: ProtocolVersion,
75 : params: StartupMessageParams,
76 : },
77 : }
78 :
79 : #[derive(Debug, Clone, Default)]
80 : pub struct StartupMessageParamsBuilder {
81 : params: BytesMut,
82 : }
83 :
84 : impl StartupMessageParamsBuilder {
85 : /// Set parameter's value by its name.
86 : /// name and value must not contain a \0 byte
87 31 : pub fn insert(&mut self, name: &str, value: &str) {
88 31 : self.params.put(name.as_bytes());
89 31 : self.params.put(&b"\0"[..]);
90 31 : self.params.put(value.as_bytes());
91 31 : self.params.put(&b"\0"[..]);
92 31 : }
93 :
94 23 : pub fn freeze(self) -> StartupMessageParams {
95 23 : StartupMessageParams {
96 23 : params: self.params.freeze(),
97 23 : }
98 23 : }
99 : }
100 :
101 : #[derive(Debug, Clone, Default)]
102 : pub struct StartupMessageParams {
103 : params: Bytes,
104 : }
105 :
106 : impl StartupMessageParams {
107 : /// Get parameter's value by its name.
108 48 : pub fn get(&self, name: &str) -> Option<&str> {
109 64 : self.iter().find_map(|(k, v)| (k == name).then_some(v))
110 48 : }
111 :
112 : /// Split command-line options according to PostgreSQL's logic,
113 : /// taking into account all escape sequences but leaving them as-is.
114 : /// [`None`] means that there's no `options` in [`Self`].
115 30 : pub fn options_raw(&self) -> Option<impl Iterator<Item = &str>> {
116 30 : self.get("options").map(Self::parse_options_raw)
117 30 : }
118 :
119 : /// Split command-line options according to PostgreSQL's logic,
120 : /// applying all escape sequences (using owned strings as needed).
121 : /// [`None`] means that there's no `options` in [`Self`].
122 5 : pub fn options_escaped(&self) -> Option<impl Iterator<Item = Cow<'_, str>>> {
123 5 : self.get("options").map(Self::parse_options_escaped)
124 5 : }
125 :
126 : /// Split command-line options according to PostgreSQL's logic,
127 : /// taking into account all escape sequences but leaving them as-is.
128 30 : pub fn parse_options_raw(input: &str) -> impl Iterator<Item = &str> {
129 30 : // See `postgres: pg_split_opts`.
130 30 : let mut last_was_escape = false;
131 30 : input
132 564 : .split(move |c: char| {
133 : // We split by non-escaped whitespace symbols.
134 564 : let should_split = c.is_ascii_whitespace() && !last_was_escape;
135 564 : last_was_escape = c == '\\' && !last_was_escape;
136 564 : should_split
137 564 : })
138 76 : .filter(|s| !s.is_empty())
139 30 : }
140 :
141 : /// Split command-line options according to PostgreSQL's logic,
142 : /// applying all escape sequences (using owned strings as needed).
143 4 : pub fn parse_options_escaped(input: &str) -> impl Iterator<Item = Cow<'_, str>> {
144 4 : // See `postgres: pg_split_opts`.
145 7 : Self::parse_options_raw(input).map(|s| {
146 7 : let mut preserve_next_escape = false;
147 17 : let escape = |c| {
148 : // We should remove '\\' unless it's preceded by '\\'.
149 17 : let should_remove = c == '\\' && !preserve_next_escape;
150 17 : preserve_next_escape = should_remove;
151 17 : should_remove
152 17 : };
153 :
154 7 : match s.contains('\\') {
155 3 : true => Cow::Owned(s.replace(escape, "")),
156 4 : false => Cow::Borrowed(s),
157 : }
158 7 : })
159 4 : }
160 :
161 : /// Iterate through key-value pairs in an arbitrary order.
162 55 : pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
163 55 : let params =
164 55 : std::str::from_utf8(&self.params).expect("should be validated as utf8 already");
165 55 : params.split_terminator('\0').tuples()
166 55 : }
167 :
168 : // This function is mostly useful in tests.
169 : #[doc(hidden)]
170 23 : pub fn new<'a, const N: usize>(pairs: [(&'a str, &'a str); N]) -> Self {
171 23 : let mut b = StartupMessageParamsBuilder::default();
172 54 : for (k, v) in pairs {
173 31 : b.insert(k, v)
174 : }
175 23 : b.freeze()
176 23 : }
177 : }
178 :
179 6 : #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
180 : pub struct CancelKeyData {
181 : pub backend_pid: i32,
182 : pub cancel_key: i32,
183 : }
184 :
185 : impl fmt::Display for CancelKeyData {
186 0 : fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 0 : // TODO: this is producing strange results, with 0xffffffff........ always in the logs.
188 0 : let hi = (self.backend_pid as u64) << 32;
189 0 : let lo = self.cancel_key as u64;
190 0 : let id = hi | lo;
191 0 :
192 0 : // This format is more compact and might work better for logs.
193 0 : f.debug_tuple("CancelKeyData")
194 0 : .field(&format_args!("{:x}", id))
195 0 : .finish()
196 0 : }
197 : }
198 :
199 : use rand::distributions::{Distribution, Standard};
200 : impl Distribution<CancelKeyData> for Standard {
201 1 : fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> CancelKeyData {
202 1 : CancelKeyData {
203 1 : backend_pid: rng.gen(),
204 1 : cancel_key: rng.gen(),
205 1 : }
206 1 : }
207 : }
208 :
209 : // We only support the simple case of Parse on unnamed prepared statement and
210 : // no params
211 : #[derive(Debug)]
212 : pub struct FeParseMessage {
213 : pub query_string: Bytes,
214 : }
215 :
216 : #[derive(Debug)]
217 : pub struct FeDescribeMessage {
218 : pub kind: u8, // 'S' to describe a prepared statement; or 'P' to describe a portal.
219 : // we only support unnamed prepared stmt or portal
220 : }
221 :
222 : // we only support unnamed prepared stmt and portal
223 : #[derive(Debug)]
224 : pub struct FeBindMessage;
225 :
226 : // we only support unnamed prepared stmt or portal
227 : #[derive(Debug)]
228 : pub struct FeExecuteMessage {
229 : /// max # of rows
230 : pub maxrows: i32,
231 : }
232 :
233 : // we only support unnamed prepared stmt and portal
234 : #[derive(Debug)]
235 : pub struct FeCloseMessage;
236 :
237 : /// An error occurred while parsing or serializing raw stream into Postgres
238 : /// messages.
239 0 : #[derive(thiserror::Error, Debug)]
240 : pub enum ProtocolError {
241 : /// Invalid packet was received from the client (e.g. unexpected message
242 : /// type or broken len).
243 : #[error("Protocol error: {0}")]
244 : Protocol(String),
245 : /// Failed to parse or, (unlikely), serialize a protocol message.
246 : #[error("Message parse error: {0}")]
247 : BadMessage(String),
248 : }
249 :
250 : impl ProtocolError {
251 : /// Proxy stream.rs uses only io::Error; provide it.
252 0 : pub fn into_io_error(self) -> io::Error {
253 0 : io::Error::new(io::ErrorKind::Other, self.to_string())
254 0 : }
255 : }
256 :
257 : impl FeMessage {
258 : /// Read and parse one message from the `buf` input buffer. If there is at
259 : /// least one valid message, returns it, advancing `buf`; redundant copies
260 : /// are avoided, as thanks to `bytes` crate ptrs in parsed message point
261 : /// directly into the `buf` (processed data is garbage collected after
262 : /// parsed message is dropped).
263 : ///
264 : /// Returns None if `buf` doesn't contain enough data for a single message.
265 : /// For efficiency, tries to reserve large enough space in `buf` for the
266 : /// next message in this case to save the repeated calls.
267 : ///
268 : /// Returns Error if message is malformed, the only possible ErrorKind is
269 : /// InvalidInput.
270 : //
271 : // Inspired by rust-postgres Message::parse.
272 57 : pub fn parse(buf: &mut BytesMut) -> Result<Option<FeMessage>, ProtocolError> {
273 57 : // Every message contains message type byte and 4 bytes len; can't do
274 57 : // much without them.
275 57 : if buf.len() < 5 {
276 30 : let to_read = 5 - buf.len();
277 30 : buf.reserve(to_read);
278 30 : return Ok(None);
279 27 : }
280 27 :
281 27 : // We shouldn't advance `buf` as probably full message is not there yet,
282 27 : // so can't directly use Bytes::get_u32 etc.
283 27 : let tag = buf[0];
284 27 : let len = (&buf[1..5]).read_u32::<BigEndian>().unwrap();
285 27 : if len < 4 {
286 0 : return Err(ProtocolError::Protocol(format!(
287 0 : "invalid message length {}",
288 0 : len
289 0 : )));
290 27 : }
291 27 :
292 27 : // length field includes itself, but not message type.
293 27 : let total_len = len as usize + 1;
294 27 : if buf.len() < total_len {
295 : // Don't have full message yet.
296 0 : let to_read = total_len - buf.len();
297 0 : buf.reserve(to_read);
298 0 : return Ok(None);
299 27 : }
300 27 :
301 27 : // got the message, advance buffer
302 27 : let mut msg = buf.split_to(total_len).freeze();
303 27 : msg.advance(5); // consume message type and len
304 27 :
305 27 : match tag {
306 2 : b'Q' => Ok(Some(FeMessage::Query(msg))),
307 0 : b'P' => Ok(Some(FeParseMessage::parse(msg)?)),
308 0 : b'D' => Ok(Some(FeDescribeMessage::parse(msg)?)),
309 0 : b'E' => Ok(Some(FeExecuteMessage::parse(msg)?)),
310 0 : b'B' => Ok(Some(FeBindMessage::parse(msg)?)),
311 0 : b'C' => Ok(Some(FeCloseMessage::parse(msg)?)),
312 0 : b'S' => Ok(Some(FeMessage::Sync)),
313 0 : b'X' => Ok(Some(FeMessage::Terminate)),
314 0 : b'd' => Ok(Some(FeMessage::CopyData(msg))),
315 0 : b'c' => Ok(Some(FeMessage::CopyDone)),
316 0 : b'f' => Ok(Some(FeMessage::CopyFail)),
317 25 : b'p' => Ok(Some(FeMessage::PasswordMessage(msg))),
318 0 : tag => Err(ProtocolError::Protocol(format!(
319 0 : "unknown message tag: {tag},'{msg:?}'"
320 0 : ))),
321 : }
322 57 : }
323 : }
324 :
325 : impl FeStartupPacket {
326 : /// Read and parse startup message from the `buf` input buffer. It is
327 : /// different from [`FeMessage::parse`] because startup messages don't have
328 : /// message type byte; otherwise, its comments apply.
329 91 : pub fn parse(buf: &mut BytesMut) -> Result<Option<FeStartupPacket>, ProtocolError> {
330 : /// <https://github.com/postgres/postgres/blob/ca481d3c9ab7bf69ff0c8d71ad3951d407f6a33c/src/include/libpq/pqcomm.h#L118>
331 : const MAX_STARTUP_PACKET_LENGTH: usize = 10000;
332 : const RESERVED_INVALID_MAJOR_VERSION: u16 = 1234;
333 : /// <https://github.com/postgres/postgres/blob/ca481d3c9ab7bf69ff0c8d71ad3951d407f6a33c/src/include/libpq/pqcomm.h#L132>
334 : const CANCEL_REQUEST_CODE: ProtocolVersion = ProtocolVersion::new(1234, 5678);
335 : /// <https://github.com/postgres/postgres/blob/ca481d3c9ab7bf69ff0c8d71ad3951d407f6a33c/src/include/libpq/pqcomm.h#L166>
336 : const NEGOTIATE_SSL_CODE: ProtocolVersion = ProtocolVersion::new(1234, 5679);
337 : /// <https://github.com/postgres/postgres/blob/ca481d3c9ab7bf69ff0c8d71ad3951d407f6a33c/src/include/libpq/pqcomm.h#L167>
338 : const NEGOTIATE_GSS_CODE: ProtocolVersion = ProtocolVersion::new(1234, 5680);
339 :
340 : // <https://github.com/postgres/postgres/blob/04bcf9e19a4261fe9c7df37c777592c2e10c32a7/src/backend/tcop/backend_startup.c#L378-L382>
341 : // First byte indicates standard SSL handshake message
342 : // (It can't be a Postgres startup length because in network byte order
343 : // that would be a startup packet hundreds of megabytes long)
344 91 : if buf.first() == Some(&0x16) {
345 0 : return Ok(Some(FeStartupPacket::SslRequest { direct: true }));
346 91 : }
347 91 :
348 91 : // need at least 4 bytes with packet len
349 91 : if buf.len() < 4 {
350 45 : let to_read = 4 - buf.len();
351 45 : buf.reserve(to_read);
352 45 : return Ok(None);
353 46 : }
354 46 :
355 46 : // We shouldn't advance `buf` as probably full message is not there yet,
356 46 : // so can't directly use Bytes::get_u32 etc.
357 46 : let len = (&buf[0..4]).read_u32::<BigEndian>().unwrap() as usize;
358 46 : // The proposed replacement is `!(8..=MAX_STARTUP_PACKET_LENGTH).contains(&len)`
359 46 : // which is less readable
360 46 : #[allow(clippy::manual_range_contains)]
361 46 : if len < 8 || len > MAX_STARTUP_PACKET_LENGTH {
362 1 : return Err(ProtocolError::Protocol(format!(
363 1 : "invalid startup packet message length {}",
364 1 : len
365 1 : )));
366 45 : }
367 45 :
368 45 : if buf.len() < len {
369 : // Don't have full message yet.
370 0 : let to_read = len - buf.len();
371 0 : buf.reserve(to_read);
372 0 : return Ok(None);
373 45 : }
374 45 :
375 45 : // got the message, advance buffer
376 45 : let mut msg = buf.split_to(len).freeze();
377 45 : msg.advance(4); // consume len
378 45 :
379 45 : let request_code = ProtocolVersion(msg.get_u32());
380 : // StartupMessage, CancelRequest, SSLRequest etc are differentiated by request code.
381 45 : let message = match request_code {
382 : CANCEL_REQUEST_CODE => {
383 0 : if msg.remaining() != 8 {
384 0 : return Err(ProtocolError::BadMessage(
385 0 : "CancelRequest message is malformed, backend PID / secret key missing"
386 0 : .to_owned(),
387 0 : ));
388 0 : }
389 0 : FeStartupPacket::CancelRequest(CancelKeyData {
390 0 : backend_pid: msg.get_i32(),
391 0 : cancel_key: msg.get_i32(),
392 0 : })
393 : }
394 : NEGOTIATE_SSL_CODE => {
395 : // Requested upgrade to SSL (aka TLS)
396 21 : FeStartupPacket::SslRequest { direct: false }
397 : }
398 : NEGOTIATE_GSS_CODE => {
399 : // Requested upgrade to GSSAPI
400 0 : FeStartupPacket::GssEncRequest
401 : }
402 24 : version if version.major() == RESERVED_INVALID_MAJOR_VERSION => {
403 0 : return Err(ProtocolError::Protocol(format!(
404 0 : "Unrecognized request code {}",
405 0 : version.minor()
406 0 : )));
407 : }
408 : // TODO bail if protocol major_version is not 3?
409 24 : version => {
410 : // StartupMessage
411 :
412 24 : let s = str::from_utf8(&msg).map_err(|_e| {
413 0 : ProtocolError::BadMessage("StartupMessage params: invalid utf-8".to_owned())
414 24 : })?;
415 24 : let s = s.strip_suffix('\0').ok_or_else(|| {
416 0 : ProtocolError::Protocol(
417 0 : "StartupMessage params: missing null terminator".to_string(),
418 0 : )
419 24 : })?;
420 :
421 24 : FeStartupPacket::StartupMessage {
422 24 : version,
423 24 : params: StartupMessageParams {
424 24 : params: msg.slice_ref(s.as_bytes()),
425 24 : },
426 24 : }
427 : }
428 : };
429 45 : Ok(Some(message))
430 91 : }
431 : }
432 :
433 : impl FeParseMessage {
434 0 : fn parse(mut buf: Bytes) -> Result<FeMessage, ProtocolError> {
435 : // FIXME: the rust-postgres driver uses a named prepared statement
436 : // for copy_out(). We're not prepared to handle that correctly. For
437 : // now, just ignore the statement name, assuming that the client never
438 : // uses more than one prepared statement at a time.
439 :
440 0 : let _pstmt_name = read_cstr(&mut buf)?;
441 0 : let query_string = read_cstr(&mut buf)?;
442 0 : if buf.remaining() < 2 {
443 0 : return Err(ProtocolError::BadMessage(
444 0 : "Parse message is malformed, nparams missing".to_string(),
445 0 : ));
446 0 : }
447 0 : let nparams = buf.get_i16();
448 0 :
449 0 : if nparams != 0 {
450 0 : return Err(ProtocolError::BadMessage(
451 0 : "query params not implemented".to_string(),
452 0 : ));
453 0 : }
454 0 :
455 0 : Ok(FeMessage::Parse(FeParseMessage { query_string }))
456 0 : }
457 : }
458 :
459 : impl FeDescribeMessage {
460 0 : fn parse(mut buf: Bytes) -> Result<FeMessage, ProtocolError> {
461 0 : let kind = buf.get_u8();
462 0 : let _pstmt_name = read_cstr(&mut buf)?;
463 :
464 : // FIXME: see FeParseMessage::parse
465 0 : if kind != b'S' {
466 0 : return Err(ProtocolError::BadMessage(
467 0 : "only prepared statemement Describe is implemented".to_string(),
468 0 : ));
469 0 : }
470 0 :
471 0 : Ok(FeMessage::Describe(FeDescribeMessage { kind }))
472 0 : }
473 : }
474 :
475 : impl FeExecuteMessage {
476 0 : fn parse(mut buf: Bytes) -> Result<FeMessage, ProtocolError> {
477 0 : let portal_name = read_cstr(&mut buf)?;
478 0 : if buf.remaining() < 4 {
479 0 : return Err(ProtocolError::BadMessage(
480 0 : "FeExecuteMessage message is malformed, maxrows missing".to_string(),
481 0 : ));
482 0 : }
483 0 : let maxrows = buf.get_i32();
484 0 :
485 0 : if !portal_name.is_empty() {
486 0 : return Err(ProtocolError::BadMessage(
487 0 : "named portals not implemented".to_string(),
488 0 : ));
489 0 : }
490 0 : if maxrows != 0 {
491 0 : return Err(ProtocolError::BadMessage(
492 0 : "row limit in Execute message not implemented".to_string(),
493 0 : ));
494 0 : }
495 0 :
496 0 : Ok(FeMessage::Execute(FeExecuteMessage { maxrows }))
497 0 : }
498 : }
499 :
500 : impl FeBindMessage {
501 0 : fn parse(mut buf: Bytes) -> Result<FeMessage, ProtocolError> {
502 0 : let portal_name = read_cstr(&mut buf)?;
503 0 : let _pstmt_name = read_cstr(&mut buf)?;
504 :
505 : // FIXME: see FeParseMessage::parse
506 0 : if !portal_name.is_empty() {
507 0 : return Err(ProtocolError::BadMessage(
508 0 : "named portals not implemented".to_string(),
509 0 : ));
510 0 : }
511 0 :
512 0 : Ok(FeMessage::Bind(FeBindMessage))
513 0 : }
514 : }
515 :
516 : impl FeCloseMessage {
517 0 : fn parse(mut buf: Bytes) -> Result<FeMessage, ProtocolError> {
518 0 : let _kind = buf.get_u8();
519 0 : let _pstmt_or_portal_name = read_cstr(&mut buf)?;
520 :
521 : // FIXME: we do nothing with Close
522 0 : Ok(FeMessage::Close(FeCloseMessage))
523 0 : }
524 : }
525 :
526 : // Backend
527 :
528 : #[derive(Debug)]
529 : pub enum BeMessage<'a> {
530 : AuthenticationOk,
531 : AuthenticationMD5Password([u8; 4]),
532 : AuthenticationSasl(BeAuthenticationSaslMessage<'a>),
533 : AuthenticationCleartextPassword,
534 : BackendKeyData(CancelKeyData),
535 : BindComplete,
536 : CommandComplete(&'a [u8]),
537 : CopyData(&'a [u8]),
538 : CopyDone,
539 : CopyFail,
540 : CopyInResponse,
541 : CopyOutResponse,
542 : CopyBothResponse,
543 : CloseComplete,
544 : // None means column is NULL
545 : DataRow(&'a [Option<&'a [u8]>]),
546 : // None errcode means internal_error will be sent.
547 : ErrorResponse(&'a str, Option<&'a [u8; 5]>),
548 : /// Single byte - used in response to SSLRequest/GSSENCRequest.
549 : EncryptionResponse(bool),
550 : NoData,
551 : ParameterDescription,
552 : ParameterStatus {
553 : name: &'a [u8],
554 : value: &'a [u8],
555 : },
556 : ParseComplete,
557 : ReadyForQuery,
558 : RowDescription(&'a [RowDescriptor<'a>]),
559 : XLogData(XLogDataBody<'a>),
560 : NoticeResponse(&'a str),
561 : NegotiateProtocolVersion {
562 : version: ProtocolVersion,
563 : options: &'a [&'a str],
564 : },
565 : KeepAlive(WalSndKeepAlive),
566 : }
567 :
568 : /// Common shorthands.
569 : impl<'a> BeMessage<'a> {
570 : /// A [`BeMessage::ParameterStatus`] holding the client encoding, i.e. UTF-8.
571 : /// This is a sensible default, given that:
572 : /// * rust strings only support this encoding out of the box.
573 : /// * tokio-postgres, postgres-jdbc (and probably more) mandate it.
574 : ///
575 : /// TODO: do we need to report `server_encoding` as well?
576 : pub const CLIENT_ENCODING: Self = Self::ParameterStatus {
577 : name: b"client_encoding",
578 : value: b"UTF8",
579 : };
580 :
581 : pub const INTEGER_DATETIMES: Self = Self::ParameterStatus {
582 : name: b"integer_datetimes",
583 : value: b"on",
584 : };
585 :
586 : /// Build a [`BeMessage::ParameterStatus`] holding the server version.
587 2 : pub fn server_version(version: &'a str) -> Self {
588 2 : Self::ParameterStatus {
589 2 : name: b"server_version",
590 2 : value: version.as_bytes(),
591 2 : }
592 2 : }
593 : }
594 :
595 : #[derive(Debug)]
596 : pub enum BeAuthenticationSaslMessage<'a> {
597 : Methods(&'a [&'a str]),
598 : Continue(&'a [u8]),
599 : Final(&'a [u8]),
600 : }
601 :
602 : #[derive(Debug)]
603 : pub enum BeParameterStatusMessage<'a> {
604 : Encoding(&'a str),
605 : ServerVersion(&'a str),
606 : }
607 :
608 : // One row description in RowDescription packet.
609 : #[derive(Debug)]
610 : pub struct RowDescriptor<'a> {
611 : pub name: &'a [u8],
612 : pub tableoid: Oid,
613 : pub attnum: i16,
614 : pub typoid: Oid,
615 : pub typlen: i16,
616 : pub typmod: i32,
617 : pub formatcode: i16,
618 : }
619 :
620 : impl Default for RowDescriptor<'_> {
621 0 : fn default() -> RowDescriptor<'static> {
622 0 : RowDescriptor {
623 0 : name: b"",
624 0 : tableoid: 0,
625 0 : attnum: 0,
626 0 : typoid: 0,
627 0 : typlen: 0,
628 0 : typmod: 0,
629 0 : formatcode: 0,
630 0 : }
631 0 : }
632 : }
633 :
634 : impl RowDescriptor<'_> {
635 : /// Convenience function to create a RowDescriptor message for an int8 column
636 0 : pub const fn int8_col(name: &[u8]) -> RowDescriptor {
637 0 : RowDescriptor {
638 0 : name,
639 0 : tableoid: 0,
640 0 : attnum: 0,
641 0 : typoid: INT8_OID,
642 0 : typlen: 8,
643 0 : typmod: 0,
644 0 : formatcode: 0,
645 0 : }
646 0 : }
647 :
648 2 : pub const fn text_col(name: &[u8]) -> RowDescriptor {
649 2 : RowDescriptor {
650 2 : name,
651 2 : tableoid: 0,
652 2 : attnum: 0,
653 2 : typoid: TEXT_OID,
654 2 : typlen: -1,
655 2 : typmod: 0,
656 2 : formatcode: 0,
657 2 : }
658 2 : }
659 : }
660 :
661 : #[derive(Debug)]
662 : pub struct XLogDataBody<'a> {
663 : pub wal_start: u64,
664 : pub wal_end: u64, // current end of WAL on the server
665 : pub timestamp: i64,
666 : pub data: &'a [u8],
667 : }
668 :
669 : #[derive(Debug)]
670 : pub struct WalSndKeepAlive {
671 : pub wal_end: u64, // current end of WAL on the server
672 : pub timestamp: i64,
673 : pub request_reply: bool,
674 : }
675 :
676 : pub static HELLO_WORLD_ROW: BeMessage = BeMessage::DataRow(&[Some(b"hello world")]);
677 :
678 : // single text column
679 : pub static SINGLE_COL_ROWDESC: BeMessage = BeMessage::RowDescription(&[RowDescriptor {
680 : name: b"data",
681 : tableoid: 0,
682 : attnum: 0,
683 : typoid: TEXT_OID,
684 : typlen: -1,
685 : typmod: 0,
686 : formatcode: 0,
687 : }]);
688 :
689 : /// Call f() to write body of the message and prepend it with 4-byte len as
690 : /// prescribed by the protocol.
691 75 : fn write_body<R>(buf: &mut BytesMut, f: impl FnOnce(&mut BytesMut) -> R) -> R {
692 75 : let base = buf.len();
693 75 : buf.extend_from_slice(&[0; 4]);
694 75 :
695 75 : let res = f(buf);
696 75 :
697 75 : let size = i32::try_from(buf.len() - base).expect("message too big to transmit");
698 75 : (&mut buf[base..]).put_slice(&size.to_be_bytes());
699 75 :
700 75 : res
701 75 : }
702 :
703 : /// Safe write of s into buf as cstring (String in the protocol).
704 56 : fn write_cstr(s: impl AsRef<[u8]>, buf: &mut BytesMut) -> Result<(), ProtocolError> {
705 56 : let bytes = s.as_ref();
706 56 : if bytes.contains(&0) {
707 0 : return Err(ProtocolError::BadMessage(
708 0 : "string contains embedded null".to_owned(),
709 0 : ));
710 56 : }
711 56 : buf.put_slice(bytes);
712 56 : buf.put_u8(0);
713 56 : Ok(())
714 56 : }
715 :
716 : /// Read cstring from buf, advancing it.
717 11 : pub fn read_cstr(buf: &mut Bytes) -> Result<Bytes, ProtocolError> {
718 11 : let pos = buf
719 11 : .iter()
720 156 : .position(|x| *x == 0)
721 11 : .ok_or_else(|| ProtocolError::BadMessage("missing cstring terminator".to_owned()))?;
722 11 : let result = buf.split_to(pos);
723 11 : buf.advance(1); // drop the null terminator
724 11 : Ok(result)
725 11 : }
726 :
727 : pub const SQLSTATE_INTERNAL_ERROR: &[u8; 5] = b"XX000";
728 : pub const SQLSTATE_ADMIN_SHUTDOWN: &[u8; 5] = b"57P01";
729 : pub const SQLSTATE_SUCCESSFUL_COMPLETION: &[u8; 5] = b"00000";
730 :
731 : impl BeMessage<'_> {
732 : /// Serialize `message` to the given `buf`.
733 : /// Apart from smart memory managemet, BytesMut is good here as msg len
734 : /// precedes its body and it is handy to write it down first and then fill
735 : /// the length. With Write we would have to either calc it manually or have
736 : /// one more buffer.
737 96 : pub fn write(buf: &mut BytesMut, message: &BeMessage) -> Result<(), ProtocolError> {
738 96 : match message {
739 12 : BeMessage::AuthenticationOk => {
740 12 : buf.put_u8(b'R');
741 12 : write_body(buf, |buf| {
742 12 : buf.put_i32(0); // Specifies that the authentication was successful.
743 12 : });
744 12 : }
745 :
746 2 : BeMessage::AuthenticationCleartextPassword => {
747 2 : buf.put_u8(b'R');
748 2 : write_body(buf, |buf| {
749 2 : buf.put_i32(3); // Specifies that clear text password is required.
750 2 : });
751 2 : }
752 :
753 0 : BeMessage::AuthenticationMD5Password(salt) => {
754 0 : buf.put_u8(b'R');
755 0 : write_body(buf, |buf| {
756 0 : buf.put_i32(5); // Specifies that an MD5-encrypted password is required.
757 0 : buf.put_slice(&salt[..]);
758 0 : });
759 0 : }
760 :
761 30 : BeMessage::AuthenticationSasl(msg) => {
762 30 : buf.put_u8(b'R');
763 30 : write_body(buf, |buf| {
764 : use BeAuthenticationSaslMessage::*;
765 30 : match msg {
766 13 : Methods(methods) => {
767 13 : buf.put_i32(10); // Specifies that SASL auth method is used.
768 25 : for method in methods.iter() {
769 25 : write_cstr(method, buf)?;
770 : }
771 13 : buf.put_u8(0); // zero terminator for the list
772 : }
773 11 : Continue(extra) => {
774 11 : buf.put_i32(11); // Continue SASL auth.
775 11 : buf.put_slice(extra);
776 11 : }
777 6 : Final(extra) => {
778 6 : buf.put_i32(12); // Send final SASL message.
779 6 : buf.put_slice(extra);
780 6 : }
781 : }
782 30 : Ok(())
783 30 : })?;
784 : }
785 :
786 0 : BeMessage::BackendKeyData(key_data) => {
787 0 : buf.put_u8(b'K');
788 0 : write_body(buf, |buf| {
789 0 : buf.put_i32(key_data.backend_pid);
790 0 : buf.put_i32(key_data.cancel_key);
791 0 : });
792 0 : }
793 :
794 0 : BeMessage::BindComplete => {
795 0 : buf.put_u8(b'2');
796 0 : write_body(buf, |_| {});
797 0 : }
798 :
799 0 : BeMessage::CloseComplete => {
800 0 : buf.put_u8(b'3');
801 0 : write_body(buf, |_| {});
802 0 : }
803 :
804 2 : BeMessage::CommandComplete(cmd) => {
805 2 : buf.put_u8(b'C');
806 2 : write_body(buf, |buf| write_cstr(cmd, buf))?;
807 : }
808 :
809 0 : BeMessage::CopyData(data) => {
810 0 : buf.put_u8(b'd');
811 0 : write_body(buf, |buf| {
812 0 : buf.put_slice(data);
813 0 : });
814 0 : }
815 :
816 0 : BeMessage::CopyDone => {
817 0 : buf.put_u8(b'c');
818 0 : write_body(buf, |_| {});
819 0 : }
820 :
821 0 : BeMessage::CopyFail => {
822 0 : buf.put_u8(b'f');
823 0 : write_body(buf, |_| {});
824 0 : }
825 :
826 0 : BeMessage::CopyInResponse => {
827 0 : buf.put_u8(b'G');
828 0 : write_body(buf, |buf| {
829 0 : buf.put_u8(1); // copy_is_binary
830 0 : buf.put_i16(0); // numAttributes
831 0 : });
832 0 : }
833 :
834 0 : BeMessage::CopyOutResponse => {
835 0 : buf.put_u8(b'H');
836 0 : write_body(buf, |buf| {
837 0 : buf.put_u8(0); // copy_is_binary
838 0 : buf.put_i16(0); // numAttributes
839 0 : });
840 0 : }
841 :
842 0 : BeMessage::CopyBothResponse => {
843 0 : buf.put_u8(b'W');
844 0 : write_body(buf, |buf| {
845 0 : // doesn't matter, used only for replication
846 0 : buf.put_u8(0); // copy_is_binary
847 0 : buf.put_i16(0); // numAttributes
848 0 : });
849 0 : }
850 :
851 2 : BeMessage::DataRow(vals) => {
852 2 : buf.put_u8(b'D');
853 2 : write_body(buf, |buf| {
854 2 : buf.put_u16(vals.len() as u16); // num of cols
855 2 : for val_opt in vals.iter() {
856 2 : if let Some(val) = val_opt {
857 2 : buf.put_u32(val.len() as u32);
858 2 : buf.put_slice(val);
859 2 : } else {
860 0 : buf.put_i32(-1);
861 0 : }
862 : }
863 2 : });
864 2 : }
865 :
866 : // ErrorResponse is a zero-terminated array of zero-terminated fields.
867 : // First byte of each field represents type of this field. Set just enough fields
868 : // to satisfy rust-postgres client: 'S' -- severity, 'C' -- error, 'M' -- error
869 : // message text.
870 1 : BeMessage::ErrorResponse(error_msg, pg_error_code) => {
871 1 : // 'E' signalizes ErrorResponse messages
872 1 : buf.put_u8(b'E');
873 1 : write_body(buf, |buf| {
874 1 : buf.put_u8(b'S'); // severity
875 1 : buf.put_slice(b"ERROR\0");
876 1 :
877 1 : buf.put_u8(b'C'); // SQLSTATE error code
878 1 : buf.put_slice(&terminate_code(
879 1 : pg_error_code.unwrap_or(SQLSTATE_INTERNAL_ERROR),
880 1 : ));
881 1 :
882 1 : buf.put_u8(b'M'); // the message
883 1 : write_cstr(error_msg, buf)?;
884 :
885 1 : buf.put_u8(0); // terminator
886 1 : Ok(())
887 1 : })?;
888 : }
889 :
890 : // NoticeResponse has the same format as ErrorResponse. From doc: "The frontend should display the
891 : // message but continue listening for ReadyForQuery or ErrorResponse"
892 0 : BeMessage::NoticeResponse(error_msg) => {
893 0 : // For all the errors set Severity to Error and error code to
894 0 : // 'internal error'.
895 0 :
896 0 : // 'N' signalizes NoticeResponse messages
897 0 : buf.put_u8(b'N');
898 0 : write_body(buf, |buf| {
899 0 : buf.put_u8(b'S'); // severity
900 0 : buf.put_slice(b"NOTICE\0");
901 0 :
902 0 : buf.put_u8(b'C'); // SQLSTATE error code
903 0 : buf.put_slice(&terminate_code(SQLSTATE_INTERNAL_ERROR));
904 0 :
905 0 : buf.put_u8(b'M'); // the message
906 0 : write_cstr(error_msg.as_bytes(), buf)?;
907 :
908 0 : buf.put_u8(0); // terminator
909 0 : Ok(())
910 0 : })?;
911 : }
912 :
913 0 : BeMessage::NoData => {
914 0 : buf.put_u8(b'n');
915 0 : write_body(buf, |_| {});
916 0 : }
917 :
918 21 : BeMessage::EncryptionResponse(should_negotiate) => {
919 21 : let response = if *should_negotiate { b'S' } else { b'N' };
920 21 : buf.put_u8(response);
921 : }
922 :
923 13 : BeMessage::ParameterStatus { name, value } => {
924 13 : buf.put_u8(b'S');
925 13 : write_body(buf, |buf| {
926 13 : write_cstr(name, buf)?;
927 13 : write_cstr(value, buf)
928 13 : })?;
929 : }
930 :
931 0 : BeMessage::ParameterDescription => {
932 0 : buf.put_u8(b't');
933 0 : write_body(buf, |buf| {
934 0 : // we don't support params, so always 0
935 0 : buf.put_i16(0);
936 0 : });
937 0 : }
938 :
939 0 : BeMessage::ParseComplete => {
940 0 : buf.put_u8(b'1');
941 0 : write_body(buf, |_| {});
942 0 : }
943 :
944 11 : BeMessage::ReadyForQuery => {
945 11 : buf.put_u8(b'Z');
946 11 : write_body(buf, |buf| {
947 11 : buf.put_u8(b'I');
948 11 : });
949 11 : }
950 :
951 2 : BeMessage::RowDescription(rows) => {
952 2 : buf.put_u8(b'T');
953 2 : write_body(buf, |buf| {
954 2 : buf.put_i16(rows.len() as i16); // # of fields
955 2 : for row in rows.iter() {
956 2 : write_cstr(row.name, buf)?;
957 2 : buf.put_i32(0); /* table oid */
958 2 : buf.put_i16(0); /* attnum */
959 2 : buf.put_u32(row.typoid);
960 2 : buf.put_i16(row.typlen);
961 2 : buf.put_i32(-1); /* typmod */
962 2 : buf.put_i16(0); /* format code */
963 : }
964 2 : Ok(())
965 2 : })?;
966 : }
967 :
968 0 : BeMessage::XLogData(body) => {
969 0 : buf.put_u8(b'd');
970 0 : write_body(buf, |buf| {
971 0 : buf.put_u8(b'w');
972 0 : buf.put_u64(body.wal_start);
973 0 : buf.put_u64(body.wal_end);
974 0 : buf.put_i64(body.timestamp);
975 0 : buf.put_slice(body.data);
976 0 : });
977 0 : }
978 :
979 0 : BeMessage::KeepAlive(req) => {
980 0 : buf.put_u8(b'd');
981 0 : write_body(buf, |buf| {
982 0 : buf.put_u8(b'k');
983 0 : buf.put_u64(req.wal_end);
984 0 : buf.put_i64(req.timestamp);
985 0 : buf.put_u8(u8::from(req.request_reply));
986 0 : });
987 0 : }
988 :
989 0 : BeMessage::NegotiateProtocolVersion { version, options } => {
990 0 : buf.put_u8(b'v');
991 0 : write_body(buf, |buf| {
992 0 : buf.put_u32(version.0);
993 0 : buf.put_u32(options.len() as u32);
994 0 : for option in options.iter() {
995 0 : write_cstr(option, buf)?;
996 : }
997 0 : Ok(())
998 0 : })?
999 : }
1000 : }
1001 96 : Ok(())
1002 96 : }
1003 : }
1004 :
1005 1 : fn terminate_code(code: &[u8; 5]) -> [u8; 6] {
1006 1 : let mut terminated = [0; 6];
1007 5 : for (i, &elem) in code.iter().enumerate() {
1008 5 : terminated[i] = elem;
1009 5 : }
1010 :
1011 1 : terminated
1012 1 : }
1013 :
1014 : #[cfg(test)]
1015 : mod tests {
1016 : use super::*;
1017 :
1018 : #[test]
1019 1 : fn test_startup_message_params_options_escaped() {
1020 4 : fn split_options(params: &StartupMessageParams) -> Vec<Cow<'_, str>> {
1021 4 : params
1022 4 : .options_escaped()
1023 4 : .expect("options are None")
1024 4 : .collect()
1025 4 : }
1026 :
1027 4 : let make_params = |options| StartupMessageParams::new([("options", options)]);
1028 :
1029 1 : let params = StartupMessageParams::new([]);
1030 1 : assert!(params.options_escaped().is_none());
1031 :
1032 1 : let params = make_params("");
1033 1 : assert!(split_options(¶ms).is_empty());
1034 :
1035 1 : let params = make_params("foo");
1036 1 : assert_eq!(split_options(¶ms), ["foo"]);
1037 :
1038 1 : let params = make_params(" foo bar ");
1039 1 : assert_eq!(split_options(¶ms), ["foo", "bar"]);
1040 :
1041 1 : let params = make_params("foo\\ bar \\ \\\\ baz\\ lol");
1042 1 : assert_eq!(split_options(¶ms), ["foo bar", " \\", "baz ", "lol"]);
1043 1 : }
1044 :
1045 : #[test]
1046 1 : fn parse_fe_startup_packet_regression() {
1047 1 : let data = [0, 0, 0, 7, 0, 0, 0, 0];
1048 1 : FeStartupPacket::parse(&mut BytesMut::from_iter(data)).unwrap_err();
1049 1 : }
1050 : }
|