Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-mysqlx.c
Line
Count
Source
1
/*
2
 * Wireshark - Network traffic analyzer
3
 * By Gerald Combs <gerald@wireshark.org>
4
 * Copyright 1998 Gerald Combs
5
 *
6
 * SPDX-License-Identifier: GPL-2.0-or-later
7
 *
8
 * Dissector for MySQL X Protocol by Daniƫl van Eeden <wireshark@myname.nl>
9
 * https://dev.mysql.com/doc/dev/mysql-server/latest/page_mysqlx_protocol.html
10
 */
11
12
#include "config.h"
13
#include "packet-tcp.h"
14
#include <epan/expert.h>
15
#include <epan/packet.h>
16
17
/* From ClientMessages in mysqlx.proto, but with CLIENT_ prefix added */
18
0
#define CLIENT_CON_CAPABILITIES_GET 1
19
0
#define CLIENT_CON_CAPABILITIES_SET 2
20
0
#define CLIENT_CON_CLOSE 3
21
0
#define CLIENT_SESS_AUTHENTICATE_START 4
22
0
#define CLIENT_SESS_AUTHENTICATE_CONTINUE 5
23
0
#define CLIENT_SESS_RESET 6
24
0
#define CLIENT_SESS_CLOSE 7
25
0
#define CLIENT_SQL_STMT_EXECUTE 12
26
0
#define CLIENT_CRUD_FIND 17
27
0
#define CLIENT_CRUD_INSERT 18
28
0
#define CLIENT_CRUD_UPDATE 19
29
0
#define CLIENT_CRUD_DELETE 20
30
0
#define CLIENT_EXPECT_OPEN 24
31
0
#define CLIENT_EXPECT_CLOSE 25
32
0
#define CLIENT_CRUD_CREATE_VIEW 30
33
0
#define CLIENT_CRUD_MODIFY_VIEW 31
34
0
#define CLIENT_CRUD_DROP_VIEW 32
35
0
#define CLIENT_PREPARE_PREPARE 40
36
0
#define CLIENT_PREPARE_EXECUTE 41
37
0
#define CLIENT_PREPARE_DEALLOCATE 42
38
0
#define CLIENT_CURSOR_OPEN 43
39
0
#define CLIENT_CURSOR_CLOSE 44
40
0
#define CLIENT_CURSOR_FETCH 45
41
0
#define CLIENT_COMPRESSION 46
42
43
/* From ServerMessages in mysqlx.proto, but with SERVER_ prefix added */
44
0
#define SERVER_OK 0
45
0
#define SERVER_ERROR 1
46
0
#define SERVER_CONN_CAPABILITIES 2
47
0
#define SERVER_SESS_AUTHENTICATE_CONTINUE 3
48
0
#define SERVER_SESS_AUTHENTICATE_OK 4
49
0
#define SERVER_NOTICE 11
50
0
#define SERVER_RESULTSET_COLUMN_META_DATA 12
51
0
#define SERVER_RESULTSET_ROW 13
52
0
#define SERVER_RESULTSET_FETCH_DONE 14
53
0
#define SERVER_RESULTSET_FETCH_SUSPENDED 15
54
0
#define SERVER_RESULTSET_FETCH_DONE_MORE_RESULTSETS 16
55
0
#define SERVER_SQL_STMT_EXECUTE_OK 17
56
0
#define SERVER_RESULTSET_FETCH_DONE_MORE_OUT_PARAMS 18
57
0
#define SERVER_COMPRESSION 19
58
59
2
#define MYSQLX_HEADER_LENGTH 5
60
61
static const value_string message_type_vals_client[] = {
62
    {CLIENT_CON_CAPABILITIES_GET, "Mysqlx.Connection.CapabilitiesGet"},
63
    {CLIENT_CON_CAPABILITIES_SET, "Mysqlx.Connection.CapabilitiesSet"},
64
    {CLIENT_CON_CLOSE, "Mysqlx.Connection.Close"},
65
    {CLIENT_SESS_AUTHENTICATE_START, "Mysqlx.Session.AuthenticateStart"},
66
    {CLIENT_SESS_AUTHENTICATE_CONTINUE, "Mysqlx.Session.AuthenticateContinue"},
67
    {CLIENT_SESS_RESET, "Mysqlx.Session.Reset"},
68
    {CLIENT_SESS_CLOSE, "Mysqlx.Session.Close"},
69
    {CLIENT_SQL_STMT_EXECUTE, "Mysqlx.Sql.StmtExecute"},
70
    {CLIENT_CRUD_FIND, "Mysqlx.Crud.Find"},
71
    {CLIENT_CRUD_INSERT, "Mysqlx.Crud.Insert"},
72
    {CLIENT_CRUD_UPDATE, "Mysqlx.Crud.Update"},
73
    {CLIENT_CRUD_DELETE, "Mysqlx.Crud.Delete"},
74
    {CLIENT_EXPECT_OPEN, "Mysqlx.Expect.Open"},
75
    {CLIENT_EXPECT_CLOSE, "Mysqlx.Expect.Close"},
76
    {CLIENT_CRUD_CREATE_VIEW, "Mysqlx.Crud.CreateView"},
77
    {CLIENT_CRUD_MODIFY_VIEW, "Mysqlx.Crud.ModifyView"},
78
    {CLIENT_CRUD_DROP_VIEW, "Mysqlx.Crud.DropView"},
79
    {CLIENT_PREPARE_PREPARE, "Mysqlx.Prepare.Prepare"},
80
    {CLIENT_PREPARE_EXECUTE, "Mysqlx.Prepare.Execute"},
81
    {CLIENT_PREPARE_DEALLOCATE, "Mysqlx.Prepare.Deallocate"},
82
    {CLIENT_CURSOR_OPEN, "Mysqlx.Cursor.Open"},
83
    {CLIENT_CURSOR_CLOSE, "Mysqlx.Cursor.Close"},
84
    {CLIENT_CURSOR_FETCH, "Mysqlx.Cursor.Fetch"},
85
    {CLIENT_COMPRESSION, "Mysqlx.Connection.Compression"},
86
    {0, NULL}};
87
88
static const value_string message_type_vals_server[] = {
89
    {SERVER_OK, "Mysqlx.Ok"},
90
    {SERVER_ERROR, "Mysqlx.Error"},
91
    {SERVER_CONN_CAPABILITIES, "Mysqlx.Connection.Capabilities"},
92
    {SERVER_SESS_AUTHENTICATE_CONTINUE, "Mysqlx.Session.AuthenticateContinue"},
93
    {SERVER_SESS_AUTHENTICATE_OK, "Mysqlx.Session.AuthenticateOk"},
94
    {SERVER_NOTICE, "Mysqlx.Notice.Frame"},
95
    {SERVER_RESULTSET_COLUMN_META_DATA, "Mysqlx.Resultset.ColumnMetaData"},
96
    {SERVER_RESULTSET_ROW, "Mysqlx.Resultset.Row"},
97
    {SERVER_RESULTSET_FETCH_DONE, "Mysqlx.Resultset.FetchDone"},
98
    {SERVER_RESULTSET_FETCH_SUSPENDED, "Mysqlx.Resultset.FetchSuspended"},
99
    {SERVER_RESULTSET_FETCH_DONE_MORE_RESULTSETS,
100
     "Mysqlx.Resultset.FetchDoneMoreResultsets"},
101
    {SERVER_SQL_STMT_EXECUTE_OK, "Mysqlx.Sql.StmtExecuteOk"},
102
    {SERVER_RESULTSET_FETCH_DONE_MORE_OUT_PARAMS,
103
     "Mysqlx.Resultset.FetchDoneMoreOutParams"},
104
    {SERVER_COMPRESSION, "Mysqlx.Connection.Compression"},
105
    {0, NULL}};
106
107
static dissector_handle_t mysqlx_handle, mysqlx_protobuf_handle;
108
109
static int ett_mysqlx;
110
static int hf_mysqlx_packet_length;
111
static int hf_mysqlx_message_type;
112
static int proto_mysqlx;
113
static expert_field ei_mysqlx_unknown_message_type;
114
115
static int dissect_mysqlx_pdu(tvbuff_t *tvb, packet_info *pinfo,
116
1
                              proto_tree *tree, void *data _U_) {
117
1
  proto_tree *mysqlx_tree = NULL;
118
1
  proto_item *ti;
119
1
  tvbuff_t *next_tvb;
120
1
  int offset = 0;
121
1
  uint8_t message_type;
122
1
  char *message = NULL;
123
124
1
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "mysqlx");
125
1
  ti = proto_tree_add_item(tree, proto_mysqlx, tvb, offset, -1, ENC_NA);
126
1
  mysqlx_tree = proto_item_add_subtree(ti, ett_mysqlx);
127
1
  proto_tree_add_item(mysqlx_tree, hf_mysqlx_packet_length, tvb, offset, 4,
128
1
                      ENC_LITTLE_ENDIAN);
129
1
  offset += 4;
130
131
1
  proto_tree_add_item_ret_uint8(mysqlx_tree, hf_mysqlx_message_type, tvb,
132
1
                                offset, 1, ENC_NA, &message_type);
133
1
  offset++;
134
135
1
  if (pinfo->destport == pinfo->match_uint) {
136
1
    proto_item_append_text(ti, " (%s)",
137
1
                           val_to_str(pinfo->pool, message_type,
138
1
                                      message_type_vals_client, "Unknown(%u)"));
139
1
    switch (message_type) {
140
0
    case CLIENT_CON_CAPABILITIES_GET:
141
0
      message = "message,Mysqlx.Connection.CapabilitiesGet";
142
0
      break;
143
0
    case CLIENT_CON_CAPABILITIES_SET:
144
0
      message = "message,Mysqlx.Connection.CapabilitiesSet";
145
0
      break;
146
0
    case CLIENT_CON_CLOSE:
147
0
      message = "message,Mysqlx.Connection.Close";
148
0
      break;
149
0
    case CLIENT_SESS_AUTHENTICATE_START:
150
0
      message = "message,Mysqlx.Session.AuthenticateStart";
151
0
      break;
152
0
    case CLIENT_SESS_AUTHENTICATE_CONTINUE:
153
0
      message = "message,Mysqlx.Session.AuthenticateContinue";
154
0
      break;
155
0
    case CLIENT_SESS_RESET:
156
0
      message = "message,Mysqlx.Session.Reset";
157
0
      break;
158
0
    case CLIENT_SESS_CLOSE:
159
0
      message = "message,Mysqlx.Session.Close";
160
0
      break;
161
0
    case CLIENT_SQL_STMT_EXECUTE:
162
0
      message = "message,Mysqlx.Sql.StmtExecute";
163
0
      break;
164
0
    case CLIENT_CRUD_FIND:
165
0
      message = "message,Mysqlx.Crud.Find";
166
0
      break;
167
0
    case CLIENT_CRUD_INSERT:
168
0
      message = "message,Mysqlx.Crud.Insert";
169
0
      break;
170
0
    case CLIENT_CRUD_UPDATE:
171
0
      message = "message,Mysqlx.Crud.Update";
172
0
      break;
173
0
    case CLIENT_CRUD_DELETE:
174
0
      message = "message,Mysqlx.Crud.Delete";
175
0
      break;
176
0
    case CLIENT_EXPECT_OPEN:
177
0
      message = "message,Mysqlx.Expect.Open";
178
0
      break;
179
0
    case CLIENT_EXPECT_CLOSE:
180
0
      message = "message,Mysqlx.Expect.Close";
181
0
      break;
182
0
    case CLIENT_CRUD_CREATE_VIEW:
183
0
      message = "message,Mysqlx.Crud.CreateView";
184
0
      break;
185
0
    case CLIENT_CRUD_MODIFY_VIEW:
186
0
      message = "message,Mysqlx.Crud.ModifyView";
187
0
      break;
188
0
    case CLIENT_CRUD_DROP_VIEW:
189
0
      message = "message,Mysqlx.Crud.DropView";
190
0
      break;
191
0
    case CLIENT_PREPARE_PREPARE:
192
0
      message = "message,Mysqlx.Prepare.Prepare";
193
0
      break;
194
0
    case CLIENT_PREPARE_EXECUTE:
195
0
      message = "message,Mysqlx.Prepare.Execute";
196
0
      break;
197
0
    case CLIENT_PREPARE_DEALLOCATE:
198
0
      message = "message,Mysqlx.Prepare.Deallocate";
199
0
      break;
200
0
    case CLIENT_CURSOR_OPEN:
201
0
      message = "message,Mysqlx.Cursor.Open";
202
0
      break;
203
0
    case CLIENT_CURSOR_CLOSE:
204
0
      message = "message,Mysqlx.Cursor.Close";
205
0
      break;
206
0
    case CLIENT_CURSOR_FETCH:
207
0
      message = "message,Mysqlx.Cursor.Fetch";
208
0
      break;
209
0
    case CLIENT_COMPRESSION:
210
0
      message = "message,Mysqlx.Connection.Compression";
211
0
      break;
212
1
    }
213
1
  } else {
214
0
    proto_item_append_text(ti, " (%s)",
215
0
                           val_to_str(pinfo->pool, message_type,
216
0
                                      message_type_vals_server, "Unknown(%u)"));
217
0
    switch (message_type) {
218
0
    case SERVER_OK:
219
0
      message = "message,Mysqlx.Ok";
220
0
      break;
221
0
    case SERVER_ERROR:
222
0
      message = "message,Mysqlx.Error";
223
0
      break;
224
0
    case SERVER_CONN_CAPABILITIES:
225
0
      message = "message,Mysqlx.Connection.Capabilities";
226
0
      break;
227
0
    case SERVER_SESS_AUTHENTICATE_CONTINUE:
228
0
      message = "message,Mysqlx.Session.AuthenticateContinue";
229
0
      break;
230
0
    case SERVER_SESS_AUTHENTICATE_OK:
231
0
      message = "message,Mysqlx.Session.AuthenticateOk";
232
0
      break;
233
0
    case SERVER_NOTICE:
234
0
      message = "message,Mysqlx.Notice.Frame";
235
0
      break;
236
0
    case SERVER_RESULTSET_COLUMN_META_DATA:
237
0
      message = "message,Mysqlx.Resultset.ColumnMetaData";
238
0
      break;
239
0
    case SERVER_RESULTSET_ROW:
240
0
      message = "message,Mysqlx.Resultset.Row";
241
0
      break;
242
0
    case SERVER_RESULTSET_FETCH_DONE:
243
0
      message = "message,Mysqlx.Resultset.FetchDone";
244
0
      break;
245
0
    case SERVER_RESULTSET_FETCH_SUSPENDED:
246
0
      message = "message,Mysqlx.Resultset.FetchSuspended";
247
0
      break;
248
0
    case SERVER_RESULTSET_FETCH_DONE_MORE_RESULTSETS:
249
0
      message = "message,Mysqlx.Resultset.FetchDoneMoreResultsets";
250
0
      break;
251
0
    case SERVER_SQL_STMT_EXECUTE_OK:
252
0
      message = "message,Mysqlx.Sql.StmtExecuteOk";
253
0
      break;
254
0
    case SERVER_RESULTSET_FETCH_DONE_MORE_OUT_PARAMS:
255
0
      message = "message,Mysqlx.Resultset.FetchDoneMoreOutParams";
256
0
      break;
257
0
    case SERVER_COMPRESSION:
258
0
      message = "message,Mysqlx.Connection.Compression";
259
0
      break;
260
0
    }
261
0
  }
262
1
  if (message == NULL) {
263
1
    expert_add_info_format(pinfo, mysqlx_tree, &ei_mysqlx_unknown_message_type,
264
1
                           "can not link message type %d to a message",
265
1
                           message_type);
266
1
  } else {
267
0
    next_tvb = tvb_new_subset_length(
268
0
        tvb, offset, tvb_reported_length_remaining(tvb, offset));
269
0
    return offset + call_dissector_with_data(mysqlx_protobuf_handle, next_tvb,
270
0
                                             pinfo, mysqlx_tree, message);
271
0
  }
272
1
  return offset;
273
1
}
274
275
static unsigned get_mysqlx_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb,
276
1
                                   int offset, void *data _U_) {
277
1
  unsigned len =
278
1
      MYSQLX_HEADER_LENGTH + tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN) - 1;
279
1
  return len;
280
1
}
281
282
static int dissect_mysqlx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
283
1
                          void *data) {
284
1
  col_clear(pinfo->cinfo, COL_INFO);
285
1
  tcp_dissect_pdus(tvb, pinfo, tree, TRUE, MYSQLX_HEADER_LENGTH,
286
1
                   get_mysqlx_pdu_len, dissect_mysqlx_pdu, data);
287
1
  return tvb_reported_length(tvb);
288
1
}
289
290
16
void proto_register_mysqlx(void) {
291
16
  static hf_register_info hf[] = {
292
16
      {&hf_mysqlx_packet_length,
293
16
       {"Packet Length", "mysqlx.packet_length", FT_UINT32, BASE_DEC, NULL, 0x0,
294
16
        NULL, HFILL}},
295
16
      {&hf_mysqlx_message_type,
296
16
       {"Message Type", "mysqlx.message_type", FT_UINT8, BASE_DEC, NULL, 0x0,
297
16
        NULL, HFILL}},
298
16
  };
299
300
16
  static int *ett[] = {
301
16
      &ett_mysqlx,
302
16
  };
303
304
16
  static ei_register_info ei[] = {
305
16
      {&ei_mysqlx_unknown_message_type,
306
16
       {"mysqlx.unknown_message_type", PI_UNDECODED, PI_WARN,
307
16
        "unknown message type", EXPFILL}},
308
16
  };
309
310
16
  proto_mysqlx =
311
16
      proto_register_protocol("MySQL X Protocol", "MySQLX", "mysqlx");
312
16
  proto_register_field_array(proto_mysqlx, hf, array_length(hf));
313
16
  proto_register_subtree_array(ett, array_length(ett));
314
16
  expert_module_t *expert_mysqlx = expert_register_protocol(proto_mysqlx);
315
16
  expert_register_field_array(expert_mysqlx, ei, array_length(ei));
316
16
  mysqlx_handle = register_dissector("mysqlx", dissect_mysqlx, proto_mysqlx);
317
16
}
318
319
16
void proto_reg_handoff_mysqlx(void) {
320
16
  mysqlx_protobuf_handle =
321
16
      find_dissector_add_dependency("protobuf", proto_mysqlx);
322
16
  dissector_add_uint_with_preference("tcp.port", 33060, mysqlx_handle);
323
16
}