Revert "Fixup: tvb_* -> tvb_captured"
[metze/wireshark/wip.git] / epan / dissectors / packet-gearman.c
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/index.php?id=protocol
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27  */
28
29 #include "config.h"
30
31 #include <epan/packet.h>
32 #include <epan/prefs.h>
33 #include <epan/expert.h>
34 #include "packet-tcp.h"
35
36 void proto_register_gearman(void);
37 void proto_reg_handoff_gearman(void);
38
39 static int proto_gearman = -1;
40
41 static int hf_gearman_mgr_cmd = -1;
42 static int hf_gearman_magic_code = -1;
43 static int hf_gearman_pkt_type = -1;
44 static int hf_gearman_data_size = -1;
45 static int hf_gearman_data_content = -1;
46 static int hf_gearman_option_name = -1;
47 static int hf_gearman_func_name = -1;
48 static int hf_gearman_func_namez = -1;
49 static int hf_gearman_client_id = -1;
50 static int hf_gearman_uniq_id = -1;
51 static int hf_gearman_argument = -1;
52 static int hf_gearman_job_handle = -1;
53 static int hf_gearman_job_handlez = -1;
54 static int hf_gearman_complete_numerator = -1;
55 static int hf_gearman_complete_denominator = -1;
56 static int hf_gearman_submit_job_sched_minute = -1;
57 static int hf_gearman_submit_job_sched_hour = -1;
58 static int hf_gearman_submit_job_sched_day_of_month = -1;
59 static int hf_gearman_submit_job_sched_month = -1;
60 static int hf_gearman_submit_job_sched_day_of_week = -1;
61 static int hf_gearman_submit_job_epoch_time = -1;
62 static int hf_gearman_result = -1;
63 static int hf_gearman_known_status = -1;
64 static int hf_gearman_running_status = -1;
65 static int hf_gearman_echo_text = -1;
66 static int hf_gearman_err_code = -1;
67 static int hf_gearman_err_text = -1;
68
69 static gint ett_gearman = -1;
70 static gint ett_gearman_command = -1;
71 static gint ett_gearman_content = -1;
72
73 static expert_field ei_gearman_pkt_type_unknown = EI_INIT;
74
75 static gboolean gearman_desegment  = TRUE;
76
77 static const int GEARMAN_COMMAND_HEADER_SIZE = 12;
78 static const int GEARMAN_PORT = 4730;
79 static const gchar *GEARMAN_MAGIC_CODE_REQUEST = "\0REQ";
80 static const gchar *GEARMAN_MAGIC_CODE_RESPONSE = "\0RES";
81
82 static const gchar *GEARMAN_MGR_CMDS[] = {
83   "workers",
84   "status",
85   "maxqueue",
86   "shutdown",
87   "version"
88 };
89
90 static const int GEARMAN_MGR_CMDS_COUNT = sizeof(GEARMAN_MGR_CMDS)/sizeof(GEARMAN_MGR_CMDS[0]);
91
92 typedef enum
93 {
94   GEARMAN_COMMAND_TEXT,
95   GEARMAN_COMMAND_CAN_DO,              /* W->J: FUNC */
96   GEARMAN_COMMAND_CANT_DO,             /* W->J: FUNC */
97   GEARMAN_COMMAND_RESET_ABILITIES,     /* W->J: -- */
98   GEARMAN_COMMAND_PRE_SLEEP,           /* W->J: -- */
99   GEARMAN_COMMAND_UNUSED,
100   GEARMAN_COMMAND_NOOP,                /* J->W: -- */
101   GEARMAN_COMMAND_SUBMIT_JOB,          /* C->J: FUNC[0]UNIQ[0]ARGS */
102   GEARMAN_COMMAND_JOB_CREATED,         /* J->C: HANDLE  */
103   GEARMAN_COMMAND_GRAB_JOB,            /* W->J: --  */
104   GEARMAN_COMMAND_NO_JOB,              /* J->W: -- */
105   GEARMAN_COMMAND_JOB_ASSIGN,          /* J->W: HANDLE[0]FUNC[0]ARG  */
106   GEARMAN_COMMAND_WORK_STATUS,         /* W->J/C: HANDLE[0]NUMERATOR[0]DENOMINATOR */
107   GEARMAN_COMMAND_WORK_COMPLETE,       /* W->J/C: HANDLE[0]RES */
108   GEARMAN_COMMAND_WORK_FAIL,           /* W->J/C: HANDLE */
109   GEARMAN_COMMAND_GET_STATUS,          /* C->J: HANDLE */
110   GEARMAN_COMMAND_ECHO_REQ,            /* ?->J: TEXT */
111   GEARMAN_COMMAND_ECHO_RES,            /* J->?: TEXT */
112   GEARMAN_COMMAND_SUBMIT_JOB_BG,       /* C->J: FUNC[0]UNIQ[0]ARGS  */
113   GEARMAN_COMMAND_ERROR,               /* J->?: ERRCODE[0]ERR_TEXT */
114   GEARMAN_COMMAND_STATUS_RES,          /* C->J: HANDLE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM */
115   GEARMAN_COMMAND_SUBMIT_JOB_HIGH,     /* C->J: FUNC[0]UNIQ[0]ARGS  */
116   GEARMAN_COMMAND_SET_CLIENT_ID,       /* W->J: [RANDOM_STRING_NO_WHITESPACE] */
117   GEARMAN_COMMAND_CAN_DO_TIMEOUT,      /* W->J: FUNC[0]TIMEOUT */
118   GEARMAN_COMMAND_ALL_YOURS,
119   GEARMAN_COMMAND_WORK_EXCEPTION,
120   GEARMAN_COMMAND_OPTION_REQ,
121   GEARMAN_COMMAND_OPTION_RES,
122   GEARMAN_COMMAND_WORK_DATA,
123   GEARMAN_COMMAND_WORK_WARNING,
124   GEARMAN_COMMAND_GRAB_JOB_UNIQ,
125   GEARMAN_COMMAND_JOB_ASSIGN_UNIQ,
126   GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG,
127   GEARMAN_COMMAND_SUBMIT_JOB_LOW,
128   GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG,
129   GEARMAN_COMMAND_SUBMIT_JOB_SCHED,
130   GEARMAN_COMMAND_SUBMIT_JOB_EPOCH,
131   GEARMAN_COMMAND_MAX /* Always add new commands before this. */
132 } gearman_command_t;
133
134 static const value_string gearman_command_names[] = {
135   { GEARMAN_COMMAND_TEXT,               "TEXT" },
136   { GEARMAN_COMMAND_CAN_DO,             "CAN_DO" },             /* W->J: FUNC */
137   { GEARMAN_COMMAND_CANT_DO,            "CANT_DO" },            /* W->J: FUNC */
138   { GEARMAN_COMMAND_RESET_ABILITIES,    "RESET_ABILITIES" },    /* W->J: -- */
139   { GEARMAN_COMMAND_PRE_SLEEP,          "PRE_SLEEP" },          /* W->J: -- */
140   { GEARMAN_COMMAND_UNUSED,             "UNUSED" },
141   { GEARMAN_COMMAND_NOOP,               "NOOP" },               /* J->W: -- */
142   { GEARMAN_COMMAND_SUBMIT_JOB,         "SUBMIT_JOB" },         /* C->J: FUNC[0]UNIQ[0]ARGS */
143   { GEARMAN_COMMAND_JOB_CREATED,        "JOB_CREATED" },        /* J->C: HANDLE  */
144   { GEARMAN_COMMAND_GRAB_JOB,           "GRAB_JOB" },           /* W->J: --  */
145   { GEARMAN_COMMAND_NO_JOB,             "NO_JOB" },             /* J->W: -- */
146   { GEARMAN_COMMAND_JOB_ASSIGN,         "JOB_ASSIGN" },         /* J->W: HANDLE[0]FUNC[0]ARG  */
147   { GEARMAN_COMMAND_WORK_STATUS,        "WORK_STATUS" },        /* W->J/C: HANDLE[0]NUMERATOR[0]DENOMINATOR */
148   { GEARMAN_COMMAND_WORK_COMPLETE,      "WORK_COMPLETE" },      /* W->J/C: HANDLE[0]RES */
149   { GEARMAN_COMMAND_WORK_FAIL,          "WORK_FAIL" },          /* W->J/C: HANDLE */
150   { GEARMAN_COMMAND_GET_STATUS,         "GET_STATUS" },         /* C->J: HANDLE */
151   { GEARMAN_COMMAND_ECHO_REQ,           "ECHO_REQ" },           /* ?->J: TEXT */
152   { GEARMAN_COMMAND_ECHO_RES,           "ECHO_RES" },           /* J->?: TEXT */
153   { GEARMAN_COMMAND_SUBMIT_JOB_BG,      "SUBMIT_JOB_BG" },      /* C->J: FUNC[0]UNIQ[0]ARGS  */
154   { GEARMAN_COMMAND_ERROR,              "ERROR" },              /* J->?: ERRCODE[0]ERR_TEXT */
155   { GEARMAN_COMMAND_STATUS_RES,         "STATUS_RES" },         /* C->J: HANDLE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM */
156   { GEARMAN_COMMAND_SUBMIT_JOB_HIGH,    "SUBMIT_JOB_HIGH" },    /* C->J: FUNC[0]UNIQ[0]ARGS  */
157   { GEARMAN_COMMAND_SET_CLIENT_ID,      "SET_CLIENT_ID" },      /* W->J: [RANDOM_STRING_NO_WHITESPACE] */
158   { GEARMAN_COMMAND_CAN_DO_TIMEOUT,     "CAN_DO_TIMEOUT" },     /* W->J: FUNC[0]TIMEOUT */
159   { GEARMAN_COMMAND_ALL_YOURS,          "ALL_YOURS" },
160   { GEARMAN_COMMAND_WORK_EXCEPTION,     "WORK_EXCEPTION" },
161   { GEARMAN_COMMAND_OPTION_REQ,         "OPTION_REQ" },
162   { GEARMAN_COMMAND_OPTION_RES,         "OPTION_RES" },
163   { GEARMAN_COMMAND_WORK_DATA,          "WORK_DATA" },
164   { GEARMAN_COMMAND_WORK_WARNING,       "WORK_WARNING" },
165   { GEARMAN_COMMAND_GRAB_JOB_UNIQ,      "GRAB_JOB_UNIQ" },
166   { GEARMAN_COMMAND_JOB_ASSIGN_UNIQ,    "JOB_ASSIGN_UNIQ" },
167   { GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG, "SUBMIT_JOB_HIGH_BG" },
168   { GEARMAN_COMMAND_SUBMIT_JOB_LOW,     "SUBMIT_JOB_LOW" },
169   { GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG,  "SUBMIT_JOB_LOW_BG" },
170   { GEARMAN_COMMAND_SUBMIT_JOB_SCHED,   "SUBMIT_JOB_SCHED" },
171   { GEARMAN_COMMAND_SUBMIT_JOB_EPOCH,   "SUBMIT_JOB_EPOCH" },
172   { 0, NULL}
173 };
174
175 static guint
176 get_gearman_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
177 {
178     return tvb_get_ntohl(tvb, offset+8)+GEARMAN_COMMAND_HEADER_SIZE;
179 }
180
181 static int
182 dissect_binary_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
183 {
184   gint offset, start_offset;
185   char *magic_code;
186   guint32 type, size;
187   guint len;
188   proto_item *content_item = NULL;
189   proto_tree *content_tree = NULL;
190
191   col_set_str(pinfo->cinfo, COL_PROTOCOL, "Gearman");
192   col_clear(pinfo->cinfo,COL_INFO);
193
194   magic_code = tvb_get_string_enc(wmem_packet_scope(), tvb, 1, 3, ENC_ASCII);
195   type = tvb_get_ntohl(tvb, 4);
196   size = tvb_get_ntohl(tvb, 8);
197
198   col_append_sep_fstr(pinfo->cinfo, COL_INFO, " , ", "[%s] ", magic_code);
199
200   col_append_fstr(pinfo->cinfo, COL_INFO, "%s(%d) LEN=%d",
201       val_to_str(type, gearman_command_names, "Unknown (0x%08x)"), type, size);
202
203   if (tree) {
204     proto_item *ti;
205     proto_tree *command_tree, *gearman_tree;
206     ti = proto_tree_add_item(tree, proto_gearman, tvb, 0, -1, ENC_NA);
207     gearman_tree = proto_item_add_subtree(ti, ett_gearman);
208
209     ti = proto_tree_add_text(gearman_tree, tvb, 0, GEARMAN_COMMAND_HEADER_SIZE+size,
210                              "[%s] %s(%d) LEN=%d", magic_code, val_to_str(type, gearman_command_names, "Unknown (0x%08x)"), type, size);
211     command_tree = proto_item_add_subtree(ti, ett_gearman_command);
212
213     proto_tree_add_string(command_tree, hf_gearman_magic_code, tvb, 0, 4, magic_code);
214     proto_tree_add_item(command_tree, hf_gearman_pkt_type, tvb, 4, 4, ENC_BIG_ENDIAN);
215     proto_tree_add_item(command_tree, hf_gearman_data_size, tvb, 8, 4, ENC_BIG_ENDIAN);
216
217     content_item = proto_tree_add_item(command_tree, hf_gearman_data_content, tvb, GEARMAN_COMMAND_HEADER_SIZE, size, ENC_ASCII|ENC_NA);
218     content_tree = proto_item_add_subtree(content_item, ett_gearman_content);
219
220     }
221   offset = GEARMAN_COMMAND_HEADER_SIZE;
222
223   switch(type)
224   {
225   case GEARMAN_COMMAND_ECHO_REQ:
226   case GEARMAN_COMMAND_ECHO_RES:
227     if (!tree) break;
228     proto_tree_add_item(content_tree, hf_gearman_echo_text, tvb,
229                offset, size, ENC_NA|ENC_ASCII);
230     break;
231
232   case GEARMAN_COMMAND_ERROR:
233     if (!tree) break;
234     len = tvb_strsize(tvb, offset);
235     proto_tree_add_item(content_tree, hf_gearman_err_code, tvb, offset, len, ENC_NA|ENC_ASCII);
236     proto_tree_add_item(content_tree, hf_gearman_err_text, tvb, offset+len, size-len, ENC_NA|ENC_ASCII);
237     break;
238
239   case GEARMAN_COMMAND_JOB_CREATED:
240   case GEARMAN_COMMAND_WORK_FAIL:
241     if (!tree) break;
242     proto_tree_add_item(content_tree, hf_gearman_job_handle, tvb,
243                offset, size, ENC_NA|ENC_ASCII);
244     break;
245
246   case GEARMAN_COMMAND_STATUS_RES:
247     if (!tree) break;
248     start_offset = offset;
249     len = tvb_strsize(tvb, start_offset);
250     proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, start_offset, len, ENC_NA|ENC_ASCII);
251
252     start_offset += len;
253     len = tvb_strsize(tvb, start_offset);
254     proto_tree_add_item(content_tree, hf_gearman_known_status, tvb, start_offset, len, ENC_NA|ENC_ASCII);
255
256     start_offset += len;
257     len = tvb_strsize(tvb, start_offset);
258     proto_tree_add_item(content_tree, hf_gearman_running_status, tvb, start_offset, len, ENC_NA|ENC_ASCII);
259
260     start_offset += len;
261     len = tvb_strsize(tvb, start_offset);
262     proto_tree_add_item(content_tree, hf_gearman_complete_numerator, tvb, start_offset, len, ENC_NA|ENC_ASCII);
263
264     proto_tree_add_item(content_tree, hf_gearman_complete_denominator, tvb, start_offset+len, size-start_offset+GEARMAN_COMMAND_HEADER_SIZE, ENC_NA|ENC_ASCII);
265         break;
266
267   case GEARMAN_COMMAND_OPTION_REQ:
268   case GEARMAN_COMMAND_OPTION_RES:
269     if (!tree) break;
270     proto_tree_add_item(content_tree, hf_gearman_option_name, tvb,
271                offset, size, ENC_NA|ENC_ASCII);
272     break;
273
274   case GEARMAN_COMMAND_SUBMIT_JOB:
275   case GEARMAN_COMMAND_SUBMIT_JOB_BG:
276   case GEARMAN_COMMAND_SUBMIT_JOB_HIGH:
277   case GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG:
278   case GEARMAN_COMMAND_SUBMIT_JOB_LOW:
279   case GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG:
280     if (!tree) break;
281     start_offset = offset;
282     len = tvb_strsize(tvb, start_offset);
283     proto_tree_add_item(content_tree, hf_gearman_func_name, tvb, start_offset, len, ENC_NA|ENC_ASCII);
284
285     start_offset += len;
286     len = tvb_strsize(tvb, start_offset);
287     proto_tree_add_item(content_tree, hf_gearman_uniq_id, tvb, start_offset, len, ENC_NA|ENC_ASCII);
288
289     proto_tree_add_item(content_tree, hf_gearman_argument, tvb, start_offset+len, size-start_offset+GEARMAN_COMMAND_HEADER_SIZE, ENC_NA|ENC_ASCII);
290     break;
291
292   case GEARMAN_COMMAND_SUBMIT_JOB_SCHED:
293     if (!tree) break;
294     start_offset = offset;
295     len = tvb_strsize(tvb, start_offset);
296     proto_tree_add_item(content_tree, hf_gearman_func_name, tvb, start_offset, len, ENC_NA|ENC_ASCII);
297
298     start_offset += len;
299     len = tvb_strsize(tvb, start_offset);
300     proto_tree_add_item(content_tree, hf_gearman_uniq_id, tvb, start_offset, len, ENC_NA|ENC_ASCII);
301
302     start_offset += len;
303     len = tvb_strsize(tvb, start_offset);
304     proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_minute, tvb, start_offset, len, ENC_NA|ENC_ASCII);
305
306     start_offset += len;
307     len = tvb_strsize(tvb, start_offset);
308     proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_hour, tvb, start_offset, len, ENC_NA|ENC_ASCII);
309
310     start_offset += len;
311     len = tvb_strsize(tvb, start_offset);
312     proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_day_of_month, tvb, start_offset, len, ENC_NA|ENC_ASCII);
313
314     start_offset += len;
315     len = tvb_strsize(tvb, start_offset);
316     proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_month, tvb, start_offset, len, ENC_NA|ENC_ASCII);
317
318     start_offset += len;
319     len = tvb_strsize(tvb, start_offset);
320     proto_tree_add_item(content_tree, hf_gearman_submit_job_sched_day_of_week, tvb, start_offset, len, ENC_NA|ENC_ASCII);
321
322     proto_tree_add_item(content_tree, hf_gearman_argument, tvb, start_offset+len, size-start_offset+GEARMAN_COMMAND_HEADER_SIZE, ENC_NA|ENC_ASCII);
323     break;
324
325   case GEARMAN_COMMAND_SUBMIT_JOB_EPOCH:
326     if (!tree) break;
327     start_offset = offset;
328     len = tvb_strsize(tvb, start_offset);
329     proto_tree_add_item(content_tree, hf_gearman_func_name, tvb, start_offset, len, ENC_NA|ENC_ASCII);
330
331     start_offset += len;
332     len = tvb_strsize(tvb, start_offset);
333     proto_tree_add_item(content_tree, hf_gearman_uniq_id, tvb, start_offset, len, ENC_NA|ENC_ASCII);
334
335     start_offset += len;
336     len = tvb_strsize(tvb, start_offset);
337     proto_tree_add_item(content_tree, hf_gearman_submit_job_epoch_time, tvb, start_offset, len, ENC_NA|ENC_ASCII);
338
339     proto_tree_add_item(content_tree, hf_gearman_argument, tvb, start_offset+len, size-start_offset+GEARMAN_COMMAND_HEADER_SIZE, ENC_NA|ENC_ASCII);
340     break;
341
342   case GEARMAN_COMMAND_JOB_ASSIGN:
343     start_offset = offset;
344     len = tvb_strsize(tvb, start_offset);
345     proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, start_offset, len, ENC_NA|ENC_ASCII);
346
347     start_offset += len;
348     len = tvb_strsize(tvb, start_offset);
349     proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, start_offset, len, ENC_NA|ENC_ASCII);
350
351     proto_tree_add_item(content_tree, hf_gearman_argument, tvb, start_offset+len, size-start_offset+GEARMAN_COMMAND_HEADER_SIZE, ENC_NA|ENC_ASCII);
352     break;
353
354   case GEARMAN_COMMAND_JOB_ASSIGN_UNIQ:
355     start_offset = offset;
356     len = tvb_strsize(tvb, start_offset);
357     proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, start_offset, len, ENC_NA|ENC_ASCII);
358
359     start_offset += len;
360     len = tvb_strsize(tvb, start_offset);
361     proto_tree_add_item(content_tree, hf_gearman_func_namez, tvb, start_offset, len, ENC_NA|ENC_ASCII);
362
363     start_offset += len;
364     len = tvb_strsize(tvb, start_offset);
365     proto_tree_add_item(content_tree, hf_gearman_uniq_id, tvb, start_offset, len, ENC_NA|ENC_ASCII);
366
367     proto_tree_add_item(content_tree, hf_gearman_argument, tvb, start_offset+len, size-start_offset+GEARMAN_COMMAND_HEADER_SIZE, ENC_NA|ENC_ASCII);
368     break;
369
370   case GEARMAN_COMMAND_CAN_DO:
371   case GEARMAN_COMMAND_CANT_DO:
372     if (!tree) break;
373     proto_tree_add_item(content_tree, hf_gearman_func_name, tvb,
374                offset, size, ENC_NA|ENC_ASCII);
375     break;
376
377   case GEARMAN_COMMAND_CAN_DO_TIMEOUT:
378     if (!tree) break;
379     proto_tree_add_item(content_tree, hf_gearman_func_name, tvb,
380                offset, tvb_strsize(tvb, offset), ENC_NA|ENC_ASCII);
381     break;
382
383   case GEARMAN_COMMAND_WORK_DATA:
384   case GEARMAN_COMMAND_WORK_WARNING:
385   case GEARMAN_COMMAND_WORK_COMPLETE:
386   case GEARMAN_COMMAND_WORK_EXCEPTION:
387     if (!tree) break;
388     len = tvb_strsize(tvb, offset);
389     proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, offset, len, ENC_NA|ENC_ASCII);
390     proto_tree_add_item(content_tree, hf_gearman_result, tvb, offset+len, size-len, ENC_NA|ENC_ASCII);
391     break;
392
393   case GEARMAN_COMMAND_WORK_STATUS:
394     if (!tree) break;
395     start_offset = offset;
396     len = tvb_strsize(tvb, start_offset);
397     proto_tree_add_item(content_tree, hf_gearman_job_handlez, tvb, start_offset, len, ENC_NA|ENC_ASCII);
398
399     start_offset += len;
400     len = tvb_strsize(tvb, start_offset);
401     proto_tree_add_item(content_tree, hf_gearman_complete_numerator, tvb, start_offset, len, ENC_NA|ENC_ASCII);
402
403     proto_tree_add_item(content_tree, hf_gearman_complete_denominator, tvb, start_offset+len, size-start_offset+GEARMAN_COMMAND_HEADER_SIZE, ENC_NA|ENC_ASCII);
404     break;
405
406   case GEARMAN_COMMAND_SET_CLIENT_ID:
407     if (!tree) break;
408     proto_tree_add_item(content_tree, hf_gearman_client_id, tvb,
409                offset, size, ENC_NA|ENC_ASCII);
410     break;
411
412   default:
413     if (size > 0)
414       expert_add_info(pinfo, content_item, &ei_gearman_pkt_type_unknown);
415   }
416
417   col_set_fence(pinfo->cinfo, COL_INFO);
418   return tvb_length(tvb);
419 }
420
421 static void
422 dissect_management_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
423 {
424   int i, type = 0, cmdlen, linelen, offset = 0, next_offset = 0;
425   proto_item *ti;
426   proto_tree *gearman_tree;
427
428   col_set_str(pinfo->cinfo, COL_PROTOCOL, "Gearman");
429   col_clear(pinfo->cinfo, COL_INFO);
430
431   ti = proto_tree_add_item(tree, proto_gearman, tvb, 0, -1, ENC_NA);
432   gearman_tree = proto_item_add_subtree(ti, ett_gearman);
433
434   while ((linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE)) > 0)
435   {
436     for (i=0; i<GEARMAN_MGR_CMDS_COUNT; i++)
437     {
438       /* the array is a const and clearly none of the elements are longer than
439        * MAX_SIGNED_INT so this is a safe cast */
440       cmdlen = (int)strlen(GEARMAN_MGR_CMDS[i]);
441
442       if (cmdlen == linelen && 0 == tvb_strneql(tvb, offset, GEARMAN_MGR_CMDS[i], cmdlen))
443       {
444         proto_tree_add_item(gearman_tree, hf_gearman_mgr_cmd, tvb, offset, cmdlen, ENC_ASCII|ENC_NA);
445         col_add_fstr(pinfo->cinfo, COL_INFO, "[MGR] %s", tvb_get_string_enc(wmem_packet_scope(), tvb, offset, linelen, ENC_ASCII));
446         type = 1;
447         break;
448       }
449     }
450
451     if (GEARMAN_MGR_CMDS_COUNT == i)
452     {
453       proto_tree_add_format_text(gearman_tree, tvb, offset, next_offset - offset);
454
455       if (type == 0)
456       {
457         col_add_fstr(pinfo->cinfo, COL_INFO, "[MGR] %s", tvb_get_string_enc(wmem_packet_scope(), tvb, offset, linelen, ENC_ASCII));
458         type = -1;
459       }
460       else
461       {
462         col_append_sep_fstr(pinfo->cinfo, COL_INFO, ",", "%s", tvb_get_string_enc(wmem_packet_scope(), tvb, offset, linelen, ENC_ASCII));
463       }
464     }
465
466     offset = next_offset;
467   }
468 }
469
470 static int
471 dissect_gearman(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
472 {
473   if ((0 == tvb_memeql(tvb, 0, GEARMAN_MAGIC_CODE_REQUEST, 4)) ||
474       (0 == tvb_memeql(tvb, 0, GEARMAN_MAGIC_CODE_RESPONSE, 4)))
475   {
476     tcp_dissect_pdus(tvb, pinfo, tree, gearman_desegment, GEARMAN_COMMAND_HEADER_SIZE, get_gearman_pdu_len, dissect_binary_packet, data);
477   }
478   else
479   {
480     dissect_management_packet(tvb, pinfo, tree);
481   }
482
483   return tvb_length(tvb);
484 }
485
486 void
487 proto_register_gearman(void)
488 {
489   static hf_register_info hf[] = {
490     { &hf_gearman_mgr_cmd, { "Management Command", "gearman.mgr_cmd", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
491     { &hf_gearman_magic_code, { "Magic Code", "gearman.magic_code", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
492     { &hf_gearman_pkt_type, { "Packet Type", "gearman.pkt_type", FT_UINT32, BASE_DEC_HEX, VALS(gearman_command_names), 0x0, NULL, HFILL} },
493     { &hf_gearman_data_size, { "Data Length", "gearman.data_size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} },
494     { &hf_gearman_data_content, { "Data Content", "gearman.data_content", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
495     { &hf_gearman_option_name, { "Option Name", "gearman.opt.name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
496     { &hf_gearman_func_name, { "Function Name", "gearman.func.name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
497     { &hf_gearman_func_namez, { "Function Name", "gearman.func.name", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
498     { &hf_gearman_client_id, { "Client ID", "gearman.client_id", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
499     { &hf_gearman_uniq_id, { "Unique ID", "gearman.uniq_id", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
500     { &hf_gearman_argument, { "Function Argument", "gearman.func.arg", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
501     { &hf_gearman_job_handle, { "Job Handle", "gearman.job.handle", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
502     { &hf_gearman_job_handlez, { "Job Handle", "gearman.job.handle", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
503     { &hf_gearman_complete_numerator, { "Complete Numerator", "gearman.numerator", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
504     { &hf_gearman_complete_denominator, { "Complete Denominator", "gearman.denominator", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
505     { &hf_gearman_submit_job_sched_minute, { "Minute", "gearman.submit_job_sched.minute", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
506     { &hf_gearman_submit_job_sched_hour, { "Hour", "gearman.submit_job_sched.hour", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
507     { &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} },
508     { &hf_gearman_submit_job_sched_month, { "Month", "gearman.submit_job_sched.month", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
509     { &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} },
510     { &hf_gearman_submit_job_epoch_time, { "Epoch Time", "gearman.submit_job.epoch_time", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
511     { &hf_gearman_result, { "Function Result", "gearman.func.result", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
512     { &hf_gearman_known_status, { "Known job", "gearman.job.known", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
513     { &hf_gearman_running_status, { "Running Job", "gearman.job.running", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
514     { &hf_gearman_echo_text, { "Echo Text", "gearman.echo_text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
515     { &hf_gearman_err_code, { "Error Code", "gearman.err.code", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} },
516     { &hf_gearman_err_text, { "Error Text", "gearman.err.text", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL} }
517   };
518
519   /* Setup protocol subtree array */
520   static gint *ett[] = {
521     &ett_gearman,
522     &ett_gearman_command,
523     &ett_gearman_content
524   };
525
526   static ei_register_info ei[] = {
527      { &ei_gearman_pkt_type_unknown, { "gearman.pkt_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown command", EXPFILL }},
528   };
529
530   module_t *gearman_module;
531   expert_module_t* expert_gearman;
532
533   proto_gearman = proto_register_protocol("Gearman Protocol", "Gearman", "gearman");
534
535   proto_register_field_array(proto_gearman, hf, array_length(hf));
536   proto_register_subtree_array(ett, array_length(ett));
537   expert_gearman = expert_register_protocol(proto_gearman);
538   expert_register_field_array(expert_gearman, ei, array_length(ei));
539
540   gearman_module = prefs_register_protocol(proto_gearman, NULL);
541   prefs_register_bool_preference(gearman_module, "desegment",
542                                   "Desegment all Gearman messages spanning multiple TCP segments",
543                                   "Whether the Gearman dissector should desegment all messages spanning multiple TCP segments",
544                                   &gearman_desegment);
545
546 }
547
548 void
549 proto_reg_handoff_gearman(void)
550 {
551   dissector_handle_t gearman_handle;
552
553   gearman_handle = new_create_dissector_handle(dissect_gearman, proto_gearman);
554   dissector_add_uint("tcp.port", GEARMAN_PORT, gearman_handle);
555 }
556
557 /*
558  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
559  *
560  * Local variables:
561  * c-basic-offset: 2
562  * tab-width: 8
563  * indent-tabs-mode: nil
564  * End:
565  *
566  * vi: set shiftwidth=2 tabstop=8 expandtab:
567  * :indentSize=2:tabSize=8:noTabs=true:
568  */