Coverage Report

Created: 2026-03-31 07:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/header/PostgresLayer.h
Line
Count
Source
1
#pragma once
2
3
#include "Layer.h"
4
#include "PointerVector.h"
5
#include <memory>
6
#include <ostream>
7
#include <unordered_map>
8
9
/// @file
10
11
/// @namespace pcpp
12
/// @brief The main namespace for the PcapPlusPlus lib
13
namespace pcpp
14
{
15
16
  /// @enum PostgresMessageOrigin
17
  /// Indicates whether the message is from the frontend (client) or backend (server)
18
  enum class PostgresMessageOrigin
19
  {
20
    Frontend,
21
    Backend
22
  };
23
24
  /// @class PostgresMessageType
25
  /// Represents PostgreSQL message types (both frontend and backend)
26
  class PostgresMessageType
27
  {
28
  public:
29
    /// Define enum types for all PostgreSQL message types
30
    enum Value : uint8_t
31
    {
32
      // Frontend (client) message types
33
34
      /// Startup message (first message in connection)
35
      Frontend_StartupMessage,
36
      /// SSL request code (sent by client to request SSL)
37
      Frontend_SSLRequest,
38
      /// Cancel request (sent by client to cancel a running query)
39
      Frontend_CancelRequest,
40
      /// GSSAPI encryption request
41
      Frontend_GSSENCRequest,
42
      /// Simple query message
43
      Frontend_Query,
44
      /// Parse message (prepared statement)
45
      Frontend_Parse,
46
      /// Bind message (portal binding)
47
      Frontend_Bind,
48
      /// Execute message (portal execution)
49
      Frontend_Execute,
50
      /// Close message (close a prepared statement or portal)
51
      Frontend_Close,
52
      /// Describe message (describe a prepared statement or portal)
53
      Frontend_Describe,
54
      /// Function call message
55
      Frontend_FunctionCall,
56
      /// Flush message
57
      Frontend_Flush,
58
      /// Sync message (sync after batch)
59
      Frontend_Sync,
60
      /// Copy data message (during COPY)
61
      Frontend_CopyData,
62
      /// Copy done message (during COPY)
63
      Frontend_CopyDone,
64
      /// Copy fail message (during COPY)
65
      Frontend_CopyFail,
66
      /// Terminate message (disconnect)
67
      Frontend_Terminate,
68
      /// Unknown frontend message type
69
      Frontend_Unknown,
70
71
      // Backend (server) message types
72
73
      /// Authentication successful
74
      Backend_AuthenticationOk,
75
      /// Authentication using Kerberos V4
76
      Backend_AuthenticationKerberosV4,
77
      /// Authentication using Kerberos V5
78
      Backend_AuthenticationKerberosV5,
79
      /// Authentication using cleartext password
80
      Backend_AuthenticationCleartextPassword,
81
      /// Authentication using MD5 password
82
      Backend_AuthenticationMD5Password,
83
      /// Authentication using GSSAPI
84
      Backend_AuthenticationGSS,
85
      /// GSSAPI authentication continues
86
      Backend_AuthenticationGSSContinue,
87
      /// Authentication using SSPI
88
      Backend_AuthenticationSSPI,
89
      /// SASL authentication mechanism list
90
      Backend_AuthenticationSASL,
91
      /// SASL authentication continues
92
      Backend_AuthenticationSASLContinue,
93
      /// SASL authentication final message
94
      Backend_AuthenticationSASLFinal,
95
      /// Backend key data (secret key for cancel)
96
      Backend_BackendKeyData,
97
      /// Bind complete
98
      Backend_BindComplete,
99
      /// Close complete
100
      Backend_CloseComplete,
101
      /// Command complete (after query execution)
102
      Backend_CommandComplete,
103
      /// Copy data (during COPY)
104
      Backend_CopyData,
105
      /// Copy done (during COPY)
106
      Backend_CopyDone,
107
      /// Copy in response (during COPY from client)
108
      Backend_CopyInResponse,
109
      /// Copy out response (during COPY to client)
110
      Backend_CopyOutResponse,
111
      /// Copy both response (during COPY bidirectional)
112
      Backend_CopyBothResponse,
113
      /// Data row (result set)
114
      Backend_DataRow,
115
      /// Empty query response
116
      Backend_EmptyQueryResponse,
117
      /// Error response
118
      Backend_ErrorResponse,
119
      /// Function call response
120
      Backend_FunctionCallResponse,
121
      /// Negotiate protocol version
122
      Backend_NegotiateProtocolVersion,
123
      /// No data (for queries that don't return rows)
124
      Backend_NoData,
125
      /// Notice response (warning)
126
      Backend_NoticeResponse,
127
      /// Notification response (LISTEN/NOTIFY)
128
      Backend_NotificationResponse,
129
      /// Parameter description (for prepared statements)
130
      Backend_ParameterDescription,
131
      /// Parameter status (runtime parameter setting)
132
      Backend_ParameterStatus,
133
      /// Parse complete
134
      Backend_ParseComplete,
135
      /// Portal suspended (during cursor fetch)
136
      Backend_PortalSuspended,
137
      /// Ready for query (idle state)
138
      Backend_ReadyForQuery,
139
      /// Row description (column definitions)
140
      Backend_RowDescription,
141
      /// Unknown backend message type
142
      Backend_Unknown,
143
    };
144
145
    constexpr PostgresMessageType() : m_Value(Frontend_Unknown)
146
0
    {}
147
148
    // cppcheck-suppress noExplicitConstructor
149
    /// @brief Constructs a PostgresMessageType object from a Value enum
150
    /// @param[in] value The Value enum value
151
69
    constexpr PostgresMessageType(Value value) : m_Value(value)
152
69
    {}
153
154
    /// @brief Converts the message type to its character representation
155
    /// @return The message type character
156
    char toChar() const;
157
158
    /// @brief Returns a string representation of the message type
159
    /// @return A string representation of the message type
160
    std::string toString() const;
161
162
    /// @brief Stream operator for PostgresMessageType
163
    /// @param[in] os The output stream
164
    /// @param[in] messageType The message type to print
165
    /// @return The output stream
166
    friend std::ostream& operator<<(std::ostream& os, const PostgresMessageType& messageType)
167
0
    {
168
0
      os << messageType.toString();
169
0
      return os;
170
0
    }
171
172
    /// @brief Converts the message type to a string
173
    /// @return The message type as a string
174
    explicit operator std::string() const
175
0
    {
176
0
      return toString();
177
0
    }
178
179
    /// @brief Returns the origin of the message (frontend or backend)
180
    /// @return The message origin
181
    PostgresMessageOrigin getOrigin() const;
182
183
    // Allow switch and comparisons
184
    constexpr operator Value() const
185
0
    {
186
0
      return m_Value;
187
0
    }
188
189
    // Prevent usage: if(PostgresMessageType)
190
    explicit operator bool() const = delete;
191
192
  private:
193
    Value m_Value;
194
  };
195
196
  /// @class PostgresMessage
197
  /// Represents a PostgreSQL message (base class)
198
  class PostgresMessage
199
  {
200
  public:
201
69
    virtual ~PostgresMessage() = default;
202
203
    /// @brief Parse a PostgreSQL backend message from raw data
204
    /// @param[in] data A pointer to the raw data
205
    /// @param[in] dataLen Size of the data in bytes
206
    /// @return A unique pointer to the parsed PostgresMessage, or nullptr if parsing fails
207
    static std::unique_ptr<PostgresMessage> parsePostgresBackendMessage(const uint8_t* data, size_t dataLen);
208
209
    /// @brief Parse a PostgreSQL frontend message from raw data
210
    /// @param[in] data A pointer to the raw data
211
    /// @param[in] dataLen Size of the data in bytes
212
    /// @return A unique pointer to the parsed PostgresMessage, or nullptr if parsing fails
213
    static std::unique_ptr<PostgresMessage> parsePostgresFrontendMessage(const uint8_t* data, size_t dataLen);
214
215
    /// @return The message type
216
    PostgresMessageType getMessageType() const
217
0
    {
218
0
      return m_MessageType;
219
0
    }
220
221
    /// @return The message origin (frontend or backend)
222
    PostgresMessageOrigin getMessageOrigin() const
223
0
    {
224
0
      return m_MessageType.getOrigin();
225
0
    }
226
227
    /// @brief Returns the length of the message payload
228
    /// @return The message length. If the first byte the message starts is 0, with length (no message type)
229
    uint32_t getMessageLength() const;
230
231
    /// @brief Returns the total length of the message including the length field
232
    /// @return The total message length in bytes
233
    size_t getTotalMessageLength() const
234
138
    {
235
138
      return m_DataLen;
236
138
    }
237
238
    /// @brief Returns the raw payload bytes of the message
239
    /// @return The raw payload bytes of the message
240
    std::vector<uint8_t> getRawPayload() const;
241
242
  protected:
243
    PostgresMessage(const uint8_t* data, size_t dataLen, const PostgresMessageType& messageType)
244
69
        : m_Data(data), m_DataLen(dataLen), m_MessageType(messageType)
245
69
    {}
246
247
    const uint8_t* m_Data;
248
    size_t m_DataLen;
249
    PostgresMessageType m_MessageType;
250
  };
251
252
  /// @class PostgresStartupMessage
253
  /// Represents a PostgreSQL StartupMessage
254
  class PostgresStartupMessage : public PostgresMessage
255
  {
256
  public:
257
    /// A map of parameter name to value
258
    using ParameterMap = std::unordered_map<std::string, std::string>;
259
260
    /// A constructor that creates the layer from an existing packet raw data
261
    /// @param[in] data A pointer to the raw data
262
    /// @param[in] dataLen Size of the data in bytes
263
    PostgresStartupMessage(const uint8_t* data, size_t dataLen)
264
2
        : PostgresMessage(data, dataLen, PostgresMessageType::Frontend_StartupMessage)
265
2
    {}
266
267
    /// @return The protocol version (major version in high 16 bits, minor version in low 16 bits)
268
    uint32_t getProtocolVersion() const;
269
270
    /// @return The major protocol version number
271
    uint16_t getProtocolMajorVersion() const;
272
273
    /// @return The minor protocol version number
274
    uint16_t getProtocolMinorVersion() const;
275
276
    /// @return The parameter name/value pairs as a map
277
    const ParameterMap& getParameters() const;
278
279
    /// @return The value of a specific parameter, or empty string if not found
280
    std::string getParameter(const std::string& name) const;
281
282
  private:
283
    static constexpr size_t ProtocolVersionOffset = 4;
284
    static constexpr size_t MinStartupMessageLength = 8;
285
286
    mutable ParameterMap m_Parameters;
287
    mutable bool m_ParametersParsed = false;
288
289
    std::string readString(size_t offset) const;
290
  };
291
292
  /// @class PostgresParameterStatus
293
  /// Represents a PostgreSQL ParameterStatus message
294
  class PostgresParameterStatus : public PostgresMessage
295
  {
296
  public:
297
    /// A constructor that creates the layer from an existing packet raw data
298
    /// @param[in] data A pointer to the raw data
299
    /// @param[in] dataLen Size of the data in bytes
300
    PostgresParameterStatus(const uint8_t* data, size_t dataLen)
301
46
        : PostgresMessage(data, dataLen, PostgresMessageType::Backend_ParameterStatus)
302
46
    {}
303
304
    /// @return The parameter name
305
    std::string getParameterName() const;
306
307
    /// @return The parameter value
308
    std::string getParameterValue() const;
309
  };
310
311
  /// @class PostgresQueryMessage
312
  /// Represents a PostgreSQL Query message (Frontend)
313
  class PostgresQueryMessage : public PostgresMessage
314
  {
315
  public:
316
    /// A constructor that creates the layer from an existing packet raw data
317
    /// @param[in] data A pointer to the raw data
318
    /// @param[in] dataLen Size of the data in bytes
319
    PostgresQueryMessage(const uint8_t* data, size_t dataLen)
320
0
        : PostgresMessage(data, dataLen, PostgresMessageType::Frontend_Query)
321
0
    {}
322
323
    /// @return The SQL query string
324
    std::string getQuery() const;
325
  };
326
327
  /// @class PostgresRowDescriptionMessage
328
  /// Represents a PostgreSQL RowDescription message (backend)
329
  class PostgresRowDescriptionMessage : public PostgresMessage
330
  {
331
  public:
332
    /// @enum PostgresColumnFormat
333
    /// Represents the format of a column in a PostgreSQL RowDescription message.
334
    /// PostgreSQL supports two formats: text (0) and binary (1).
335
    enum class PostgresColumnFormat
336
    {
337
      /// Text format (format code 0)
338
      Text = 0,
339
      /// Binary format (format code 1)
340
      Binary = 1,
341
      /// Unknown format (format code >= 2)
342
      Unknown = 2
343
    };
344
345
    /// @struct PostgresColumnInfo
346
    /// Represents metadata for a single column in a RowDescription message
347
    struct PostgresColumnInfo
348
    {
349
      /// Column name
350
      std::string name;
351
      /// Table OID (0 if not from a table column)
352
      uint32_t tableOID;
353
      /// Column index within the table
354
      uint16_t columnIndex;
355
      /// Data type OID
356
      uint32_t typeOID;
357
      /// Type size (-1 for variable length)
358
      int16_t typeSize;
359
      /// Type modifier (-1 if none)
360
      int32_t typeModifier;
361
      /// Format
362
      PostgresColumnFormat format;
363
    };
364
365
    /// A constructor that creates the layer from an existing packet raw data
366
    /// @param[in] data A pointer to the raw data
367
    /// @param[in] dataLen Size of the data in bytes
368
    PostgresRowDescriptionMessage(const uint8_t* data, size_t dataLen)
369
0
        : PostgresMessage(data, dataLen, PostgresMessageType::Backend_RowDescription)
370
0
    {}
371
372
    /// @return Vector of column metadata
373
    std::vector<PostgresColumnInfo> getColumnInfos() const;
374
  };
375
376
  /// @class PostgresDataRowMessage
377
  /// Represents a PostgreSQL DataRow message (backend)
378
  class PostgresDataRowMessage : public PostgresMessage
379
  {
380
  public:
381
    /// @class ColumnData
382
    /// Represents raw column data in a PostgreSQL DataRow message
383
    class ColumnData
384
    {
385
    public:
386
      /// A constructor that creates ColumnData from raw bytes
387
      /// @param[in] data A pointer to the raw column data
388
      /// @param[in] dataLen Size of the data in bytes
389
0
      ColumnData(const uint8_t* data, size_t dataLen) : m_Data(data), m_DataLen(dataLen)
390
0
      {}
391
392
      /// @return The raw column data as a vector of bytes
393
      std::vector<uint8_t> getData() const
394
0
      {
395
0
        if (!m_Data)
396
0
        {
397
0
          return {};
398
0
        }
399
0
        return { m_Data, m_Data + m_DataLen };
400
0
      }
401
402
      /// @return The column data as a hex string
403
      std::string toHexString() const;
404
405
      /// @return The column data as a UTF-8 string (empty if conversion fails)
406
      std::string toString() const;
407
408
      /// @return True if the column value is NULL
409
      bool isNull() const
410
0
      {
411
0
        return m_Data == nullptr;
412
0
      }
413
414
    private:
415
      const uint8_t* m_Data;
416
      size_t m_DataLen;
417
    };
418
419
    /// A constructor that creates the layer from an existing packet raw data
420
    /// @param[in] data A pointer to the raw data
421
    /// @param[in] dataLen Size of the data in bytes
422
    PostgresDataRowMessage(const uint8_t* data, size_t dataLen)
423
0
        : PostgresMessage(data, dataLen, PostgresMessageType::Backend_DataRow)
424
0
    {}
425
426
    /// @return Vector of column data values
427
    std::vector<ColumnData> getDataRow() const;
428
  };
429
430
  /// @class PostgresErrorResponseMessage
431
  /// Represents a PostgreSQL ErrorResponse or NoticeResponse message (backend)
432
  class PostgresErrorResponseMessage : public PostgresMessage
433
  {
434
  public:
435
    /// @enum ErrorField
436
    /// Represents the field types in a PostgreSQL ErrorResponse or NoticeResponse message
437
    enum class ErrorField : uint8_t
438
    {
439
      /// Severity: the field contents are ERROR, FATAL, or PANIC (localized)
440
      Severity = 'S',
441
      /// Severity: the field contents are ERROR, FATAL, PANIC or DEBUG, LOG, INFO, NOTICE, WARNING, or DEBUG
442
      /// (non-localized)
443
      SeverityNonLocalized = 'V',
444
      /// SQLSTATE code
445
      SQLState = 'C',
446
      /// Primary human-readable error message
447
      Message = 'M',
448
      /// Optional secondary error message
449
      Detail = 'D',
450
      /// Optional hint
451
      Hint = 'H',
452
      /// Decimal integer indicating an error cursor position
453
      Position = 'P',
454
      /// Internal cursor position (where error occurred)
455
      InternalPosition = 'p',
456
      /// Text of internal query
457
      InternalQuery = 'q',
458
      /// Indicating context of error
459
      Where = 'W',
460
      /// Schema name
461
      Schema = 's',
462
      /// Table name
463
      Table = 't',
464
      /// Column name
465
      Column = 'c',
466
      /// Data type name
467
      DataType = 'd',
468
      /// Constraint name
469
      Constraint = 'n',
470
      /// File name of error
471
      File = 'F',
472
      /// Line number of error
473
      Line = 'L',
474
      /// Routine name
475
      Routine = 'R',
476
      /// Terminator (always '\0')
477
      Terminator = '\0'
478
    };
479
480
    /// A map of error field type to value
481
    using FieldMap = std::unordered_map<ErrorField, std::string>;
482
483
    /// A constructor that creates the layer from an existing packet raw data
484
    /// @param[in] data A pointer to the raw data
485
    /// @param[in] dataLen Size of the data in bytes
486
    PostgresErrorResponseMessage(const uint8_t* data, size_t dataLen)
487
0
        : PostgresMessage(data, dataLen, PostgresMessageType::Backend_ErrorResponse)
488
0
    {}
489
490
    /// @return The error fields as a map
491
    const FieldMap& getFields() const;
492
493
  private:
494
    mutable FieldMap m_Fields;
495
    mutable bool m_FieldsParsed = false;
496
  };
497
498
  /// @class PostgresLayer
499
  /// Represents a PostgreSQL protocol layer
500
  class PostgresLayer : public Layer
501
  {
502
  public:
503
    /// A d'tor for this layer
504
67
    ~PostgresLayer() override = default;
505
506
    /// A static method that checks whether the port is considered as PostgreSQL
507
    /// @param[in] port The port number to be checked
508
    static bool isPostgresPort(uint16_t port)
509
146k
    {
510
146k
      return port == 5432;
511
146k
    }
512
513
    /// Parse a PostgreSQL backend message from raw data
514
    /// @param[in] data A pointer to the raw data
515
    /// @param[in] dataLen Size of the data in bytes
516
    /// @param[in] prevLayer A pointer to the previous layer
517
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
518
    /// @return A pointer to the parsed PostgresLayer, or nullptr if parsing fails
519
    static PostgresLayer* parsePostgresBackendMessages(uint8_t* data, size_t dataLen, Layer* prevLayer,
520
                                                       Packet* packet);
521
522
    /// Parse a PostgreSQL frontend message from raw data
523
    /// @param[in] data A pointer to the raw data
524
    /// @param[in] dataLen Size of the data in bytes
525
    /// @param[in] prevLayer A pointer to the previous layer
526
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
527
    /// @return A pointer to the parsed PostgresLayer, or nullptr if parsing fails
528
    static PostgresLayer* parsePostgresFrontendMessages(uint8_t* data, size_t dataLen, Layer* prevLayer,
529
                                                        Packet* packet);
530
531
    /// @return The message origin (frontend or backend)
532
    PostgresMessageOrigin getPostgresOrigin() const
533
0
    {
534
0
      return m_MessageOrigin;
535
0
    }
536
537
    /// @return A vector of all PostgreSQL messages in this layer
538
    const PointerVector<PostgresMessage>& getPostgresMessages() const;
539
540
    /// @brief Get a PostgreSQL message by its type
541
    /// @param[in] messageType The type of message to retrieve
542
    /// @return A pointer to the message, or nullptr if not found
543
    const PostgresMessage* getPostgresMessage(const PostgresMessageType& messageType) const;
544
545
    /// @brief Get a PostgreSQL message by its type (template version)
546
    /// @tparam TMessage The message type to retrieve (must derive from PostgresMessage)
547
    /// @return A pointer to the message of the specified type, or nullptr if not found
548
    template <class TMessage, std::enable_if_t<std::is_base_of<PostgresMessage, TMessage>::value, bool> = nullptr>
549
    const TMessage* getPostgresMessage() const
550
    {
551
      const auto& messages = getPostgresMessages();
552
      for (const auto& msg : messages)
553
      {
554
        auto result = dynamic_cast<const TMessage*>(msg);
555
        if (result != nullptr)
556
        {
557
          return result;
558
        }
559
      }
560
      return nullptr;
561
    }
562
563
    // Overridden methods
564
565
    /// @return The size of the Postgres layer header
566
    size_t getHeaderLen() const override
567
13
    {
568
13
      return m_DataLen;
569
13
    }
570
571
    /// Does nothing for this layer, Postgres is always last
572
    void parseNextLayer() override
573
67
    {}
574
575
    /// Does nothing for this layer
576
    void computeCalculateFields() override
577
13
    {}
578
579
    /// @return The OSI layer level of Postgres (Application Layer).
580
    OsiModelLayer getOsiModelLayer() const override
581
13
    {
582
13
      return OsiModelApplicationLayer;
583
13
    }
584
585
    /// @return Returns the protocol info as readable string
586
    std::string toString() const override;
587
588
  private:
589
    PostgresMessageOrigin m_MessageOrigin;
590
    mutable PointerVector<PostgresMessage> m_Messages;
591
    mutable bool m_MessagesInitialized = false;
592
593
    PostgresLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet,
594
                  PostgresMessageOrigin messageOrigin)
595
67
        : Layer(data, dataLen, prevLayer, packet, Postgres), m_MessageOrigin(messageOrigin)
596
67
    {}
597
  };
598
}  // namespace pcpp