Coverage Report

Created: 2026-05-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/MySqlLayer.cpp
Line
Count
Source
1
#include "MySqlLayer.h"
2
#include "EndianPortable.h"
3
#include <algorithm>
4
5
namespace pcpp
6
{
7
  namespace
8
  {
9
    constexpr uint8_t ClientSleepCommand = 0x00;
10
    constexpr uint8_t ClientQuitCommand = 0x01;
11
    constexpr uint8_t ClientInitDbCommand = 0x02;
12
    constexpr uint8_t ClientQueryCommand = 0x03;
13
    constexpr uint8_t ClientFieldListCommand = 0x04;
14
    constexpr uint8_t ClientCreateDbCommand = 0x05;
15
    constexpr uint8_t ClientDropDbCommand = 0x06;
16
    constexpr uint8_t ClientRefreshCommand = 0x07;
17
    constexpr uint8_t ClientShutdownCommand = 0x08;
18
    constexpr uint8_t ClientStatisticsCommand = 0x09;
19
    constexpr uint8_t ClientProcessInfoCommand = 0x0a;
20
    constexpr uint8_t ClientConnectCommand = 0x0b;
21
    constexpr uint8_t ClientProcessKillCommand = 0x0c;
22
    constexpr uint8_t ClientDebugCommand = 0x0d;
23
    constexpr uint8_t ClientPingCommand = 0x0e;
24
    constexpr uint8_t ClientTimeCommand = 0x0f;
25
    constexpr uint8_t ClientDelayedInsertCommand = 0x10;
26
    constexpr uint8_t ClientChangeUserCommand = 0x11;
27
    constexpr uint8_t ClientBinlogDumpCommand = 0x12;
28
    constexpr uint8_t ClientTableDumpCommand = 0x13;
29
    constexpr uint8_t ClientConnectOutCommand = 0x14;
30
    constexpr uint8_t ClientRegisterSlaveCommand = 0x15;
31
    constexpr uint8_t ClientStmtPrepareCommand = 0x16;
32
    constexpr uint8_t ClientStmtExecuteCommand = 0x17;
33
    constexpr uint8_t ClientStmtSendLongDataCommand = 0x18;
34
    constexpr uint8_t ClientStmtCloseCommand = 0x19;
35
    constexpr uint8_t ClientStmtResetCommand = 0x1a;
36
    constexpr uint8_t ClientSetOptionCommand = 0x1b;
37
    constexpr uint8_t ClientStmtFetchCommand = 0x1c;
38
    constexpr uint8_t ClientDaemonCommand = 0x1d;
39
    constexpr uint8_t ClientBinlogDumpGtidCommand = 0x1e;
40
    constexpr uint8_t ClientResetConnectionCommand = 0x1f;
41
    constexpr uint8_t ClientCloneCommand = 0x20;
42
43
    constexpr uint8_t ServerOk = 0x00;
44
    constexpr uint8_t ServerError = 0xff;
45
    constexpr uint8_t ServerEof_AuthSwitchRequest = 0xfe;
46
  }  // namespace
47
48
  std::string MySqlMessageType::toString() const
49
0
  {
50
0
    switch (m_Value)
51
0
    {
52
0
    case Client_HandshakeResponse:
53
0
      return "HandshakeResponse";
54
0
    case Client_Sleep:
55
0
      return "COM_SLEEP";
56
0
    case Client_Quit:
57
0
      return "COM_QUIT";
58
0
    case Client_InitDb:
59
0
      return "COM_INIT_DB";
60
0
    case Client_Query:
61
0
      return "COM_QUERY";
62
0
    case Client_FieldList:
63
0
      return "COM_FIELD_LIST";
64
0
    case Client_CreateDb:
65
0
      return "COM_CREATE_DB";
66
0
    case Client_DropDb:
67
0
      return "COM_DROP_DB";
68
0
    case Client_Refresh:
69
0
      return "COM_REFRESH";
70
0
    case Client_Shutdown:
71
0
      return "COM_SHUTDOWN";
72
0
    case Client_Statistics:
73
0
      return "COM_STATISTICS";
74
0
    case Client_ProcessInfo:
75
0
      return "COM_PROCESS_INFO";
76
0
    case Client_Connect:
77
0
      return "COM_CONNECT";
78
0
    case Client_ProcessKill:
79
0
      return "COM_PROCESS_KILL";
80
0
    case Client_Debug:
81
0
      return "COM_DEBUG";
82
0
    case Client_Ping:
83
0
      return "COM_PING";
84
0
    case Client_Time:
85
0
      return "COM_TIME";
86
0
    case Client_DelayedInsert:
87
0
      return "COM_DELAYED_INSERT";
88
0
    case Client_ChangeUser:
89
0
      return "COM_CHANGE_USER";
90
0
    case Client_BinlogDump:
91
0
      return "COM_BINLOG_DUMP";
92
0
    case Client_TableDump:
93
0
      return "COM_TABLE_DUMP";
94
0
    case Client_ConnectOut:
95
0
      return "COM_CONNECT_OUT";
96
0
    case Client_RegisterSlave:
97
0
      return "COM_REGISTER_SLAVE";
98
0
    case Client_StmtPrepare:
99
0
      return "COM_STMT_PREPARE";
100
0
    case Client_StmtExecute:
101
0
      return "COM_STMT_EXECUTE";
102
0
    case Client_StmtFetch:
103
0
      return "COM_STMT_FETCH";
104
0
    case Client_StmtClose:
105
0
      return "COM_STMT_CLOSE";
106
0
    case Client_StmtReset:
107
0
      return "COM_STMT_RESET";
108
0
    case Client_SetOption:
109
0
      return "COM_SET_OPTION";
110
0
    case Client_StmtSendLongData:
111
0
      return "COM_STMT_SEND_LONG_DATA";
112
0
    case Client_Daemon:
113
0
      return "COM_DAEMON";
114
0
    case Client_BinlogDumpGtid:
115
0
      return "COM_BINLOG_DUMP_GTID";
116
0
    case Client_ResetConnection:
117
0
      return "COM_RESET_CONNECTION";
118
0
    case Client_Clone:
119
0
      return "COM_CLONE";
120
0
    case Server_Handshake:
121
0
      return "Handshake";
122
0
    case Server_Ok:
123
0
      return "OK";
124
0
    case Server_AuthSwitchRequest:
125
0
      return "AuthSwitchRequest";
126
0
    case Server_Error:
127
0
      return "Error";
128
0
    case Server_EOF:
129
0
      return "EOF";
130
0
    case Server_Data:
131
0
      return "Data";
132
0
    default:
133
0
      return "Unknown";
134
0
    }
135
0
  }
136
137
  MySqlMessageOrigin MySqlMessageType::getOrigin() const
138
0
  {
139
0
    switch (m_Value)
140
0
    {
141
0
    case Client_HandshakeResponse:
142
0
    case Client_Sleep:
143
0
    case Client_Quit:
144
0
    case Client_InitDb:
145
0
    case Client_Query:
146
0
    case Client_FieldList:
147
0
    case Client_CreateDb:
148
0
    case Client_DropDb:
149
0
    case Client_Refresh:
150
0
    case Client_Shutdown:
151
0
    case Client_Statistics:
152
0
    case Client_ProcessInfo:
153
0
    case Client_Connect:
154
0
    case Client_ProcessKill:
155
0
    case Client_Debug:
156
0
    case Client_Ping:
157
0
    case Client_Time:
158
0
    case Client_DelayedInsert:
159
0
    case Client_ChangeUser:
160
0
    case Client_BinlogDump:
161
0
    case Client_TableDump:
162
0
    case Client_ConnectOut:
163
0
    case Client_RegisterSlave:
164
0
    case Client_StmtPrepare:
165
0
    case Client_StmtExecute:
166
0
    case Client_StmtFetch:
167
0
    case Client_StmtClose:
168
0
    case Client_StmtReset:
169
0
    case Client_SetOption:
170
0
    case Client_StmtSendLongData:
171
0
    case Client_Daemon:
172
0
    case Client_BinlogDumpGtid:
173
0
    case Client_ResetConnection:
174
0
    case Client_Clone:
175
0
      return MySqlMessageOrigin::Client;
176
0
    default:
177
0
      return MySqlMessageOrigin::Server;
178
0
    }
179
0
  }
180
181
  uint32_t MySqlMessage::getMessageLength() const
182
0
  {
183
0
    if (m_Data == nullptr || m_DataLen < basicMessageLength)
184
0
    {
185
0
      return 0;
186
0
    }
187
0
    return static_cast<uint32_t>(m_Data[0]) | (static_cast<uint32_t>(m_Data[1]) << 8) |
188
0
           (static_cast<uint32_t>(m_Data[2]) << 16);
189
0
  }
190
191
  uint8_t MySqlMessage::getPacketNumber() const
192
0
  {
193
0
    if (m_Data == nullptr || m_DataLen < basicMessageLength)
194
0
    {
195
0
      return 0;
196
0
    }
197
0
    return m_Data[packetNumberIndex];
198
0
  }
199
200
  std::unique_ptr<MySqlMessage> MySqlMessage::parseMySqlMessage(const uint8_t* data, size_t dataLen,
201
                                                                MySqlMessageOrigin origin)
202
284
  {
203
284
    if (data == nullptr || dataLen < basicMessageLength)
204
1
    {
205
1
      return nullptr;
206
1
    }
207
208
283
    auto messageLength = static_cast<uint32_t>(data[0]) | (static_cast<uint32_t>(data[1]) << 8) |
209
283
                         (static_cast<uint32_t>(data[2]) << 16);
210
283
    if (dataLen < messageLength + basicMessageLength)
211
184
    {
212
184
      return nullptr;
213
184
    }
214
215
99
    MySqlMessageType messageType = MySqlMessageType::Unknown;
216
217
99
    if (origin == MySqlMessageOrigin::Client)
218
45
    {
219
45
      if (data[packetNumberIndex] == 1)
220
34
      {
221
34
        return std::unique_ptr<MySqlMessage>(
222
34
            new MySqlMessage(data, messageLength + 4, MySqlMessageType::Client_HandshakeResponse, origin));
223
34
      }
224
225
11
      if (dataLen < commandIndex + 1 || messageLength == 0)
226
10
      {
227
10
        return std::unique_ptr<MySqlMessage>(
228
10
            new MySqlMessage(data, messageLength + 4, MySqlMessageType::Unknown, origin));
229
10
      }
230
231
1
      auto command = data[commandIndex];
232
1
      switch (command)
233
1
      {
234
0
      case ClientSleepCommand:
235
0
        messageType = MySqlMessageType::Client_Sleep;
236
0
        break;
237
0
      case ClientQuitCommand:
238
0
        messageType = MySqlMessageType::Client_Quit;
239
0
        break;
240
0
      case ClientInitDbCommand:
241
0
        messageType = MySqlMessageType::Client_InitDb;
242
0
        break;
243
0
      case ClientQueryCommand:
244
0
        return std::unique_ptr<MySqlMessage>(new MySqlQueryMessage(data, messageLength + 4));
245
0
      case ClientFieldListCommand:
246
0
        messageType = MySqlMessageType::Client_FieldList;
247
0
        break;
248
0
      case ClientCreateDbCommand:
249
0
        messageType = MySqlMessageType::Client_CreateDb;
250
0
        break;
251
0
      case ClientDropDbCommand:
252
0
        messageType = MySqlMessageType::Client_DropDb;
253
0
        break;
254
0
      case ClientRefreshCommand:
255
0
        messageType = MySqlMessageType::Client_Refresh;
256
0
        break;
257
0
      case ClientShutdownCommand:
258
0
        messageType = MySqlMessageType::Client_Shutdown;
259
0
        break;
260
0
      case ClientStatisticsCommand:
261
0
        messageType = MySqlMessageType::Client_Statistics;
262
0
        break;
263
0
      case ClientProcessInfoCommand:
264
0
        messageType = MySqlMessageType::Client_ProcessInfo;
265
0
        break;
266
0
      case ClientConnectCommand:
267
0
        messageType = MySqlMessageType::Client_Connect;
268
0
        break;
269
0
      case ClientProcessKillCommand:
270
0
        messageType = MySqlMessageType::Client_ProcessKill;
271
0
        break;
272
0
      case ClientDebugCommand:
273
0
        messageType = MySqlMessageType::Client_Debug;
274
0
        break;
275
0
      case ClientPingCommand:
276
0
        messageType = MySqlMessageType::Client_Ping;
277
0
        break;
278
0
      case ClientTimeCommand:
279
0
        messageType = MySqlMessageType::Client_Time;
280
0
        break;
281
0
      case ClientDelayedInsertCommand:
282
0
        messageType = MySqlMessageType::Client_DelayedInsert;
283
0
        break;
284
0
      case ClientChangeUserCommand:
285
0
        messageType = MySqlMessageType::Client_ChangeUser;
286
0
        break;
287
0
      case ClientBinlogDumpCommand:
288
0
        messageType = MySqlMessageType::Client_BinlogDump;
289
0
        break;
290
0
      case ClientTableDumpCommand:
291
0
        messageType = MySqlMessageType::Client_TableDump;
292
0
        break;
293
0
      case ClientConnectOutCommand:
294
0
        messageType = MySqlMessageType::Client_ConnectOut;
295
0
        break;
296
0
      case ClientRegisterSlaveCommand:
297
0
        messageType = MySqlMessageType::Client_RegisterSlave;
298
0
        break;
299
0
      case ClientStmtPrepareCommand:
300
0
        messageType = MySqlMessageType::Client_StmtPrepare;
301
0
        break;
302
0
      case ClientStmtExecuteCommand:
303
0
        messageType = MySqlMessageType::Client_StmtExecute;
304
0
        break;
305
0
      case ClientStmtSendLongDataCommand:
306
0
        messageType = MySqlMessageType::Client_StmtSendLongData;
307
0
        break;
308
0
      case ClientStmtCloseCommand:
309
0
        messageType = MySqlMessageType::Client_StmtClose;
310
0
        break;
311
0
      case ClientStmtResetCommand:
312
0
        messageType = MySqlMessageType::Client_StmtReset;
313
0
        break;
314
0
      case ClientSetOptionCommand:
315
0
        messageType = MySqlMessageType::Client_SetOption;
316
0
        break;
317
0
      case ClientStmtFetchCommand:
318
0
        messageType = MySqlMessageType::Client_StmtFetch;
319
0
        break;
320
0
      case ClientDaemonCommand:
321
0
        messageType = MySqlMessageType::Client_Daemon;
322
0
        break;
323
0
      case ClientBinlogDumpGtidCommand:
324
0
        messageType = MySqlMessageType::Client_BinlogDumpGtid;
325
0
        break;
326
0
      case ClientResetConnectionCommand:
327
0
        messageType = MySqlMessageType::Client_ResetConnection;
328
0
        break;
329
0
      case ClientCloneCommand:
330
0
        messageType = MySqlMessageType::Client_Clone;
331
0
        break;
332
1
      default:
333
1
        break;
334
1
      }
335
1
    }
336
54
    else  // Server message
337
54
    {
338
54
      if (data[packetNumberIndex] == 0)
339
19
      {
340
19
        return std::unique_ptr<MySqlMessage>(
341
19
            new MySqlMessage(data, messageLength + 4, MySqlMessageType::Server_Handshake, origin));
342
19
      }
343
344
35
      if (dataLen < commandIndex + 1 || messageLength == 0)
345
0
      {
346
0
        return std::unique_ptr<MySqlMessage>(
347
0
            new MySqlMessage(data, messageLength + 4, MySqlMessageType::Unknown, origin));
348
0
      }
349
350
35
      auto firstByte = data[commandIndex];
351
35
      switch (firstByte)
352
35
      {
353
0
      case ServerOk:
354
0
        messageType = MySqlMessageType::Server_Ok;
355
0
        break;
356
0
      case ServerError:
357
0
        return std::unique_ptr<MySqlMessage>(new MySqlErrorMessage(data, messageLength + 4));
358
2
      case ServerEof_AuthSwitchRequest:
359
2
      {
360
2
        messageLength = dataLen - basicMessageLength;
361
2
        if (messageLength < 9)
362
2
        {
363
2
          messageType = MySqlMessageType::Server_EOF;
364
2
        }
365
0
        else
366
0
        {
367
0
          messageType = MySqlMessageType::Server_AuthSwitchRequest;
368
0
        }
369
2
        break;
370
0
      }
371
33
      default:
372
33
        return std::unique_ptr<MySqlMessage>(
373
33
            new MySqlMessage(data, messageLength + basicMessageLength, MySqlMessageType::Server_Data, origin));
374
35
      }
375
35
    }
376
377
3
    return std::unique_ptr<MySqlCommandMessage>(
378
3
        new MySqlCommandMessage(data, messageLength + basicMessageLength, messageType, origin));
379
99
  }
380
381
  std::vector<uint8_t> MySqlMessage::getRawPayload() const
382
0
  {
383
0
    if (m_DataLen < basicMessageLength)
384
0
    {
385
0
      return {};
386
0
    }
387
0
    return { m_Data + basicMessageLength, m_Data + m_DataLen };
388
0
  }
389
390
  uint32_t MySqlCommandMessage::getMessageLength() const
391
0
  {
392
0
    auto length = MySqlMessage::getMessageLength();
393
0
    if (length > 0)
394
0
    {
395
0
      length--;
396
0
    }
397
398
0
    return length;
399
0
  }
400
401
  std::vector<uint8_t> MySqlCommandMessage::getRawPayload() const
402
0
  {
403
0
    if (m_DataLen < basicMessageLength)
404
0
    {
405
0
      return {};
406
0
    }
407
0
    return { m_Data + basicMessageLength + commandLength, m_Data + m_DataLen };
408
0
  }
409
410
  std::string MySqlQueryMessage::getQuery() const
411
0
  {
412
0
    if (!m_Data || m_DataLen < statementIndex + 1)
413
0
    {
414
0
      return {};
415
0
    }
416
417
0
    return { reinterpret_cast<const char*>(m_Data + statementIndex), m_DataLen - statementIndex };
418
0
  }
419
420
  uint16_t MySqlErrorMessage::getErrorCode() const
421
0
  {
422
0
    if (!m_Data || m_DataLen < errorCodeIndex + sizeof(uint16_t))
423
0
    {
424
0
      return 0;
425
0
    }
426
427
0
    return *reinterpret_cast<const uint16_t*>(m_Data + errorCodeIndex);
428
0
  }
429
430
  std::string MySqlErrorMessage::getSqlState() const
431
0
  {
432
0
    if (!m_Data || m_DataLen < sqlStateIndex + sqlStateSize)
433
0
    {
434
0
      return {};
435
0
    }
436
437
0
    return { reinterpret_cast<const char*>(m_Data + sqlStateIndex), sqlStateSize };
438
0
  }
439
440
  std::string MySqlErrorMessage::getErrorMessage() const
441
0
  {
442
0
    if (!m_Data || m_DataLen < errorMessageIndex + 1)
443
0
    {
444
0
      return {};
445
0
    }
446
447
0
    return { reinterpret_cast<const char*>(m_Data + errorMessageIndex), m_DataLen - errorMessageIndex };
448
0
  }
449
450
  MySqlLayer* MySqlLayer::parseMySqlClientMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
451
740
  {
452
740
    return new MySqlLayer(data, dataLen, prevLayer, packet, MySqlMessageOrigin::Client);
453
740
  }
454
455
  MySqlLayer* MySqlLayer::parseMySqlServerMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
456
470
  {
457
470
    return new MySqlLayer(data, dataLen, prevLayer, packet, MySqlMessageOrigin::Server);
458
470
  }
459
460
  const PointerVector<MySqlMessage>& MySqlLayer::getMySqlMessages() const
461
482
  {
462
482
    if (!m_MessagesInitialized)
463
241
    {
464
241
      auto data = m_Data;
465
241
      auto dataLen = m_DataLen;
466
467
340
      while (dataLen > 0)
468
284
      {
469
284
        auto curMessage = MySqlMessage::parseMySqlMessage(data, dataLen, m_MessageOrigin);
470
284
        if (curMessage == nullptr)
471
185
        {
472
185
          break;
473
185
        }
474
475
99
        dataLen -= curMessage->getTotalMessageLength();
476
99
        data += curMessage->getTotalMessageLength();
477
99
        m_Messages.pushBack(std::move(curMessage));
478
99
      }
479
480
241
      m_MessagesInitialized = true;
481
241
    }
482
483
482
    return m_Messages;
484
482
  }
485
486
  const MySqlMessage* MySqlLayer::getMySqlMessage(const MySqlMessageType& messageType) const
487
0
  {
488
0
    const auto& messages = getMySqlMessages();
489
0
    auto it = std::find_if(messages.begin(), messages.end(), [&messageType](const MySqlMessage* message) {
490
0
      return message->getMessageType() == messageType;
491
0
    });
492
493
0
    return it != messages.end() ? *it : nullptr;
494
0
  }
495
496
  std::string MySqlLayer::toString() const
497
482
  {
498
482
    const auto& messages = getMySqlMessages();
499
482
    return std::string("MySQL ") + (m_MessageOrigin == MySqlMessageOrigin::Client ? "Client" : "Server") +
500
482
           " Layer, " + std::to_string(messages.size()) + " message(s)";
501
482
  }
502
}  // namespace pcpp