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