Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-gearman.c
Line
Count
Source
1
/* packet-gearman.c
2
 * Routines for Gearman protocol packet disassembly
3
 * By Flier Lu <flier.lu@gmail.com>
4
 * Copyright 2010 Flier Lu
5
 *
6
 * Gearman Protocol
7
 * ----------------
8
 * http://gearman.org/protocol/
9
 *
10
 * Wireshark - Network traffic analyzer
11
 * By Gerald Combs <gerald@wireshark.org>
12
 * Copyright 1998 Gerald Combs
13
 *
14
 * SPDX-License-Identifier: GPL-2.0-or-later
15
 */
16
17
#include "config.h"
18
19
#include <epan/packet.h>
20
#include <epan/prefs.h>
21
#include <epan/expert.h>
22
#include <wsutil/array.h>
23
#include "packet-tcp.h"
24
25
void proto_register_gearman(void);
26
void proto_reg_handoff_gearman(void);
27
28
static dissector_handle_t gearman_handle;
29
30
static int proto_gearman;
31
32
static int hf_gearman_mgr_cmd;
33
static int hf_gearman_magic_code;
34
static int hf_gearman_pkt_type;
35
static int hf_gearman_data_size;
36
static int hf_gearman_data_content;
37
static int hf_gearman_option_name;
38
static int hf_gearman_func_name;
39
static int hf_gearman_func_namez;
40
static int hf_gearman_client_id;
41
static int hf_gearman_client_count;
42
static int hf_gearman_uniq_id;
43
static int hf_gearman_uniq_idz;
44
static int hf_gearman_argument;
45
static int hf_gearman_job_handle;
46
static int hf_gearman_job_handlez;
47
static int hf_gearman_complete_numerator;
48
static int hf_gearman_complete_denominator;
49
static int hf_gearman_submit_job_sched_minute;
50
static int hf_gearman_submit_job_sched_hour;
51
static int hf_gearman_submit_job_sched_day_of_month;
52
static int hf_gearman_submit_job_sched_month;
53
static int hf_gearman_submit_job_sched_day_of_week;
54
static int hf_gearman_submit_job_epoch_time;
55
static int hf_gearman_reducer;
56
static int hf_gearman_result;
57
static int hf_gearman_known_status;
58
static int hf_gearman_running_status;
59
static int hf_gearman_timeout_value;
60
static int hf_gearman_echo_text;
61
static int hf_gearman_err_code;
62
static int hf_gearman_err_text;
63
64
static int ett_gearman;
65
static int ett_gearman_command;
66
static int ett_gearman_content;
67
68
static expert_field ei_gearman_pkt_type_unknown;
69
70
static bool gearman_desegment  = true;
71
72
static const int GEARMAN_COMMAND_HEADER_SIZE = 12;
73
static const int GEARMAN_PORT = 4730;
74
static const unsigned char *GEARMAN_MAGIC_CODE_REQUEST = (uint8_t*)"\0REQ";
75
static const unsigned char *GEARMAN_MAGIC_CODE_RESPONSE = (uint8_t*)"\0RES";
76
77
static const char *GEARMAN_MGR_CMDS[] = {
78
  "workers",
79
  "status",
80
  "maxqueue",
81
  "shutdown",
82
  "version"
83
};
84
85
static const unsigned GEARMAN_MGR_CMDS_COUNT = array_length(GEARMAN_MGR_CMDS);
86
87
typedef enum
88
{
89
  GEARMAN_COMMAND_TEXT,
90
  GEARMAN_COMMAND_CAN_DO,              /* W->J: FUNC */
91
  GEARMAN_COMMAND_CANT_DO,             /* W->J: FUNC */
92
  GEARMAN_COMMAND_RESET_ABILITIES,     /* W->J: -- */
93
  GEARMAN_COMMAND_PRE_SLEEP,           /* W->J: -- */
94
  GEARMAN_COMMAND_UNUSED,
95
  GEARMAN_COMMAND_NOOP,                /* J->W: -- */
96
  GEARMAN_COMMAND_SUBMIT_JOB,          /* C->J: FUNC[0]UNIQ[0]ARGS */
97
  GEARMAN_COMMAND_JOB_CREATED,         /* J->C: HANDLE  */
98
  GEARMAN_COMMAND_GRAB_JOB,            /* W->J: --  */
99
  GEARMAN_COMMAND_NO_JOB,              /* J->W: -- */
100
  GEARMAN_COMMAND_JOB_ASSIGN,          /* J->W: HANDLE[0]FUNC[0]ARG  */
101
  GEARMAN_COMMAND_WORK_STATUS,         /* W->J/C: HANDLE[0]NUMERATOR[0]DENOMINATOR */
102
  GEARMAN_COMMAND_WORK_COMPLETE,       /* W->J/C: HANDLE[0]RES */
103
  GEARMAN_COMMAND_WORK_FAIL,           /* W->J/C: HANDLE */
104
  GEARMAN_COMMAND_GET_STATUS,          /* C->J: HANDLE */
105
  GEARMAN_COMMAND_ECHO_REQ,            /* ?->J: TEXT */
106
  GEARMAN_COMMAND_ECHO_RES,            /* J->?: TEXT */
107
  GEARMAN_COMMAND_SUBMIT_JOB_BG,       /* C->J: FUNC[0]UNIQ[0]ARGS  */
108
  GEARMAN_COMMAND_ERROR,               /* J->?: ERRCODE[0]ERR_TEXT */
109
  GEARMAN_COMMAND_STATUS_RES,          /* C->J: HANDLE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM */
110
  GEARMAN_COMMAND_SUBMIT_JOB_HIGH,     /* C->J: FUNC[0]UNIQ[0]ARGS  */
111
  GEARMAN_COMMAND_SET_CLIENT_ID,       /* W->J: [RANDOM_STRING_NO_WHITESPACE] */
112
  GEARMAN_COMMAND_CAN_DO_TIMEOUT,      /* W->J: FUNC[0]TIMEOUT */
113
  GEARMAN_COMMAND_ALL_YOURS,
114
  GEARMAN_COMMAND_WORK_EXCEPTION,
115
  GEARMAN_COMMAND_OPTION_REQ,
116
  GEARMAN_COMMAND_OPTION_RES,
117
  GEARMAN_COMMAND_WORK_DATA,
118
  GEARMAN_COMMAND_WORK_WARNING,
119
  GEARMAN_COMMAND_GRAB_JOB_UNIQ,
120
  GEARMAN_COMMAND_JOB_ASSIGN_UNIQ,
121
  GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG,
122
  GEARMAN_COMMAND_SUBMIT_JOB_LOW,
123
  GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG,
124
  GEARMAN_COMMAND_SUBMIT_JOB_SCHED,
125
  GEARMAN_COMMAND_SUBMIT_JOB_EPOCH,
126
  GEARMAN_COMMAND_SUBMIT_REDUCE_JOB,      /* C->J: FUNC[0]UNIQ[0]REDUCER[0]UNUSED[0]ARGS */
127
  GEARMAN_COMMAND_SUBMIT_REDUCE_JOB_BG,   /* C->J: FUNC[0]UNIQ[0]REDUCER[0]UNUSED[0]ARGS */
128
  GEARMAN_COMMAND_GRAB_JOB_ALL,           /* W->J -- */
129
  GEARMAN_COMMAND_JOB_ASSIGN_ALL,         /* J->W: HANDLE[0]FUNC[0]UNIQ[0]REDUCER[0]ARGS */
130
  GEARMAN_COMMAND_GET_STATUS_UNIQUE,      /* C->J: UNIQUE */
131
  GEARMAN_COMMAND_STATUS_RES_UNIQUE,      /* J->C: UNIQUE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM[0]CLIENT_COUNT */
132
  GEARMAN_COMMAND_MAX /* Always add new commands before this. */
133
} gearman_command_t;
134
135
static const value_string gearman_command_names[] = {
136
  { GEARMAN_COMMAND_TEXT,                 "TEXT" },
137
  { GEARMAN_COMMAND_CAN_DO,               "CAN_DO" },             /* W->J: FUNC */
138
  { GEARMAN_COMMAND_CANT_DO,              "CANT_DO" },            /* W->J: FUNC */
139
  { GEARMAN_COMMAND_RESET_ABILITIES,      "RESET_ABILITIES" },    /* W->J: -- */
140
  { GEARMAN_COMMAND_PRE_SLEEP,            "PRE_SLEEP" },          /* W->J: -- */
141
  { GEARMAN_COMMAND_UNUSED,               "UNUSED" },
142
  { GEARMAN_COMMAND_NOOP,                 "NOOP" },               /* J->W: -- */
143
  { GEARMAN_COMMAND_SUBMIT_JOB,           "SUBMIT_JOB" },         /* C->J: FUNC[0]UNIQ[0]ARGS */
144
  { GEARMAN_COMMAND_JOB_CREATED,          "JOB_CREATED" },        /* J->C: HANDLE  */
145
  { GEARMAN_COMMAND_GRAB_JOB,             "GRAB_JOB" },           /* W->J: --  */
146
  { GEARMAN_COMMAND_NO_JOB,               "NO_JOB" },             /* J->W: -- */
147
  { GEARMAN_COMMAND_JOB_ASSIGN,           "JOB_ASSIGN" },         /* J->W: HANDLE[0]FUNC[0]ARG  */
148
  { GEARMAN_COMMAND_WORK_STATUS,          "WORK_STATUS" },        /* W->J/C: HANDLE[0]NUMERATOR[0]DENOMINATOR */
149
  { GEARMAN_COMMAND_WORK_COMPLETE,        "WORK_COMPLETE" },      /* W->J/C: HANDLE[0]RES */
150
  { GEARMAN_COMMAND_WORK_FAIL,            "WORK_FAIL" },          /* W->J/C: HANDLE */
151
  { GEARMAN_COMMAND_GET_STATUS,           "GET_STATUS" },         /* C->J: HANDLE */
152
  { GEARMAN_COMMAND_ECHO_REQ,             "ECHO_REQ" },           /* ?->J: TEXT */
153
  { GEARMAN_COMMAND_ECHO_RES,             "ECHO_RES" },           /* J->?: TEXT */
154
  { GEARMAN_COMMAND_SUBMIT_JOB_BG,        "SUBMIT_JOB_BG" },      /* C->J: FUNC[0]UNIQ[0]ARGS  */
155
  { GEARMAN_COMMAND_ERROR,                "ERROR" },              /* J->?: ERRCODE[0]ERR_TEXT */
156
  { GEARMAN_COMMAND_STATUS_RES,           "STATUS_RES" },         /* C->J: HANDLE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM */
157
  { GEARMAN_COMMAND_SUBMIT_JOB_HIGH,      "SUBMIT_JOB_HIGH" },    /* C->J: FUNC[0]UNIQ[0]ARGS  */
158
  { GEARMAN_COMMAND_SET_CLIENT_ID,        "SET_CLIENT_ID" },      /* W->J: [RANDOM_STRING_NO_WHITESPACE] */
159
  { GEARMAN_COMMAND_CAN_DO_TIMEOUT,       "CAN_DO_TIMEOUT" },     /* W->J: FUNC[0]TIMEOUT */
160
  { GEARMAN_COMMAND_ALL_YOURS,            "ALL_YOURS" },
161
  { GEARMAN_COMMAND_WORK_EXCEPTION,       "WORK_EXCEPTION" },
162
  { GEARMAN_COMMAND_OPTION_REQ,           "OPTION_REQ" },
163
  { GEARMAN_COMMAND_OPTION_RES,           "OPTION_RES" },
164
  { GEARMAN_COMMAND_WORK_DATA,            "WORK_DATA" },
165
  { GEARMAN_COMMAND_WORK_WARNING,         "WORK_WARNING" },
166
  { GEARMAN_COMMAND_GRAB_JOB_UNIQ,        "GRAB_JOB_UNIQ" },
167
  { GEARMAN_COMMAND_JOB_ASSIGN_UNIQ,      "JOB_ASSIGN_UNIQ" },
168
  { GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG,   "SUBMIT_JOB_HIGH_BG" },
169
  { GEARMAN_COMMAND_SUBMIT_JOB_LOW,       "SUBMIT_JOB_LOW" },
170
  { GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG,    "SUBMIT_JOB_LOW_BG" },
171
  { GEARMAN_COMMAND_SUBMIT_JOB_SCHED,     "SUBMIT_JOB_SCHED" },
172
  { GEARMAN_COMMAND_SUBMIT_JOB_EPOCH,     "SUBMIT_JOB_EPOCH" },
173
  { GEARMAN_COMMAND_SUBMIT_REDUCE_JOB,    "SUBMIT_REDUCE_JOB" },
174
  { GEARMAN_COMMAND_SUBMIT_REDUCE_JOB_BG, "SUBMIT_REDUCE_JOB_BG" },
175
  { GEARMAN_COMMAND_GRAB_JOB_ALL,         "GRAB_JOB_ALL" },
176
  { GEARMAN_COMMAND_JOB_ASSIGN_ALL,       "JOB_ASSIGN_ALL" },
177
  { GEARMAN_COMMAND_GET_STATUS_UNIQUE,    "GET_STATUS_UNIQUE" },
178
  { GEARMAN_COMMAND_STATUS_RES_UNIQUE,    "STATUS_RES_UNIQUE" },
179
  { 0, NULL}
180
};
181
182
static unsigned
183
get_gearman_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
184
2
{
185
2
    return tvb_get_ntohl(tvb, offset+8)+GEARMAN_COMMAND_HEADER_SIZE;
186
2
}
187
188
static int
189
dissect_binary_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
190
2
{
191
2
  unsigned curr_offset;
192
2
  char *magic_code;
193
2
  uint32_t type, size;
194
2
  unsigned len;
195
2
  proto_item *content_item = NULL;
196
2
  proto_tree *content_tree = NULL;
197
198
2
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "Gearman");
199
2
  col_clear(pinfo->cinfo,COL_INFO);
200
201
2
  magic_code = (char*)tvb_get_string_enc(pinfo->pool, tvb, 1, 3, ENC_ASCII);
202
2
  type = tvb_get_ntohl(tvb, 4);
203
2
  size = tvb_get_ntohl(tvb, 8);
204
205
2
  col_append_sep_fstr(pinfo->cinfo, COL_INFO, " , ", "[%s] ", magic_code);
206
207
2
  col_append_fstr(pinfo->cinfo, COL_INFO, "%s(%d) LEN=%d",
208
2
      val_to_str(pinfo->pool, type, gearman_command_names, "Unknown (0x%08x)"), type, size);
209
210
2
  if (tree) {
211
2
    proto_item *ti;
212
2
    proto_tree *command_tree, *gearman_tree;
213
2
    ti = proto_tree_add_item(tree, proto_gearman, tvb, 0, -1, ENC_NA);
214
2
    gearman_tree = proto_item_add_subtree(ti, ett_gearman);
215
216
2
    command_tree = proto_tree_add_subtree_format(gearman_tree, tvb, 0, GEARMAN_COMMAND_HEADER_SIZE+size, ett_gearman_command, NULL,
217
2
                             "[%s] %s(%d) LEN=%d", magic_code, val_to_str(pinfo->pool, type, gearman_command_names, "Unknown (0x%08x)"), type, size);
218
219
2
    proto_tree_add_string(command_tree, hf_gearman_magic_code, tvb, 0, 4, magic_code);
220
2
    proto_tree_add_item(command_tree, hf_gearman_pkt_type, tvb, 4, 4, ENC_BIG_ENDIAN);
221
2
    proto_tree_add_item(command_tree, hf_gearman_data_size, tvb, 8, 4, ENC_BIG_ENDIAN);
222
223
    // explicitly set len to 0 if there are no arguments,
224
    // else use tvb_strnlen() to find the remaining length of tvb
225
2
    len = ( size > 0 ) ? tvb_strnlen( tvb, GEARMAN_COMMAND_HEADER_SIZE, -1 ) : 0 ;
226
2
    content_item = proto_tree_add_item(command_tree, hf_gearman_data_content, tvb, GEARMAN_COMMAND_HEADER_SIZE, len, ENC_ASCII);
227
2
    content_tree = proto_item_add_subtree(content_item, ett_gearman_content);
228
2
  }
229
230
2
  curr_offset = GEARMAN_COMMAND_HEADER_SIZE;
231
232
2
  switch(type)
233
2
  {
234
235
  //
236
  // when determining len for proto_tree_add_item()
237
  //
238
  // if the command has one argument:
239
  //   - use tvb_strnlen()
240
  //
241
  // if the command has multiple arguments:
242
  //   - use tvb_strsize() for the all but the last argument
243
  //   - use tvb_strnlen() for the last argument
244
  //
245
  // These are *not* null-terminated strings, they're null-separated
246
  // strings.  For example, some arguments might be the last argument
247
  // in some commands and not be the last argument in other commands,
248
  // so they're not always followed by a null.
249
250
  //
251
  // commands with a single argument
252
  //
253
254
0
  case GEARMAN_COMMAND_ECHO_REQ:
255
0
  case GEARMAN_COMMAND_ECHO_RES:
256
0
    if (!tree) break;
257
0
    if (tvb_captured_length_remaining(tvb, curr_offset) == 0) break;
258
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
259
0
    proto_tree_add_item(content_tree, hf_gearman_echo_text, tvb, curr_offset, len, ENC_ASCII);
260
0
    break;
261
262
0
  case GEARMAN_COMMAND_JOB_CREATED:
263
0
  case GEARMAN_COMMAND_WORK_FAIL:
264
0
    if (!tree) break;
265
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
266
0
    proto_tree_add_item(content_tree, hf_gearman_job_handle, tvb, curr_offset, len, ENC_ASCII);
267
0
    break;
268
269
0
  case GEARMAN_COMMAND_OPTION_REQ:
270
0
  case GEARMAN_COMMAND_OPTION_RES:
271
0
    if (!tree) break;
272
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
273
0
    proto_tree_add_item(content_tree, hf_gearman_option_name, tvb, curr_offset, len, ENC_ASCII);
274
0
    break;
275
276
0
  case GEARMAN_COMMAND_SET_CLIENT_ID:
277
0
    if (!tree) break;
278
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
279
0
    proto_tree_add_item(content_tree, hf_gearman_client_id, tvb, curr_offset, len, ENC_ASCII);
280
0
    break;
281
282
0
  case GEARMAN_COMMAND_GET_STATUS_UNIQUE:
283
0
    if (!tree) break;
284
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
285
0
    proto_tree_add_item(content_tree, hf_gearman_uniq_id, tvb, curr_offset, len, ENC_ASCII);
286
0
    break;
287
288
0
  case GEARMAN_COMMAND_CAN_DO:
289
0
  case GEARMAN_COMMAND_CANT_DO:
290
0
    if (!tree) break;
291
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
292
0
    proto_tree_add_item(content_tree, hf_gearman_func_name, tvb, curr_offset, len, ENC_ASCII);
293
0
    break;
294
295
  //
296
  // commands with multiple arguments
297
  //
298
299
0
  case GEARMAN_COMMAND_ERROR:
300
0
    if (!tree) break;
301
0
    len = tvb_strsize(tvb, curr_offset);
302
0
    proto_tree_add_item(content_tree, hf_gearman_err_code, tvb, curr_offset, len, ENC_ASCII);
303
304
0
    curr_offset += len;
305
0
    if (tvb_captured_length_remaining(tvb, curr_offset) == 0) break;
306
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
307
0
    proto_tree_add_item(content_tree, hf_gearman_err_text, tvb, curr_offset, len, ENC_ASCII);
308
0
    break;
309
310
0
  case GEARMAN_COMMAND_WORK_DATA:
311
0
  case GEARMAN_COMMAND_WORK_WARNING:
312
0
  case GEARMAN_COMMAND_WORK_COMPLETE:
313
0
  case GEARMAN_COMMAND_WORK_EXCEPTION:
314
0
    if (!tree) break;
315
0
    len = tvb_strsize(tvb, curr_offset);
316
0
    proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, curr_offset, len, ENC_ASCII);
317
318
0
    curr_offset += len;
319
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
320
0
    proto_tree_add_item(content_tree, hf_gearman_result, tvb, curr_offset, len, ENC_ASCII);
321
0
    break;
322
323
0
  case GEARMAN_COMMAND_STATUS_RES:
324
0
    if (!tree) break;
325
0
    len = tvb_strsize(tvb, curr_offset);
326
0
    proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, curr_offset, len, ENC_ASCII);
327
328
0
    curr_offset += len;
329
0
    len = tvb_strsize(tvb, curr_offset);
330
0
    proto_tree_add_item(content_tree, hf_gearman_known_status, tvb, curr_offset, len, ENC_ASCII);
331
332
0
    curr_offset += len;
333
0
    len = tvb_strsize(tvb, curr_offset);
334
0
    proto_tree_add_item(content_tree, hf_gearman_running_status, tvb, curr_offset, len, ENC_ASCII);
335
336
0
    curr_offset += len;
337
0
    len = tvb_strsize(tvb, curr_offset);
338
0
    proto_tree_add_item(content_tree, hf_gearman_complete_numerator, tvb, curr_offset, len, ENC_ASCII);
339
340
0
    curr_offset += len;
341
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
342
0
    proto_tree_add_item(content_tree, hf_gearman_complete_denominator, tvb, curr_offset, len, ENC_ASCII);
343
0
    break;
344
345
0
  case GEARMAN_COMMAND_SUBMIT_JOB:
346
0
  case GEARMAN_COMMAND_SUBMIT_JOB_BG:
347
0
  case GEARMAN_COMMAND_SUBMIT_JOB_HIGH:
348
0
  case GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG:
349
0
  case GEARMAN_COMMAND_SUBMIT_JOB_LOW:
350
0
  case GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG:
351
0
    if (!tree) break;
352
0
    len = tvb_strsize(tvb, curr_offset);
353
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
354
355
0
    curr_offset += len;
356
0
    len = tvb_strsize(tvb, curr_offset);
357
0
    proto_tree_add_item(content_tree, hf_gearman_uniq_idz, tvb, curr_offset, len, ENC_ASCII);
358
359
0
    curr_offset += len;
360
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
361
0
    proto_tree_add_item(content_tree, hf_gearman_argument, tvb, curr_offset, len, ENC_ASCII);
362
0
    break;
363
364
0
  case GEARMAN_COMMAND_SUBMIT_REDUCE_JOB:
365
0
  case GEARMAN_COMMAND_SUBMIT_REDUCE_JOB_BG:
366
0
    if (!tree) break;
367
0
    len = tvb_strsize(tvb, curr_offset);
368
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
369
370
0
    curr_offset += len;
371
0
    len = tvb_strsize(tvb, curr_offset);
372
0
    proto_tree_add_item(content_tree, hf_gearman_uniq_idz, tvb, curr_offset, len, ENC_ASCII);
373
374
0
    curr_offset += len;
375
0
    len = tvb_strsize(tvb, curr_offset);
376
0
    proto_tree_add_item(content_tree, hf_gearman_reducer, tvb, curr_offset, len, ENC_ASCII);
377
378
0
    curr_offset += len;
379
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
380
0
    proto_tree_add_item(content_tree, hf_gearman_argument, tvb, curr_offset, len, ENC_ASCII);
381
0
    break;
382
383
0
  case GEARMAN_COMMAND_SUBMIT_JOB_SCHED:
384
0
    if (!tree) break;
385
0
    len = tvb_strsize(tvb, curr_offset);
386
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
387
388
0
    curr_offset += len;
389
0
    len = tvb_strsize(tvb, curr_offset);
390
0
    proto_tree_add_item(content_tree, hf_gearman_uniq_idz, tvb, curr_offset, len, ENC_ASCII);
391
392
0
    curr_offset += len;
393
0
    len = tvb_strsize(tvb, curr_offset);
394
0
    proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_minute, tvb, curr_offset, len, ENC_ASCII);
395
396
0
    curr_offset += len;
397
0
    len = tvb_strsize(tvb, curr_offset);
398
0
    proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_hour, tvb, curr_offset, len, ENC_ASCII);
399
400
0
    curr_offset += len;
401
0
    len = tvb_strsize(tvb, curr_offset);
402
0
    proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_day_of_month, tvb, curr_offset, len, ENC_ASCII);
403
404
0
    curr_offset += len;
405
0
    len = tvb_strsize(tvb, curr_offset);
406
0
    proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_month, tvb, curr_offset, len, ENC_ASCII);
407
408
0
    curr_offset += len;
409
0
    len = tvb_strsize(tvb, curr_offset);
410
0
    proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_day_of_week, tvb, curr_offset, len, ENC_ASCII);
411
412
0
    curr_offset += len;
413
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
414
0
    proto_tree_add_item(content_tree, hf_gearman_argument, tvb, curr_offset, len, ENC_ASCII);
415
0
    break;
416
417
0
  case GEARMAN_COMMAND_SUBMIT_JOB_EPOCH:
418
0
    if (!tree) break;
419
0
    len = tvb_strsize(tvb, curr_offset);
420
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
421
422
0
    curr_offset += len;
423
0
    len = tvb_strsize(tvb, curr_offset);
424
0
    proto_tree_add_item(content_tree, hf_gearman_uniq_idz, tvb, curr_offset, len, ENC_ASCII);
425
426
0
    curr_offset += len;
427
0
    len = tvb_strsize(tvb, curr_offset);
428
0
    proto_tree_add_item(content_tree, hf_gearman_submit_job_epoch_time, tvb, curr_offset, len, ENC_ASCII);
429
430
0
    curr_offset += len;
431
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
432
0
    proto_tree_add_item(content_tree, hf_gearman_argument, tvb, curr_offset, len, ENC_ASCII);
433
0
    break;
434
435
0
  case GEARMAN_COMMAND_JOB_ASSIGN:
436
0
    len = tvb_strsize(tvb, curr_offset);
437
0
    proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, curr_offset, len, ENC_ASCII);
438
439
0
    curr_offset += len;
440
0
    len = tvb_strsize(tvb, curr_offset);
441
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
442
443
0
    curr_offset += len;
444
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
445
0
    proto_tree_add_item(content_tree, hf_gearman_argument, tvb, curr_offset, len, ENC_ASCII);
446
0
    break;
447
448
0
  case GEARMAN_COMMAND_JOB_ASSIGN_UNIQ:
449
0
    len = tvb_strsize(tvb, curr_offset);
450
0
    proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, curr_offset, len, ENC_ASCII);
451
452
0
    curr_offset += len;
453
0
    len = tvb_strsize(tvb, curr_offset);
454
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
455
456
0
    curr_offset += len;
457
0
    len = tvb_strsize(tvb, curr_offset);
458
0
    proto_tree_add_item(content_tree, hf_gearman_uniq_idz, tvb, curr_offset, len, ENC_ASCII);
459
460
0
    curr_offset += len;
461
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
462
0
    proto_tree_add_item(content_tree, hf_gearman_argument, tvb, curr_offset, len, ENC_ASCII);
463
0
    break;
464
465
0
  case GEARMAN_COMMAND_JOB_ASSIGN_ALL:
466
0
    if (!tree) break;
467
0
    len = tvb_strsize(tvb, curr_offset);
468
0
    proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, curr_offset, len, ENC_ASCII);
469
470
0
    curr_offset += len;
471
0
    len = tvb_strsize(tvb, curr_offset);
472
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
473
474
0
    curr_offset += len;
475
0
    len = tvb_strsize(tvb, curr_offset);
476
0
    proto_tree_add_item(content_tree, hf_gearman_uniq_idz, tvb, curr_offset, len, ENC_ASCII);
477
478
0
    curr_offset += len;
479
0
    len = tvb_strsize(tvb, curr_offset);
480
0
    proto_tree_add_item(content_tree, hf_gearman_reducer, tvb, curr_offset, len, ENC_ASCII);
481
482
0
    curr_offset += len;
483
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
484
0
    proto_tree_add_item(content_tree, hf_gearman_argument, tvb, curr_offset, len, ENC_ASCII);
485
0
    break;
486
487
0
  case GEARMAN_COMMAND_WORK_STATUS:
488
0
    if (!tree) break;
489
0
    len = tvb_strsize(tvb, curr_offset);
490
0
    proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, curr_offset, len, ENC_ASCII);
491
492
0
    curr_offset += len;
493
0
    len = tvb_strsize(tvb, curr_offset);
494
0
    proto_tree_add_item(content_tree, hf_gearman_complete_numerator, tvb, curr_offset, len, ENC_ASCII);
495
496
0
    curr_offset += len;
497
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
498
0
    proto_tree_add_item(content_tree, hf_gearman_complete_denominator, tvb, curr_offset, len, ENC_ASCII);
499
0
    break;
500
501
0
  case GEARMAN_COMMAND_CAN_DO_TIMEOUT:
502
0
    if (!tree) break;
503
0
    len = tvb_strsize(tvb, curr_offset);
504
0
    proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, curr_offset, len, ENC_ASCII);
505
506
0
    curr_offset += len;
507
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
508
0
    proto_tree_add_item(content_tree, hf_gearman_timeout_value, tvb, curr_offset, len, ENC_ASCII);
509
0
    break;
510
511
0
  case GEARMAN_COMMAND_STATUS_RES_UNIQUE:
512
0
    if (!tree) break;
513
0
    len = tvb_strsize(tvb, curr_offset);
514
0
    proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, curr_offset, len, ENC_ASCII);
515
516
0
    curr_offset += len;
517
0
    len = tvb_strsize(tvb, curr_offset);
518
0
    proto_tree_add_item(content_tree, hf_gearman_known_status, tvb, curr_offset, len, ENC_ASCII);
519
520
0
    curr_offset += len;
521
0
    len = tvb_strsize(tvb, curr_offset);
522
0
    proto_tree_add_item(content_tree, hf_gearman_running_status, tvb, curr_offset, len, ENC_ASCII);
523
524
0
    curr_offset += len;
525
0
    len = tvb_strsize(tvb, curr_offset);
526
0
    proto_tree_add_item(content_tree, hf_gearman_complete_numerator, tvb, curr_offset, len, ENC_ASCII);
527
528
0
    curr_offset += len;
529
0
    len = tvb_strsize(tvb, curr_offset);
530
0
    proto_tree_add_item(content_tree, hf_gearman_complete_denominator, tvb, curr_offset, len, ENC_ASCII);
531
532
0
    curr_offset += len;
533
0
    len = tvb_strnlen( tvb, curr_offset, -1 );
534
0
    proto_tree_add_item(content_tree, hf_gearman_client_count, tvb, curr_offset, len, ENC_ASCII);
535
0
    break;
536
537
2
  default:
538
2
    if (size > 0)
539
2
      expert_add_info(pinfo, content_item, &ei_gearman_pkt_type_unknown);
540
2
  }
541
542
2
  col_set_fence(pinfo->cinfo, COL_INFO);
543
2
  return tvb_captured_length(tvb);
544
2
}
545
546
static void
547
dissect_management_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
548
16
{
549
16
  unsigned i, type = 0, cmdlen, linelen, offset = 0, next_offset = 0;
550
16
  proto_item *ti;
551
16
  proto_tree *gearman_tree;
552
553
16
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "Gearman");
554
16
  col_clear(pinfo->cinfo, COL_INFO);
555
556
16
  ti = proto_tree_add_item(tree, proto_gearman, tvb, 0, -1, ENC_NA);
557
16
  gearman_tree = proto_item_add_subtree(ti, ett_gearman);
558
559
113
  while (tvb_find_line_end_remaining(tvb, offset, &linelen, &next_offset))
560
97
  {
561
582
    for (i=0; i<GEARMAN_MGR_CMDS_COUNT; i++)
562
485
    {
563
      /* the array is a const and clearly none of the elements are longer than
564
       * MAX_SIGNED_INT so this is a safe cast */
565
485
      cmdlen = (int)strlen(GEARMAN_MGR_CMDS[i]);
566
567
485
      if (cmdlen == linelen && 0 == tvb_strneql(tvb, offset, GEARMAN_MGR_CMDS[i], cmdlen))
568
0
      {
569
0
        const uint8_t* cmdstr;
570
0
        proto_tree_add_item_ret_string(gearman_tree, hf_gearman_mgr_cmd, tvb, offset, cmdlen, ENC_ASCII|ENC_NA, pinfo->pool, &cmdstr);
571
0
        col_add_fstr(pinfo->cinfo, COL_INFO, "[MGR] %s", cmdstr);
572
0
        type = 1;
573
0
        break;
574
0
      }
575
485
    }
576
577
97
    if (GEARMAN_MGR_CMDS_COUNT == i)
578
97
    {
579
97
      proto_tree_add_format_text(gearman_tree, tvb, offset, next_offset - offset);
580
581
97
      if (type == 0)
582
15
      {
583
15
        col_add_fstr(pinfo->cinfo, COL_INFO, "[MGR] %s", tvb_get_string_enc(pinfo->pool, tvb, offset, linelen, ENC_ASCII));
584
15
        type = -1;
585
15
      }
586
82
      else
587
82
      {
588
82
        col_append_sep_str(pinfo->cinfo, COL_INFO, ",", (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, linelen, ENC_ASCII));
589
82
      }
590
97
    }
591
592
97
    offset = next_offset;
593
97
  }
594
16
}
595
596
static int
597
dissect_gearman(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
598
18
{
599
18
  if ((0 == tvb_memeql(tvb, 0, GEARMAN_MAGIC_CODE_REQUEST, 4)) ||
600
18
      (0 == tvb_memeql(tvb, 0, GEARMAN_MAGIC_CODE_RESPONSE, 4)))
601
2
  {
602
2
    tcp_dissect_pdus(tvb, pinfo, tree, gearman_desegment, GEARMAN_COMMAND_HEADER_SIZE, get_gearman_pdu_len, dissect_binary_packet, data);
603
2
  }
604
16
  else
605
16
  {
606
16
    dissect_management_packet(tvb, pinfo, tree);
607
16
  }
608
609
18
  return tvb_captured_length(tvb);
610
18
}
611
612
void
613
proto_register_gearman(void)
614
16
{
615
16
  static hf_register_info hf[] = {
616
16
    { &hf_gearman_mgr_cmd, { "Management Command", "gearman.mgr_cmd", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
617
16
    { &hf_gearman_magic_code, { "Magic Code", "gearman.magic_code", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
618
16
    { &hf_gearman_pkt_type, { "Packet Type", "gearman.pkt_type", FT_UINT32, BASE_DEC_HEX, VALS(gearman_command_names), 0x0, NULL, HFILL} },
619
16
    { &hf_gearman_data_size, { "Data Length", "gearman.data_size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} },
620
16
    { &hf_gearman_data_content, { "Data Content", "gearman.data_content", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
621
16
    { &hf_gearman_option_name, { "Option Name", "gearman.opt.name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
622
16
    { &hf_gearman_func_name, { "Function Name", "gearman.func.name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
623
16
    { &hf_gearman_func_namez, { "Function Name", "gearman.func.name", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
624
16
    { &hf_gearman_client_id, { "Client ID", "gearman.client_id", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
625
16
    { &hf_gearman_client_count, { "Client Count", "gearman.client_count", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
626
16
    { &hf_gearman_uniq_id, { "Unique ID", "gearman.uniq_id", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
627
16
    { &hf_gearman_uniq_idz, { "Unique ID", "gearman.uniq_id", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
628
16
    { &hf_gearman_argument, { "Function Argument", "gearman.func.arg", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
629
16
    { &hf_gearman_job_handle, { "Job Handle", "gearman.job.handle", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
630
16
    { &hf_gearman_job_handlez, { "Job Handle", "gearman.job.handle", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
631
16
    { &hf_gearman_complete_numerator, { "Complete Numerator", "gearman.numerator", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
632
16
    { &hf_gearman_complete_denominator, { "Complete Denominator", "gearman.denominator", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
633
16
    { &hf_gearman_submit_job_sched_minute, { "Minute", "gearman.submit_job_sched.minute", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
634
16
    { &hf_gearman_submit_job_sched_hour, { "Hour", "gearman.submit_job_sched.hour", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
635
16
    { &hf_gearman_submit_job_sched_day_of_month, { "Day of Month", "gearman.submit_job_sched.day_of_month", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
636
16
    { &hf_gearman_submit_job_sched_month, { "Month", "gearman.submit_job_sched.month", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
637
16
    { &hf_gearman_submit_job_sched_day_of_week, { "Day of Week", "gearman.submit_job_sched.day_of_week", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
638
16
    { &hf_gearman_submit_job_epoch_time, { "Epoch Time", "gearman.submit_job.epoch_time", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
639
16
    { &hf_gearman_reducer, { "Reducer", "gearman.reducer", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
640
16
    { &hf_gearman_result, { "Function Result", "gearman.func.result", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
641
16
    { &hf_gearman_known_status, { "Known job", "gearman.job.known", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
642
16
    { &hf_gearman_running_status, { "Running Job", "gearman.job.running", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
643
16
    { &hf_gearman_timeout_value, { "Timeout Value", "gearman.timeout.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
644
16
    { &hf_gearman_echo_text, { "Echo Text", "gearman.echo_text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
645
16
    { &hf_gearman_err_code, { "Error Code", "gearman.err.code", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
646
16
    { &hf_gearman_err_text, { "Error Text", "gearman.err.text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} }
647
16
  };
648
649
  /* Setup protocol subtree array */
650
16
  static int *ett[] = {
651
16
    &ett_gearman,
652
16
    &ett_gearman_command,
653
16
    &ett_gearman_content
654
16
  };
655
656
16
  static ei_register_info ei[] = {
657
16
     { &ei_gearman_pkt_type_unknown, { "gearman.pkt_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown command", EXPFILL }},
658
16
  };
659
660
16
  module_t *gearman_module;
661
16
  expert_module_t* expert_gearman;
662
663
16
  proto_gearman = proto_register_protocol("Gearman Protocol", "Gearman", "gearman");
664
665
16
  proto_register_field_array(proto_gearman, hf, array_length(hf));
666
16
  proto_register_subtree_array(ett, array_length(ett));
667
16
  expert_gearman = expert_register_protocol(proto_gearman);
668
16
  expert_register_field_array(expert_gearman, ei, array_length(ei));
669
670
16
  gearman_module = prefs_register_protocol(proto_gearman, NULL);
671
16
  prefs_register_bool_preference(gearman_module, "desegment",
672
16
                                  "Desegment all Gearman messages spanning multiple TCP segments",
673
16
                                  "Whether the Gearman dissector should desegment all messages spanning multiple TCP segments",
674
16
                                  &gearman_desegment);
675
676
16
  gearman_handle = register_dissector("gearman", dissect_gearman, proto_gearman);
677
16
}
678
679
void
680
proto_reg_handoff_gearman(void)
681
16
{
682
16
  dissector_add_uint_with_preference("tcp.port", GEARMAN_PORT, gearman_handle);
683
16
}
684
685
/*
686
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
687
 *
688
 * Local variables:
689
 * c-basic-offset: 2
690
 * tab-width: 8
691
 * indent-tabs-mode: nil
692
 * End:
693
 *
694
 * vi: set shiftwidth=2 tabstop=8 expandtab:
695
 * :indentSize=2:tabSize=8:noTabs=true:
696
 */