/src/PcapPlusPlus/Packet++/src/PostgresLayer.cpp
Line | Count | Source |
1 | | #include "PostgresLayer.h" |
2 | | #include "EndianPortable.h" |
3 | | #include "GeneralUtils.h" |
4 | | #include <algorithm> |
5 | | #include <cstring> |
6 | | #include <unordered_set> |
7 | | |
8 | | #pragma pack(push, 1) |
9 | | namespace internal |
10 | | { |
11 | | struct PostgresColumnFixedData |
12 | | { |
13 | | uint32_t tableOID; |
14 | | uint16_t columnIndex; |
15 | | uint32_t typeOID; |
16 | | int16_t typeSize; |
17 | | int32_t typeModifier; |
18 | | uint16_t formatCode; |
19 | | }; |
20 | | } // namespace internal |
21 | | #pragma pack(pop) |
22 | | |
23 | | static_assert(sizeof(::internal::PostgresColumnFixedData) == 18, "PostgresColumnFixedData must be 18 bytes"); |
24 | | |
25 | | namespace pcpp |
26 | | { |
27 | | constexpr char PostgresMessage_0 = '\0'; |
28 | | constexpr char PostgresBackendMessage_R = 'R'; |
29 | | constexpr char PostgresBackendMessage_K = 'K'; |
30 | | constexpr char PostgresBackendMessage_2 = '2'; |
31 | | constexpr char PostgresBackendMessage_3 = '3'; |
32 | | constexpr char PostgresBackendMessage_S = 'S'; |
33 | | constexpr char PostgresBackendMessage_Z = 'Z'; |
34 | | constexpr char PostgresBackendMessage_C = 'C'; |
35 | | constexpr char PostgresBackendMessage_d = 'd'; |
36 | | constexpr char PostgresBackendMessage_c = 'c'; |
37 | | constexpr char PostgresBackendMessage_G = 'G'; |
38 | | constexpr char PostgresBackendMessage_H = 'H'; |
39 | | constexpr char PostgresBackendMessage_W = 'W'; |
40 | | constexpr char PostgresBackendMessage_D = 'D'; |
41 | | constexpr char PostgresBackendMessage_I = 'I'; |
42 | | constexpr char PostgresBackendMessage_E = 'E'; |
43 | | constexpr char PostgresBackendMessage_V = 'V'; |
44 | | constexpr char PostgresBackendMessage_v = 'v'; |
45 | | constexpr char PostgresBackendMessage_n = 'n'; |
46 | | constexpr char PostgresBackendMessage_N = 'N'; |
47 | | constexpr char PostgresBackendMessage_A = 'A'; |
48 | | constexpr char PostgresBackendMessage_t = 't'; |
49 | | constexpr char PostgresBackendMessage_1 = '1'; |
50 | | constexpr char PostgresBackendMessage_s = 's'; |
51 | | constexpr char PostgresBackendMessage_T = 'T'; |
52 | | constexpr char PostgresFrontendMessage_Q = 'Q'; |
53 | | constexpr char PostgresFrontendMessage_P = 'P'; |
54 | | constexpr char PostgresFrontendMessage_B = 'B'; |
55 | | constexpr char PostgresFrontendMessage_E = 'E'; |
56 | | constexpr char PostgresFrontendMessage_C = 'C'; |
57 | | constexpr char PostgresFrontendMessage_D = 'D'; |
58 | | constexpr char PostgresFrontendMessage_F = 'F'; |
59 | | constexpr char PostgresFrontendMessage_H = 'H'; |
60 | | constexpr char PostgresFrontendMessage_S = 'S'; |
61 | | constexpr char PostgresFrontendMessage_d = 'd'; |
62 | | constexpr char PostgresFrontendMessage_c = 'c'; |
63 | | constexpr char PostgresFrontendMessage_f = 'f'; |
64 | | constexpr char PostgresFrontendMessage_X = 'X'; |
65 | | |
66 | | const std::unordered_set<uint8_t> validErrorFieldTypes = { 'S', 'V', 'C', 'M', 'D', 'H', 'P', 'p', 'q', |
67 | | 'W', 's', 't', 'c', 'd', 'n', 'F', 'L', 'R' }; |
68 | | |
69 | | constexpr uint32_t PostgresFrontendTag_SSLRequest = 80877103; |
70 | | constexpr uint32_t PostgresFrontendTag_GSSENCRequest = 80877104; |
71 | | constexpr uint32_t PostgresFrontendTag_CancelRequest = 80877102; |
72 | | constexpr uint32_t PostgresFrontendTag_StartupMessage = 196608; |
73 | | |
74 | | char PostgresMessageType::toChar() const |
75 | 0 | { |
76 | 0 | switch (m_Value) |
77 | 0 | { |
78 | | // Frontend message types |
79 | 0 | case Frontend_Query: |
80 | 0 | return PostgresFrontendMessage_Q; |
81 | 0 | case Frontend_Parse: |
82 | 0 | return PostgresFrontendMessage_P; |
83 | 0 | case Frontend_Bind: |
84 | 0 | return PostgresFrontendMessage_B; |
85 | 0 | case Frontend_Execute: |
86 | 0 | return PostgresFrontendMessage_E; |
87 | 0 | case Frontend_Close: |
88 | 0 | return PostgresFrontendMessage_C; |
89 | 0 | case Frontend_Describe: |
90 | 0 | return PostgresFrontendMessage_D; |
91 | 0 | case Frontend_FunctionCall: |
92 | 0 | return PostgresFrontendMessage_F; |
93 | 0 | case Frontend_Flush: |
94 | 0 | return PostgresFrontendMessage_H; |
95 | 0 | case Frontend_Sync: |
96 | 0 | return PostgresFrontendMessage_S; |
97 | 0 | case Frontend_CopyData: |
98 | 0 | return PostgresFrontendMessage_d; |
99 | 0 | case Frontend_CopyDone: |
100 | 0 | return PostgresFrontendMessage_c; |
101 | 0 | case Frontend_CopyFail: |
102 | 0 | return PostgresFrontendMessage_f; |
103 | 0 | case Frontend_Terminate: |
104 | 0 | return PostgresFrontendMessage_X; |
105 | | // Backend message types |
106 | 0 | case Backend_AuthenticationOk: |
107 | 0 | case Backend_AuthenticationMD5Password: |
108 | 0 | case Backend_AuthenticationSASL: |
109 | 0 | case Backend_AuthenticationSASLContinue: |
110 | 0 | case Backend_AuthenticationSASLFinal: |
111 | 0 | return PostgresBackendMessage_R; |
112 | 0 | case Backend_BackendKeyData: |
113 | 0 | return PostgresBackendMessage_K; |
114 | 0 | case Backend_BindComplete: |
115 | 0 | return PostgresBackendMessage_2; |
116 | 0 | case Backend_CloseComplete: |
117 | 0 | return PostgresBackendMessage_3; |
118 | 0 | case Backend_CommandComplete: |
119 | 0 | return PostgresBackendMessage_C; |
120 | 0 | case Backend_CopyData: |
121 | 0 | return PostgresBackendMessage_d; |
122 | 0 | case Backend_CopyDone: |
123 | 0 | return PostgresBackendMessage_c; |
124 | 0 | case Backend_CopyInResponse: |
125 | 0 | return PostgresBackendMessage_G; |
126 | 0 | case Backend_CopyOutResponse: |
127 | 0 | return PostgresBackendMessage_H; |
128 | 0 | case Backend_CopyBothResponse: |
129 | 0 | return PostgresBackendMessage_W; |
130 | 0 | case Backend_DataRow: |
131 | 0 | return PostgresBackendMessage_D; |
132 | 0 | case Backend_EmptyQueryResponse: |
133 | 0 | return PostgresBackendMessage_I; |
134 | 0 | case Backend_ErrorResponse: |
135 | 0 | return PostgresBackendMessage_E; |
136 | 0 | case Backend_FunctionCallResponse: |
137 | 0 | return PostgresBackendMessage_V; |
138 | 0 | case Backend_NegotiateProtocolVersion: |
139 | 0 | return PostgresBackendMessage_v; |
140 | 0 | case Backend_NoData: |
141 | 0 | return PostgresBackendMessage_n; |
142 | 0 | case Backend_NoticeResponse: |
143 | 0 | return PostgresBackendMessage_N; |
144 | 0 | case Backend_NotificationResponse: |
145 | 0 | return PostgresBackendMessage_A; |
146 | 0 | case Backend_ParameterDescription: |
147 | 0 | return PostgresBackendMessage_t; |
148 | 0 | case Backend_ParameterStatus: |
149 | 0 | return PostgresBackendMessage_S; |
150 | 0 | case Backend_ParseComplete: |
151 | 0 | return PostgresBackendMessage_1; |
152 | 0 | case Backend_PortalSuspended: |
153 | 0 | return PostgresBackendMessage_s; |
154 | 0 | case Backend_ReadyForQuery: |
155 | 0 | return PostgresBackendMessage_Z; |
156 | 0 | case Backend_RowDescription: |
157 | 0 | return PostgresBackendMessage_T; |
158 | 0 | default: |
159 | 0 | return '\0'; |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | std::string PostgresMessageType::toString() const |
164 | 0 | { |
165 | 0 | switch (m_Value) |
166 | 0 | { |
167 | | // Frontend message types |
168 | 0 | case Frontend_StartupMessage: |
169 | 0 | return "Frontend_StartupMessage"; |
170 | 0 | case Frontend_SSLRequest: |
171 | 0 | return "Frontend_SSLRequest"; |
172 | 0 | case Frontend_CancelRequest: |
173 | 0 | return "Frontend_CancelRequest"; |
174 | 0 | case Frontend_GSSENCRequest: |
175 | 0 | return "Frontend_GSSENCRequest"; |
176 | 0 | case Frontend_Query: |
177 | 0 | return "Frontend_Query"; |
178 | 0 | case Frontend_Parse: |
179 | 0 | return "Frontend_Parse"; |
180 | 0 | case Frontend_Bind: |
181 | 0 | return "Frontend_Bind"; |
182 | 0 | case Frontend_Execute: |
183 | 0 | return "Frontend_Execute"; |
184 | 0 | case Frontend_Close: |
185 | 0 | return "Frontend_Close"; |
186 | 0 | case Frontend_Describe: |
187 | 0 | return "Frontend_Describe"; |
188 | 0 | case Frontend_FunctionCall: |
189 | 0 | return "Frontend_FunctionCall"; |
190 | 0 | case Frontend_Flush: |
191 | 0 | return "Frontend_Flush"; |
192 | 0 | case Frontend_Sync: |
193 | 0 | return "Frontend_Sync"; |
194 | 0 | case Frontend_CopyData: |
195 | 0 | return "Frontend_CopyData"; |
196 | 0 | case Frontend_CopyDone: |
197 | 0 | return "Frontend_CopyDone"; |
198 | 0 | case Frontend_CopyFail: |
199 | 0 | return "Frontend_CopyFail"; |
200 | 0 | case Frontend_Terminate: |
201 | 0 | return "Frontend_Terminate"; |
202 | 0 | case Frontend_Unknown: |
203 | 0 | return "Frontend_Unknown"; |
204 | | // Backend message types |
205 | 0 | case Backend_AuthenticationOk: |
206 | 0 | return "Backend_AuthenticationOk"; |
207 | 0 | case Backend_AuthenticationKerberosV4: |
208 | 0 | return "Backend_AuthenticationKerberosV4"; |
209 | 0 | case Backend_AuthenticationKerberosV5: |
210 | 0 | return "Backend_AuthenticationKerberosV5"; |
211 | 0 | case Backend_AuthenticationCleartextPassword: |
212 | 0 | return "Backend_AuthenticationCleartextPassword"; |
213 | 0 | case Backend_AuthenticationMD5Password: |
214 | 0 | return "Backend_AuthenticationMD5Password"; |
215 | 0 | case Backend_AuthenticationGSS: |
216 | 0 | return "Backend_AuthenticationGSS"; |
217 | 0 | case Backend_AuthenticationGSSContinue: |
218 | 0 | return "Backend_AuthenticationGSSContinue"; |
219 | 0 | case Backend_AuthenticationSSPI: |
220 | 0 | return "Backend_AuthenticationSSPI"; |
221 | 0 | case Backend_AuthenticationSASL: |
222 | 0 | return "Backend_AuthenticationSASL"; |
223 | 0 | case Backend_AuthenticationSASLContinue: |
224 | 0 | return "Backend_AuthenticationSASLContinue"; |
225 | 0 | case Backend_AuthenticationSASLFinal: |
226 | 0 | return "Backend_AuthenticationSASLFinal"; |
227 | 0 | case Backend_BackendKeyData: |
228 | 0 | return "Backend_BackendKeyData"; |
229 | 0 | case Backend_BindComplete: |
230 | 0 | return "Backend_BindComplete"; |
231 | 0 | case Backend_CloseComplete: |
232 | 0 | return "Backend_CloseComplete"; |
233 | 0 | case Backend_CommandComplete: |
234 | 0 | return "Backend_CommandComplete"; |
235 | 0 | case Backend_CopyData: |
236 | 0 | return "Backend_CopyData"; |
237 | 0 | case Backend_CopyDone: |
238 | 0 | return "Backend_CopyDone"; |
239 | 0 | case Backend_CopyInResponse: |
240 | 0 | return "Backend_CopyInResponse"; |
241 | 0 | case Backend_CopyOutResponse: |
242 | 0 | return "Backend_CopyOutResponse"; |
243 | 0 | case Backend_CopyBothResponse: |
244 | 0 | return "Backend_CopyBothResponse"; |
245 | 0 | case Backend_DataRow: |
246 | 0 | return "Backend_DataRow"; |
247 | 0 | case Backend_EmptyQueryResponse: |
248 | 0 | return "Backend_EmptyQueryResponse"; |
249 | 0 | case Backend_ErrorResponse: |
250 | 0 | return "Backend_ErrorResponse"; |
251 | 0 | case Backend_FunctionCallResponse: |
252 | 0 | return "Backend_FunctionCallResponse"; |
253 | 0 | case Backend_NegotiateProtocolVersion: |
254 | 0 | return "Backend_NegotiateProtocolVersion"; |
255 | 0 | case Backend_NoData: |
256 | 0 | return "Backend_NoData"; |
257 | 0 | case Backend_NoticeResponse: |
258 | 0 | return "Backend_NoticeResponse"; |
259 | 0 | case Backend_NotificationResponse: |
260 | 0 | return "Backend_NotificationResponse"; |
261 | 0 | case Backend_ParameterDescription: |
262 | 0 | return "Backend_ParameterDescription"; |
263 | 0 | case Backend_ParameterStatus: |
264 | 0 | return "Backend_ParameterStatus"; |
265 | 0 | case Backend_ParseComplete: |
266 | 0 | return "Backend_ParseComplete"; |
267 | 0 | case Backend_PortalSuspended: |
268 | 0 | return "Backend_PortalSuspended"; |
269 | 0 | case Backend_ReadyForQuery: |
270 | 0 | return "Backend_ReadyForQuery"; |
271 | 0 | case Backend_RowDescription: |
272 | 0 | return "Backend_RowDescription"; |
273 | 0 | case Backend_Unknown: |
274 | 0 | return "Backend_Unknown"; |
275 | 0 | default: |
276 | 0 | return "Unknown"; |
277 | 0 | } |
278 | 0 | } |
279 | | |
280 | | PostgresMessageOrigin PostgresMessageType::getOrigin() const |
281 | 0 | { |
282 | 0 | switch (m_Value) |
283 | 0 | { |
284 | 0 | case Frontend_StartupMessage: |
285 | 0 | case Frontend_SSLRequest: |
286 | 0 | case Frontend_CancelRequest: |
287 | 0 | case Frontend_GSSENCRequest: |
288 | 0 | case Frontend_Query: |
289 | 0 | case Frontend_Parse: |
290 | 0 | case Frontend_Bind: |
291 | 0 | case Frontend_Execute: |
292 | 0 | case Frontend_Close: |
293 | 0 | case Frontend_Describe: |
294 | 0 | case Frontend_FunctionCall: |
295 | 0 | case Frontend_Flush: |
296 | 0 | case Frontend_Sync: |
297 | 0 | case Frontend_CopyData: |
298 | 0 | case Frontend_CopyDone: |
299 | 0 | case Frontend_CopyFail: |
300 | 0 | case Frontend_Terminate: |
301 | 0 | case Frontend_Unknown: |
302 | 0 | return PostgresMessageOrigin::Frontend; |
303 | 0 | default: |
304 | 0 | return PostgresMessageOrigin::Backend; |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | | std::unique_ptr<PostgresMessage> PostgresMessage::parsePostgresBackendMessage(const uint8_t* data, size_t dataLen) |
309 | 6 | { |
310 | 6 | if (data == nullptr || dataLen < 1) |
311 | 0 | { |
312 | 0 | return nullptr; |
313 | 0 | } |
314 | | |
315 | 6 | if (dataLen < 5) |
316 | 5 | { |
317 | 5 | return std::unique_ptr<PostgresMessage>( |
318 | 5 | new PostgresMessage(data, dataLen, PostgresMessageType::Backend_Unknown)); |
319 | 5 | } |
320 | | |
321 | 1 | auto messageLength = be32toh(*reinterpret_cast<const uint32_t*>(data + 1)); |
322 | 1 | if (dataLen < messageLength + 1) |
323 | 1 | { |
324 | 1 | return std::unique_ptr<PostgresMessage>( |
325 | 1 | new PostgresMessage(data, dataLen, PostgresMessageType::Backend_Unknown)); |
326 | 1 | } |
327 | | |
328 | 0 | auto messageTypeValue = data[0]; |
329 | 0 | auto messageType = PostgresMessageType::Backend_Unknown; |
330 | |
|
331 | 0 | switch (messageTypeValue) |
332 | 0 | { |
333 | 0 | case PostgresBackendMessage_R: |
334 | 0 | { |
335 | 0 | uint32_t authType = be32toh(*reinterpret_cast<const uint32_t*>(data + 5)); |
336 | 0 | switch (authType) |
337 | 0 | { |
338 | 0 | case 0: |
339 | 0 | { |
340 | 0 | messageType = PostgresMessageType::Backend_AuthenticationOk; |
341 | 0 | break; |
342 | 0 | } |
343 | 0 | case 1: |
344 | 0 | { |
345 | 0 | messageType = PostgresMessageType::Backend_AuthenticationKerberosV4; |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | case 2: |
349 | 0 | { |
350 | 0 | messageType = PostgresMessageType::Backend_AuthenticationKerberosV5; |
351 | 0 | break; |
352 | 0 | } |
353 | 0 | case 3: |
354 | 0 | { |
355 | 0 | messageType = PostgresMessageType::Backend_AuthenticationCleartextPassword; |
356 | 0 | break; |
357 | 0 | } |
358 | 0 | case 5: |
359 | 0 | { |
360 | 0 | messageType = PostgresMessageType::Backend_AuthenticationMD5Password; |
361 | 0 | break; |
362 | 0 | } |
363 | 0 | case 7: |
364 | 0 | { |
365 | 0 | messageType = PostgresMessageType::Backend_AuthenticationGSS; |
366 | 0 | break; |
367 | 0 | } |
368 | 0 | case 8: |
369 | 0 | { |
370 | 0 | messageType = PostgresMessageType::Backend_AuthenticationGSSContinue; |
371 | 0 | break; |
372 | 0 | } |
373 | 0 | case 9: |
374 | 0 | { |
375 | 0 | messageType = PostgresMessageType::Backend_AuthenticationSSPI; |
376 | 0 | break; |
377 | 0 | } |
378 | 0 | case 10: |
379 | 0 | { |
380 | 0 | messageType = PostgresMessageType::Backend_AuthenticationSASL; |
381 | 0 | break; |
382 | 0 | } |
383 | 0 | case 11: |
384 | 0 | { |
385 | 0 | messageType = PostgresMessageType::Backend_AuthenticationSASLContinue; |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | case 12: |
389 | 0 | { |
390 | 0 | messageType = PostgresMessageType::Backend_AuthenticationSASLFinal; |
391 | 0 | break; |
392 | 0 | } |
393 | 0 | default: |
394 | 0 | { |
395 | 0 | break; |
396 | 0 | } |
397 | 0 | } |
398 | 0 | break; |
399 | 0 | } |
400 | 0 | case PostgresBackendMessage_K: |
401 | 0 | { |
402 | 0 | messageType = PostgresMessageType::Backend_BackendKeyData; |
403 | 0 | break; |
404 | 0 | } |
405 | 0 | case PostgresBackendMessage_2: |
406 | 0 | { |
407 | 0 | messageType = PostgresMessageType::Backend_BindComplete; |
408 | 0 | break; |
409 | 0 | } |
410 | 0 | case PostgresBackendMessage_3: |
411 | 0 | { |
412 | 0 | messageType = PostgresMessageType::Backend_CloseComplete; |
413 | 0 | break; |
414 | 0 | } |
415 | 0 | case PostgresBackendMessage_S: |
416 | 0 | { |
417 | 0 | return std::make_unique<PostgresParameterStatus>(data, messageLength + 1); |
418 | 0 | } |
419 | 0 | case PostgresBackendMessage_Z: |
420 | 0 | { |
421 | 0 | messageType = PostgresMessageType::Backend_ReadyForQuery; |
422 | 0 | break; |
423 | 0 | } |
424 | 0 | case PostgresBackendMessage_C: |
425 | 0 | { |
426 | 0 | messageType = PostgresMessageType::Backend_CommandComplete; |
427 | 0 | break; |
428 | 0 | } |
429 | 0 | case PostgresBackendMessage_d: |
430 | 0 | { |
431 | 0 | messageType = PostgresMessageType::Backend_CopyData; |
432 | 0 | break; |
433 | 0 | } |
434 | 0 | case PostgresBackendMessage_c: |
435 | 0 | { |
436 | 0 | messageType = PostgresMessageType::Backend_CopyDone; |
437 | 0 | break; |
438 | 0 | } |
439 | 0 | case PostgresBackendMessage_G: |
440 | 0 | { |
441 | 0 | messageType = PostgresMessageType::Backend_CopyInResponse; |
442 | 0 | break; |
443 | 0 | } |
444 | 0 | case PostgresBackendMessage_H: |
445 | 0 | { |
446 | 0 | messageType = PostgresMessageType::Backend_CopyOutResponse; |
447 | 0 | break; |
448 | 0 | } |
449 | 0 | case PostgresBackendMessage_W: |
450 | 0 | { |
451 | 0 | messageType = PostgresMessageType::Backend_CopyBothResponse; |
452 | 0 | break; |
453 | 0 | } |
454 | 0 | case PostgresBackendMessage_D: |
455 | 0 | { |
456 | 0 | return std::make_unique<PostgresDataRowMessage>(data, messageLength + 1); |
457 | 0 | } |
458 | 0 | case PostgresBackendMessage_I: |
459 | 0 | { |
460 | 0 | messageType = PostgresMessageType::Backend_EmptyQueryResponse; |
461 | 0 | break; |
462 | 0 | } |
463 | 0 | case PostgresBackendMessage_E: |
464 | 0 | { |
465 | 0 | return std::make_unique<PostgresErrorResponseMessage>(data, messageLength + 1); |
466 | 0 | } |
467 | 0 | case PostgresBackendMessage_V: |
468 | 0 | { |
469 | 0 | messageType = PostgresMessageType::Backend_FunctionCallResponse; |
470 | 0 | break; |
471 | 0 | } |
472 | 0 | case PostgresBackendMessage_v: |
473 | 0 | { |
474 | 0 | messageType = PostgresMessageType::Backend_NegotiateProtocolVersion; |
475 | 0 | break; |
476 | 0 | } |
477 | 0 | case PostgresBackendMessage_n: |
478 | 0 | { |
479 | 0 | messageType = PostgresMessageType::Backend_NoData; |
480 | 0 | break; |
481 | 0 | } |
482 | 0 | case PostgresBackendMessage_N: |
483 | 0 | { |
484 | 0 | messageType = PostgresMessageType::Backend_NoticeResponse; |
485 | 0 | break; |
486 | 0 | } |
487 | 0 | case PostgresBackendMessage_A: |
488 | 0 | { |
489 | 0 | messageType = PostgresMessageType::Backend_NotificationResponse; |
490 | 0 | break; |
491 | 0 | } |
492 | 0 | case PostgresBackendMessage_t: |
493 | 0 | { |
494 | 0 | messageType = PostgresMessageType::Backend_ParameterDescription; |
495 | 0 | break; |
496 | 0 | } |
497 | 0 | case PostgresBackendMessage_1: |
498 | 0 | { |
499 | 0 | messageType = PostgresMessageType::Backend_ParseComplete; |
500 | 0 | break; |
501 | 0 | } |
502 | 0 | case PostgresBackendMessage_s: |
503 | 0 | { |
504 | 0 | messageType = PostgresMessageType::Backend_PortalSuspended; |
505 | 0 | break; |
506 | 0 | } |
507 | 0 | case PostgresBackendMessage_T: |
508 | 0 | { |
509 | 0 | return std::make_unique<PostgresRowDescriptionMessage>(data, messageLength + 1); |
510 | 0 | } |
511 | 0 | default: |
512 | 0 | { |
513 | 0 | break; |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | 0 | return std::unique_ptr<PostgresMessage>(new PostgresMessage(data, messageLength + 1, messageType)); |
518 | 0 | } |
519 | | |
520 | | std::unique_ptr<PostgresMessage> PostgresMessage::parsePostgresFrontendMessage(const uint8_t* data, size_t dataLen) |
521 | 10 | { |
522 | 10 | if (data == nullptr || dataLen < 1) |
523 | 0 | { |
524 | 0 | return nullptr; |
525 | 0 | } |
526 | | |
527 | 10 | uint8_t messageTypeValue = data[0]; |
528 | 10 | if (messageTypeValue == PostgresMessage_0) |
529 | 3 | { |
530 | 3 | if (dataLen < 8) |
531 | 0 | { |
532 | 0 | return std::unique_ptr<PostgresMessage>( |
533 | 0 | new PostgresMessage(data, dataLen, PostgresMessageType::Frontend_Unknown)); |
534 | 0 | } |
535 | | |
536 | 3 | auto messageLength = be32toh(*reinterpret_cast<const uint32_t*>(data)); |
537 | 3 | if (messageLength > dataLen) |
538 | 2 | { |
539 | 2 | return std::unique_ptr<PostgresMessage>( |
540 | 2 | new PostgresMessage(data, dataLen, PostgresMessageType::Frontend_Unknown)); |
541 | 2 | } |
542 | | |
543 | 1 | auto messageTag = be32toh(*reinterpret_cast<const uint32_t*>(data + 4)); |
544 | 1 | auto messageType = PostgresMessageType::Frontend_Unknown; |
545 | | |
546 | 1 | switch (messageTag) |
547 | 1 | { |
548 | 0 | case PostgresFrontendTag_StartupMessage: |
549 | 0 | { |
550 | 0 | return std::make_unique<PostgresStartupMessage>(data, messageLength); |
551 | 0 | } |
552 | 0 | case PostgresFrontendTag_SSLRequest: |
553 | 0 | { |
554 | 0 | messageType = PostgresMessageType::Frontend_SSLRequest; |
555 | 0 | break; |
556 | 0 | } |
557 | 0 | case PostgresFrontendTag_CancelRequest: |
558 | 0 | { |
559 | 0 | messageType = PostgresMessageType::Frontend_CancelRequest; |
560 | 0 | break; |
561 | 0 | } |
562 | 0 | case PostgresFrontendTag_GSSENCRequest: |
563 | 0 | { |
564 | 0 | messageType = PostgresMessageType::Frontend_GSSENCRequest; |
565 | 0 | break; |
566 | 0 | } |
567 | 1 | default: |
568 | 1 | { |
569 | 1 | break; |
570 | 0 | } |
571 | 1 | } |
572 | | |
573 | 1 | return std::unique_ptr<PostgresMessage>(new PostgresMessage(data, messageLength, messageType)); |
574 | 1 | } |
575 | | |
576 | 7 | if (dataLen < 5) |
577 | 2 | { |
578 | 2 | return std::unique_ptr<PostgresMessage>( |
579 | 2 | new PostgresMessage(data, dataLen, PostgresMessageType::Frontend_Unknown)); |
580 | 2 | } |
581 | | |
582 | 5 | auto messageLength = be32toh(*reinterpret_cast<const uint32_t*>(data + 1)); |
583 | 5 | if (dataLen < messageLength + 1) |
584 | 4 | { |
585 | 4 | return std::unique_ptr<PostgresMessage>( |
586 | 4 | new PostgresMessage(data, dataLen, PostgresMessageType::Frontend_Unknown)); |
587 | 4 | } |
588 | | |
589 | 1 | auto messageType = PostgresMessageType::Frontend_Unknown; |
590 | 1 | switch (messageTypeValue) |
591 | 1 | { |
592 | 0 | case PostgresFrontendMessage_Q: |
593 | 0 | { |
594 | 0 | return std::make_unique<PostgresQueryMessage>(data, |
595 | 0 | (std::min)(static_cast<size_t>(messageLength) + 1, dataLen)); |
596 | 0 | } |
597 | 0 | case PostgresFrontendMessage_P: |
598 | 0 | { |
599 | 0 | messageType = PostgresMessageType::Frontend_Parse; |
600 | 0 | break; |
601 | 0 | } |
602 | 0 | case PostgresFrontendMessage_B: |
603 | 0 | { |
604 | 0 | messageType = PostgresMessageType::Frontend_Bind; |
605 | 0 | break; |
606 | 0 | } |
607 | 0 | case PostgresFrontendMessage_E: |
608 | 0 | { |
609 | 0 | messageType = PostgresMessageType::Frontend_Execute; |
610 | 0 | break; |
611 | 0 | } |
612 | 0 | case PostgresFrontendMessage_C: |
613 | 0 | { |
614 | 0 | messageType = PostgresMessageType::Frontend_Close; |
615 | 0 | break; |
616 | 0 | } |
617 | 0 | case PostgresFrontendMessage_D: |
618 | 0 | { |
619 | 0 | messageType = PostgresMessageType::Frontend_Describe; |
620 | 0 | break; |
621 | 0 | } |
622 | 0 | case PostgresFrontendMessage_F: |
623 | 0 | { |
624 | 0 | messageType = PostgresMessageType::Frontend_FunctionCall; |
625 | 0 | break; |
626 | 0 | } |
627 | 0 | case PostgresFrontendMessage_H: |
628 | 0 | { |
629 | 0 | messageType = PostgresMessageType::Frontend_Flush; |
630 | 0 | break; |
631 | 0 | } |
632 | 0 | case PostgresFrontendMessage_S: |
633 | 0 | { |
634 | 0 | messageType = PostgresMessageType::Frontend_Sync; |
635 | 0 | break; |
636 | 0 | } |
637 | 0 | case PostgresFrontendMessage_d: |
638 | 0 | { |
639 | 0 | messageType = PostgresMessageType::Frontend_CopyData; |
640 | 0 | break; |
641 | 0 | } |
642 | 0 | case PostgresFrontendMessage_c: |
643 | 0 | { |
644 | 0 | messageType = PostgresMessageType::Frontend_CopyDone; |
645 | 0 | break; |
646 | 0 | } |
647 | 0 | case PostgresFrontendMessage_f: |
648 | 0 | { |
649 | 0 | messageType = PostgresMessageType::Frontend_CopyFail; |
650 | 0 | break; |
651 | 0 | } |
652 | 0 | case PostgresFrontendMessage_X: |
653 | 0 | { |
654 | 0 | messageType = PostgresMessageType::Frontend_Terminate; |
655 | 0 | break; |
656 | 0 | } |
657 | 1 | default: |
658 | 1 | { |
659 | 1 | break; |
660 | 0 | } |
661 | 1 | } |
662 | | |
663 | 1 | return std::unique_ptr<PostgresMessage>(new PostgresMessage(data, messageLength + 1, messageType)); |
664 | 1 | } |
665 | | |
666 | | uint32_t PostgresMessage::getMessageLength() const |
667 | 0 | { |
668 | 0 | if (m_Data == nullptr || m_DataLen < 5) |
669 | 0 | { |
670 | 0 | return 0; |
671 | 0 | } |
672 | | |
673 | 0 | const auto offset = (m_Data[0] == 0) ? 0 : 1; |
674 | 0 | return be32toh(*reinterpret_cast<const uint32_t*>(m_Data + offset)); |
675 | 0 | } |
676 | | |
677 | | std::vector<uint8_t> PostgresMessage::getRawPayload() const |
678 | 0 | { |
679 | 0 | const size_t offset = (m_Data[0] == 0) ? 0 : 1; |
680 | 0 | if (m_DataLen < offset + 4) |
681 | 0 | { |
682 | 0 | return {}; |
683 | 0 | } |
684 | 0 | return { m_Data + offset + 4, m_Data + m_DataLen }; |
685 | 0 | } |
686 | | |
687 | | std::string PostgresParameterStatus::getParameterName() const |
688 | 0 | { |
689 | 0 | constexpr size_t headerLen = 5; |
690 | |
|
691 | 0 | if (m_DataLen < headerLen + 1) |
692 | 0 | { |
693 | 0 | return {}; |
694 | 0 | } |
695 | | |
696 | 0 | const auto* start = reinterpret_cast<const char*>(m_Data + headerLen); |
697 | 0 | const auto* end = start + m_DataLen - headerLen; |
698 | 0 | end = std::find(start, end, '\0'); |
699 | |
|
700 | 0 | return { start, end }; |
701 | 0 | } |
702 | | |
703 | | std::string PostgresParameterStatus::getParameterValue() const |
704 | 0 | { |
705 | 0 | constexpr size_t headerLen = 5; |
706 | |
|
707 | 0 | if (m_DataLen < headerLen + 1) |
708 | 0 | { |
709 | 0 | return ""; |
710 | 0 | } |
711 | | |
712 | 0 | const char* base = reinterpret_cast<const char*>(m_Data) + headerLen; |
713 | 0 | const char* baseEnd = base + m_DataLen - headerLen; |
714 | |
|
715 | 0 | const char* nameEnd = std::find(base, baseEnd, '\0'); |
716 | 0 | if (nameEnd >= baseEnd || nameEnd + 1 >= baseEnd) |
717 | 0 | { |
718 | 0 | return ""; |
719 | 0 | } |
720 | | |
721 | 0 | const char* valueStart = nameEnd + 1; |
722 | 0 | const char* valueEnd = std::find(valueStart, baseEnd, '\0'); |
723 | |
|
724 | 0 | return { valueStart, valueEnd }; |
725 | 0 | } |
726 | | |
727 | | std::string PostgresQueryMessage::getQuery() const |
728 | 0 | { |
729 | 0 | constexpr size_t headerLen = 5; |
730 | |
|
731 | 0 | if (m_DataLen < headerLen + 1) |
732 | 0 | { |
733 | 0 | return ""; |
734 | 0 | } |
735 | | |
736 | 0 | const char* queryStart = reinterpret_cast<const char*>(m_Data) + headerLen; |
737 | 0 | const char* queryEnd = queryStart + m_DataLen - headerLen; |
738 | 0 | queryEnd = std::find(queryStart, queryEnd, '\0'); |
739 | 0 | return { queryStart, queryEnd }; |
740 | 0 | } |
741 | | |
742 | | std::vector<PostgresRowDescriptionMessage::PostgresColumnInfo> PostgresRowDescriptionMessage::getColumnInfos() const |
743 | 0 | { |
744 | 0 | std::vector<PostgresColumnInfo> columns; |
745 | |
|
746 | 0 | constexpr size_t headerLen = 7; |
747 | 0 | if (m_DataLen < headerLen) |
748 | 0 | return columns; |
749 | | |
750 | 0 | uint16_t numFields = be16toh(*reinterpret_cast<const uint16_t*>(m_Data + 5)); |
751 | 0 | if (numFields > 10000) |
752 | 0 | return columns; |
753 | | |
754 | 0 | const char* iter = reinterpret_cast<const char*>(m_Data) + headerLen; |
755 | 0 | const char* end = reinterpret_cast<const char*>(m_Data) + m_DataLen; |
756 | |
|
757 | 0 | for (uint16_t i = 0; i < numFields; ++i) |
758 | 0 | { |
759 | 0 | if (iter >= end) |
760 | 0 | break; |
761 | | |
762 | 0 | PostgresColumnInfo column; |
763 | |
|
764 | 0 | const char* nameEnd = std::find(iter, end, '\0'); |
765 | 0 | if (nameEnd == end) |
766 | 0 | { |
767 | 0 | break; |
768 | 0 | } |
769 | | |
770 | 0 | column.name.assign(iter, nameEnd); |
771 | | |
772 | | // +1 offset because the nameEnd is currently at the null terminator of column name. |
773 | 0 | if (end - nameEnd < static_cast<std::ptrdiff_t>(sizeof(::internal::PostgresColumnFixedData) + 1)) |
774 | 0 | { |
775 | 0 | columns.push_back(column); |
776 | 0 | break; |
777 | 0 | } |
778 | | |
779 | 0 | iter = nameEnd + 1; |
780 | |
|
781 | 0 | const auto* fixedData = reinterpret_cast<const ::internal::PostgresColumnFixedData*>(iter); |
782 | 0 | column.tableOID = be32toh(fixedData->tableOID); |
783 | 0 | column.columnIndex = be16toh(fixedData->columnIndex); |
784 | 0 | column.typeOID = be32toh(fixedData->typeOID); |
785 | 0 | column.typeSize = be16toh(fixedData->typeSize); |
786 | 0 | column.typeModifier = be32toh(fixedData->typeModifier); |
787 | 0 | auto formatCode = be16toh(fixedData->formatCode); |
788 | 0 | column.format = |
789 | 0 | formatCode < 2 ? static_cast<PostgresColumnFormat>(formatCode) : PostgresColumnFormat::Unknown; |
790 | |
|
791 | 0 | iter += sizeof(::internal::PostgresColumnFixedData); |
792 | 0 | columns.push_back(column); |
793 | 0 | } |
794 | |
|
795 | 0 | return columns; |
796 | 0 | } |
797 | | |
798 | | std::vector<PostgresDataRowMessage::ColumnData> PostgresDataRowMessage::getDataRow() const |
799 | 0 | { |
800 | 0 | constexpr size_t headerLen = 7; |
801 | 0 | if (m_DataLen < headerLen) |
802 | 0 | { |
803 | 0 | return {}; |
804 | 0 | } |
805 | | |
806 | 0 | uint16_t numColumns = be16toh(*reinterpret_cast<const uint16_t*>(m_Data + 5)); |
807 | 0 | if (numColumns > 10000) |
808 | 0 | { |
809 | 0 | return {}; |
810 | 0 | } |
811 | | |
812 | 0 | std::vector<ColumnData> rowData; |
813 | |
|
814 | 0 | size_t offset = headerLen; |
815 | |
|
816 | 0 | for (uint16_t i = 0; i < numColumns; ++i) |
817 | 0 | { |
818 | 0 | if (offset + 4 > m_DataLen) |
819 | 0 | { |
820 | 0 | if (offset < m_DataLen) |
821 | 0 | { |
822 | 0 | rowData.emplace_back(nullptr, 0); |
823 | 0 | } |
824 | 0 | break; |
825 | 0 | } |
826 | | |
827 | 0 | const auto colLength = be32toh(*reinterpret_cast<const uint32_t*>(m_Data + offset)); |
828 | 0 | offset += 4; |
829 | |
|
830 | 0 | if (colLength == 0 || colLength == 0xffffffff) |
831 | 0 | { |
832 | 0 | rowData.emplace_back(nullptr, 0); |
833 | 0 | continue; |
834 | 0 | } |
835 | | |
836 | 0 | if (offset + colLength > m_DataLen) |
837 | 0 | { |
838 | 0 | break; |
839 | 0 | } |
840 | | |
841 | 0 | rowData.emplace_back(m_Data + offset, colLength); |
842 | 0 | offset += colLength; |
843 | 0 | } |
844 | |
|
845 | 0 | return rowData; |
846 | 0 | } |
847 | | |
848 | | std::string PostgresDataRowMessage::ColumnData::toHexString() const |
849 | 0 | { |
850 | 0 | return byteArrayToHexString(m_Data, m_DataLen); |
851 | 0 | } |
852 | | |
853 | | std::string PostgresDataRowMessage::ColumnData::toString() const |
854 | 0 | { |
855 | 0 | if (m_Data == nullptr || m_DataLen == 0) |
856 | 0 | { |
857 | 0 | return ""; |
858 | 0 | } |
859 | | |
860 | 0 | return { m_Data, m_Data + m_DataLen }; |
861 | 0 | } |
862 | | |
863 | | const PostgresErrorResponseMessage::FieldMap& PostgresErrorResponseMessage::getFields() const |
864 | 0 | { |
865 | 0 | if (m_FieldsParsed) |
866 | 0 | { |
867 | 0 | return m_Fields; |
868 | 0 | } |
869 | | |
870 | 0 | constexpr auto headerLen = static_cast<size_t>(5); |
871 | 0 | if (m_DataLen < headerLen) |
872 | 0 | { |
873 | 0 | m_FieldsParsed = true; |
874 | 0 | return m_Fields; |
875 | 0 | } |
876 | | |
877 | 0 | auto offset = headerLen; |
878 | 0 | while (offset < m_DataLen) |
879 | 0 | { |
880 | 0 | auto fieldTypeValue = m_Data[offset]; |
881 | 0 | if (fieldTypeValue == 0) |
882 | 0 | { |
883 | 0 | break; |
884 | 0 | } |
885 | | |
886 | 0 | const bool isKnownField = validErrorFieldTypes.find(fieldTypeValue) != validErrorFieldTypes.end(); |
887 | |
|
888 | 0 | offset++; |
889 | 0 | if (offset >= m_DataLen) |
890 | 0 | { |
891 | 0 | break; |
892 | 0 | } |
893 | | |
894 | 0 | auto* valueStart = reinterpret_cast<const char*>(m_Data) + offset; |
895 | 0 | auto remaining = m_DataLen - offset; |
896 | 0 | auto* nullPos = static_cast<const char*>(memchr(valueStart, '\0', remaining)); |
897 | |
|
898 | 0 | std::string fieldValue; |
899 | 0 | if (nullPos != nullptr) |
900 | 0 | { |
901 | 0 | fieldValue.assign(valueStart, nullPos - valueStart); |
902 | 0 | offset = static_cast<size_t>(nullPos - reinterpret_cast<const char*>(m_Data)) + 1; |
903 | 0 | } |
904 | 0 | else |
905 | 0 | { |
906 | 0 | fieldValue.assign(valueStart, remaining); |
907 | 0 | break; |
908 | 0 | } |
909 | | |
910 | 0 | if (isKnownField) |
911 | 0 | { |
912 | 0 | const auto fieldType = static_cast<ErrorField>(fieldTypeValue); |
913 | 0 | m_Fields[fieldType] = std::move(fieldValue); |
914 | 0 | } |
915 | 0 | } |
916 | |
|
917 | 0 | m_FieldsParsed = true; |
918 | 0 | return m_Fields; |
919 | 0 | } |
920 | | |
921 | | uint32_t PostgresStartupMessage::getProtocolVersion() const |
922 | 0 | { |
923 | 0 | if (m_DataLen < MinStartupMessageLength) |
924 | 0 | { |
925 | 0 | return 0; |
926 | 0 | } |
927 | 0 | return *reinterpret_cast<const uint32_t*>(m_Data + ProtocolVersionOffset); |
928 | 0 | } |
929 | | |
930 | | uint16_t PostgresStartupMessage::getProtocolMajorVersion() const |
931 | 0 | { |
932 | 0 | return be16toh(*reinterpret_cast<const uint16_t*>(m_Data + ProtocolVersionOffset)); |
933 | 0 | } |
934 | | |
935 | | uint16_t PostgresStartupMessage::getProtocolMinorVersion() const |
936 | 0 | { |
937 | 0 | return be16toh(*reinterpret_cast<const uint16_t*>(m_Data + ProtocolVersionOffset + 2)); |
938 | 0 | } |
939 | | |
940 | | std::string PostgresStartupMessage::readString(size_t offset) const |
941 | 0 | { |
942 | 0 | if (offset >= m_DataLen) |
943 | 0 | { |
944 | 0 | return ""; |
945 | 0 | } |
946 | | |
947 | 0 | const auto* strStart = m_Data + offset; |
948 | 0 | const auto* dataEnd = m_Data + m_DataLen; |
949 | |
|
950 | 0 | const uint8_t* nullPos = std::find(strStart, dataEnd, uint8_t{ 0 }); |
951 | |
|
952 | 0 | if (nullPos == strStart) |
953 | 0 | { |
954 | 0 | return ""; |
955 | 0 | } |
956 | | |
957 | 0 | return { strStart, nullPos }; |
958 | 0 | } |
959 | | |
960 | | const PostgresStartupMessage::ParameterMap& PostgresStartupMessage::getParameters() const |
961 | 0 | { |
962 | 0 | if (m_ParametersParsed) |
963 | 0 | { |
964 | 0 | return m_Parameters; |
965 | 0 | } |
966 | | |
967 | 0 | if (m_DataLen < MinStartupMessageLength) |
968 | 0 | { |
969 | 0 | m_ParametersParsed = true; |
970 | 0 | return m_Parameters; |
971 | 0 | } |
972 | | |
973 | 0 | size_t offset = MinStartupMessageLength; |
974 | 0 | while (offset < m_DataLen) |
975 | 0 | { |
976 | 0 | auto name = readString(offset); |
977 | 0 | if (name.empty()) |
978 | 0 | { |
979 | 0 | break; |
980 | 0 | } |
981 | 0 | offset += name.length() + 1; |
982 | |
|
983 | 0 | if (offset >= m_DataLen) |
984 | 0 | { |
985 | 0 | break; |
986 | 0 | } |
987 | 0 | auto value = readString(offset); |
988 | 0 | offset += value.length() + 1; |
989 | |
|
990 | 0 | m_Parameters.emplace(std::move(name), std::move(value)); |
991 | 0 | } |
992 | |
|
993 | 0 | m_ParametersParsed = true; |
994 | 0 | return m_Parameters; |
995 | 0 | } |
996 | | |
997 | | std::string PostgresStartupMessage::getParameter(const std::string& name) const |
998 | 0 | { |
999 | 0 | auto parameters = getParameters(); |
1000 | 0 | auto it = parameters.find(name); |
1001 | 0 | if (it != parameters.end()) |
1002 | 0 | { |
1003 | 0 | return it->second; |
1004 | 0 | } |
1005 | 0 | return ""; |
1006 | 0 | } |
1007 | | |
1008 | | PostgresLayer* PostgresLayer::parsePostgresBackendMessages(uint8_t* data, size_t dataLen, Layer* prevLayer, |
1009 | | Packet* packet) |
1010 | 30 | { |
1011 | 30 | return new PostgresLayer(data, dataLen, prevLayer, packet, PostgresMessageOrigin::Backend); |
1012 | 30 | } |
1013 | | |
1014 | | PostgresLayer* PostgresLayer::parsePostgresFrontendMessages(uint8_t* data, size_t dataLen, Layer* prevLayer, |
1015 | | Packet* packet) |
1016 | 41 | { |
1017 | 41 | return new PostgresLayer(data, dataLen, prevLayer, packet, PostgresMessageOrigin::Frontend); |
1018 | 41 | } |
1019 | | |
1020 | | const PointerVector<PostgresMessage>& PostgresLayer::getPostgresMessages() const |
1021 | 28 | { |
1022 | 28 | if (!m_MessagesInitialized) |
1023 | 14 | { |
1024 | 14 | auto parseFunc = (m_MessageOrigin == PostgresMessageOrigin::Backend) |
1025 | 14 | ? &PostgresMessage::parsePostgresBackendMessage |
1026 | 14 | : &PostgresMessage::parsePostgresFrontendMessage; |
1027 | | |
1028 | 14 | auto data = m_Data; |
1029 | 14 | auto dataLen = m_DataLen; |
1030 | | |
1031 | 30 | while (dataLen > 0) |
1032 | 16 | { |
1033 | 16 | auto curMessage = parseFunc(data, dataLen); |
1034 | 16 | if (curMessage == nullptr) |
1035 | 0 | { |
1036 | 0 | break; |
1037 | 0 | } |
1038 | | |
1039 | 16 | dataLen -= curMessage->getTotalMessageLength(); |
1040 | 16 | data += curMessage->getTotalMessageLength(); |
1041 | 16 | m_Messages.pushBack(std::move(curMessage)); |
1042 | 16 | } |
1043 | | |
1044 | 14 | m_MessagesInitialized = true; |
1045 | 14 | } |
1046 | | |
1047 | 28 | return m_Messages; |
1048 | 28 | } |
1049 | | |
1050 | | const PostgresMessage* PostgresLayer::getPostgresMessage(const PostgresMessageType& messageType) const |
1051 | 0 | { |
1052 | 0 | const auto& messages = getPostgresMessages(); |
1053 | 0 | auto it = std::find_if(messages.begin(), messages.end(), [&messageType](const PostgresMessage* message) { |
1054 | 0 | return message->getMessageType() == messageType; |
1055 | 0 | }); |
1056 | |
|
1057 | 0 | return it != messages.end() ? *it : nullptr; |
1058 | 0 | } |
1059 | | |
1060 | | std::string PostgresLayer::toString() const |
1061 | 28 | { |
1062 | 28 | const auto& messages = getPostgresMessages(); |
1063 | 28 | return std::string("PostgreSQL ") + |
1064 | 28 | (m_MessageOrigin == PostgresMessageOrigin::Frontend ? "Frontend" : "Backend") + " Layer, " + |
1065 | 28 | std::to_string(messages.size()) + " message(s)"; |
1066 | 28 | } |
1067 | | } // namespace pcpp |