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