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