From Didier Gautheron:
[obnox/wireshark/wip.git] / epan / dissectors / packet-distcc.c
1 /* packet-distcc.c
2  * Routines for distcc dissection
3  * Copyright 2003, Brad Hards <bradh@frogmouth.net>
4  * Copyright 2003, Ronnie Sahlberg, added TCP desegmentation.
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26 /* This dissector supports version 1 of the DISTCC protocol */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34
35 #include <string.h>
36 #include <time.h>
37 #include <glib.h>
38
39 #include <epan/packet.h>
40 #include <epan/strutil.h>
41
42 #include <epan/prefs.h>
43
44
45 static int proto_distcc = -1;
46 static int hf_distcc_version = -1;
47 static int hf_distcc_argc = -1;
48 static int hf_distcc_argv = -1;
49 static int hf_distcc_doti_source = -1;
50 static int hf_distcc_stat = -1;
51 static int hf_distcc_serr = -1;
52 static int hf_distcc_sout = -1;
53 static int hf_distcc_doto_object = -1;
54
55
56 static gint ett_distcc = -1;
57
58 static dissector_handle_t data_handle;
59
60
61 static gboolean distcc_desegment = TRUE;
62
63
64 #define TCP_PORT_DISTCC 3632
65
66 static guint glb_distcc_tcp_port = TCP_PORT_DISTCC;
67
68 extern void proto_reg_handoff_distcc(void);
69
70 #define CHECK_PDU_LEN(x) \
71         if(parameter>tvb_length_remaining(tvb, offset) || parameter < 1){\
72                 len=tvb_length_remaining(tvb, offset);\
73                 if (check_col(pinfo->cinfo, COL_INFO)) {\
74                         col_append_str(pinfo->cinfo, COL_INFO, "[Short" x " PDU]");\
75                 }\
76         } \
77         tvb_ensure_bytes_exist(tvb, offset, len);
78
79
80 #define DESEGMENT_TCP(x) \
81         if(distcc_desegment && pinfo->can_desegment){\
82                 /* only attempt reassembly if whe have the full segment */\
83                 if(tvb_length_remaining(tvb, offset)==tvb_reported_length_remaining(tvb, offset)){\
84                         if(parameter>tvb_length_remaining(tvb, offset)){\
85                                 proto_tree_add_text(tree, tvb, offset-12, -1, "[Short " x " PDU]");\
86                                 pinfo->desegment_offset=offset-12;\
87                                 pinfo->desegment_len=parameter-tvb_length_remaining(tvb, offset);\
88                                 return offset+len;\
89                         }\
90                 }\
91         }
92
93
94
95
96
97 static int
98 dissect_distcc_dist(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 parameter)
99 {
100         proto_tree_add_uint_format(tree, hf_distcc_version, tvb, offset-12, 12, parameter, "DIST: %d", parameter);
101
102         if (check_col(pinfo->cinfo, COL_INFO)) {
103                 col_append_fstr(pinfo->cinfo, COL_INFO, "DIST:%d ", parameter);
104         }
105
106         return offset;
107 }
108
109 static int
110 dissect_distcc_done(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 parameter)
111 {
112         proto_tree_add_uint_format(tree, hf_distcc_version, tvb, offset-12, 12, parameter, "DONE: %d", parameter);
113
114         if (check_col(pinfo->cinfo, COL_INFO)) {
115                 col_append_fstr(pinfo->cinfo, COL_INFO, "DONE:%d ", parameter);
116         }
117
118         return offset;
119 }
120
121 static int
122 dissect_distcc_stat(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 parameter)
123 {
124         proto_tree_add_uint_format(tree, hf_distcc_stat, tvb, offset-12, 12, parameter, "STAT: %d", parameter);
125
126         if (check_col(pinfo->cinfo, COL_INFO)) {
127                 col_append_fstr(pinfo->cinfo, COL_INFO, "STAT:%d ", parameter);
128         }
129
130         return offset;
131 }
132
133 static int
134 dissect_distcc_argc(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, guint32 parameter)
135 {
136         proto_tree_add_uint_format(tree, hf_distcc_argc, tvb, offset-12, 12, parameter, "ARGC: %d", parameter);
137
138         if (check_col(pinfo->cinfo, COL_INFO)) {
139                 col_append_fstr(pinfo->cinfo, COL_INFO, "ARGC:%d ", parameter);
140         }
141
142         return offset;
143 }
144
145 static int
146 dissect_distcc_argv(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gint parameter)
147 {
148         char argv[256]; 
149         int argv_len;
150         gint len=parameter;
151
152
153         CHECK_PDU_LEN("ARGV");
154
155         /* see if we need to desegment the PDU */
156         DESEGMENT_TCP("ARGV");
157
158
159
160         argv_len=len>255?255:len;
161         tvb_memcpy(tvb, argv, offset, argv_len);
162         argv[argv_len]=0;
163
164         proto_tree_add_item(tree, hf_distcc_argv, tvb, offset, len, FALSE);
165
166         if (check_col(pinfo->cinfo, COL_INFO)) {
167                 col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", argv);
168         }
169
170         if(len!=parameter){
171                 proto_tree_add_text(tree, tvb, 0, 0, "[Short ARGV PDU]");
172         }
173         return offset+len;
174 }
175
176 static int
177 dissect_distcc_serr(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gint parameter)
178 {
179         char argv[256]; 
180         int argv_len;
181         gint len=parameter;
182
183
184         CHECK_PDU_LEN("SERR");
185
186         /* see if we need to desegment the PDU */
187         DESEGMENT_TCP("SERR");
188
189
190
191         argv_len=len>255?255:len;
192         tvb_memcpy(tvb, argv, offset, argv_len);
193         argv[argv_len]=0;
194
195         proto_tree_add_item(tree, hf_distcc_serr, tvb, offset, len, FALSE);
196
197         if (check_col(pinfo->cinfo, COL_INFO)) {
198                 col_append_fstr(pinfo->cinfo, COL_INFO, "SERR:%s ", argv);
199         }
200
201         if(len!=parameter){
202                 proto_tree_add_text(tree, tvb, 0, 0, "[Short SERR PDU]");
203         }
204         return offset+len;
205 }
206
207 static int
208 dissect_distcc_sout(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gint parameter)
209 {
210         char argv[256]; 
211         int argv_len;
212         gint len=parameter;
213
214
215         CHECK_PDU_LEN("SOUT");
216
217         /* see if we need to desegment the PDU */
218         DESEGMENT_TCP("SOUT");
219
220
221
222         argv_len=len>255?255:len;
223         tvb_memcpy(tvb, argv, offset, argv_len);
224         argv[argv_len]=0;
225
226         proto_tree_add_item(tree, hf_distcc_sout, tvb, offset, len, FALSE);
227
228         if (check_col(pinfo->cinfo, COL_INFO)) {
229                 col_append_fstr(pinfo->cinfo, COL_INFO, "SOUT:%s ", argv);
230         }
231
232         if(len!=parameter){
233                 proto_tree_add_text(tree, tvb, 0, 0, "[Short SOUT PDU]");
234         }
235         return offset+len;
236 }
237
238
239 static int
240 dissect_distcc_doti(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, gint parameter)
241 {
242         gint len=parameter;
243
244
245         CHECK_PDU_LEN("DOTI");
246
247         /* see if we need to desegment the PDU */
248         DESEGMENT_TCP("DOTI");
249
250         col_append_str(pinfo->cinfo, COL_INFO, "DOTI source ");
251
252         proto_tree_add_item(tree, hf_distcc_doti_source, tvb, offset, len, FALSE);
253         if(len!=parameter){
254                 proto_tree_add_text(tree, tvb, 0, 0, "[Short DOTI PDU]");
255         }
256         return offset+len;
257 }
258
259 static int
260 dissect_distcc_doto(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, gint parameter)
261 {
262         gint len=parameter;
263
264
265         CHECK_PDU_LEN("DOTO");
266
267         /* see if we need to desegment the PDU */
268         DESEGMENT_TCP("DOTO");
269
270         col_append_str(pinfo->cinfo, COL_INFO, "DOTO object ");
271
272         proto_tree_add_item(tree, hf_distcc_doto_object, tvb, offset, len, FALSE);
273         if(len!=parameter){
274                 proto_tree_add_text(tree, tvb, 0, 0, "[Short DOTO PDU]");
275         }
276         return offset+len;
277 }
278
279
280
281 /* Packet dissection routine called by tcp (& udp) when port 3632 detected */
282 static void
283 dissect_distcc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
284 {
285         int offset=0;
286         proto_tree *tree=NULL;
287         proto_item *item=NULL;
288         char token[4];
289         guint32 parameter;
290         
291
292         col_set_str(pinfo->cinfo, COL_PROTOCOL, "DISTCC ");
293
294         col_clear(pinfo->cinfo, COL_INFO);
295
296         if (parent_tree) {
297                 item = proto_tree_add_item(parent_tree, proto_distcc, tvb, offset,
298                         -1, FALSE);
299                 tree = proto_item_add_subtree(item, ett_distcc);
300         }
301
302         while(1){
303                 /* we must have at least 12 bytes so we can read the 
304                    token and the parameter */
305                 if(tvb_length_remaining(tvb, offset)<12){
306                         return;
307                 }
308
309                 /* read the token */
310                 tvb_memcpy(tvb, token, offset, 4);
311                 offset+=4;
312
313                 /* read the parameter */
314                 sscanf(tvb_get_ptr(tvb, offset, 8), "%08x", &parameter);
315                 offset+=8;
316
317                 if(!strncmp(token, "DIST", 4)){
318                         offset=dissect_distcc_dist(tvb, pinfo, tree, offset, parameter);
319                 } else if(!strncmp(token, "ARGC", 4)){
320                         offset=dissect_distcc_argc(tvb, pinfo, tree, offset, parameter);
321                 } else if(!strncmp(token, "ARGV", 4)){
322                         offset=dissect_distcc_argv(tvb, pinfo, tree, offset, parameter);
323                 } else if(!strncmp(token, "DOTI", 4)){
324                         offset=dissect_distcc_doti(tvb, pinfo, tree, offset, parameter);
325                 } else if(!strncmp(token, "DONE", 4)){
326                         offset=dissect_distcc_done(tvb, pinfo, tree, offset, parameter);
327                 } else if(!strncmp(token, "STAT", 4)){
328                         offset=dissect_distcc_stat(tvb, pinfo, tree, offset, parameter);
329                 } else if(!strncmp(token, "SERR", 4)){
330                         offset=dissect_distcc_serr(tvb, pinfo, tree, offset, parameter);
331                 } else if(!strncmp(token, "SOUT", 4)){
332                         offset=dissect_distcc_sout(tvb, pinfo, tree, offset, parameter);
333                 } else if(!strncmp(token, "DOTO", 4)){
334                         offset=dissect_distcc_doto(tvb, pinfo, tree, offset, parameter);
335                 } else {
336                         call_dissector(data_handle, tvb, pinfo, tree);
337                         return;
338                 }               
339         }
340
341
342 }
343
344 /* Register protocol with Wireshark. */
345 void
346 proto_register_distcc(void)
347 {
348     static hf_register_info hf[] = {
349         {&hf_distcc_version,
350          {"DISTCC Version", "distcc.version",
351           FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
352         },
353         {&hf_distcc_argc,
354          {"ARGC", "distcc.argc",
355           FT_UINT32, BASE_DEC, NULL, 0x0, "Number of arguments", HFILL }
356         },
357         {&hf_distcc_argv,
358          {"ARGV", "distcc.argv",
359           FT_STRING, BASE_NONE, NULL, 0x0, "ARGV argument", HFILL }
360         },
361         {&hf_distcc_doti_source,
362          {"Source", "distcc.doti_source",
363           FT_STRING, BASE_NONE, NULL, 0x0, "DOTI Preprocessed Source File (.i)", HFILL }
364         },
365         {&hf_distcc_stat,
366          {"Status", "distcc.status",
367           FT_UINT32, BASE_DEC, NULL, 0x0, "Unix wait status for command completion", HFILL }
368         },
369         {&hf_distcc_serr,
370          {"SERR", "distcc.serr",
371           FT_STRING, BASE_NONE, NULL, 0x0, "STDERR output", HFILL }
372         },
373         {&hf_distcc_sout,
374          {"SOUT", "distcc.sout",
375           FT_STRING, BASE_NONE, NULL, 0x0, "STDOUT output", HFILL }
376         },
377         {&hf_distcc_doto_object,
378          {"Object", "distcc.doto_object",
379           FT_BYTES, BASE_NONE, NULL, 0x0, "DOTO Compiled object file (.o)", HFILL }
380         }
381
382         };
383
384         static gint *ett[] = {
385                 &ett_distcc,
386         };
387
388         module_t *distcc_module;
389
390         proto_distcc = proto_register_protocol("Distcc Distributed Compiler",
391                                            "DISTCC", "distcc");
392         proto_register_field_array(proto_distcc, hf, array_length(hf));
393         proto_register_subtree_array(ett, array_length(ett));
394
395         distcc_module = prefs_register_protocol(proto_distcc,
396             proto_reg_handoff_distcc);
397         prefs_register_uint_preference(distcc_module, "tcp.port",
398                                    "DISTCC TCP Port",
399                                    "Set the TCP port for DISTCC messages",
400                                    10,
401                                    &glb_distcc_tcp_port);
402         prefs_register_bool_preference(distcc_module, "desegment_distcc_over_tcp",
403         "Reassemble DISTCC-over-TCP messages\nspanning multiple TCP segments",
404                 "Whether the DISTCC dissector should reassemble messages spanning multiple TCP segments."
405                 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
406                 &distcc_desegment);
407 }
408
409 void
410 proto_reg_handoff_distcc(void)
411 {
412         static gboolean registered_dissector = FALSE;
413         static int distcc_tcp_port;
414         static dissector_handle_t distcc_handle;
415
416         if (!registered_dissector) {
417                 /*
418                  * We haven't registered the dissector yet; get a handle
419                  * for it.
420                  */
421                 distcc_handle = create_dissector_handle(dissect_distcc,
422                     proto_distcc);
423                 data_handle = find_dissector("data");
424                 registered_dissector = TRUE;
425         } else {
426                 /*
427                  * We've registered the dissector with a TCP port number
428                  * of "distcc_tcp_port"; we might be changing the TCP port
429                  * number, so remove that registration.
430                  */
431                 dissector_delete("tcp.port", distcc_tcp_port, distcc_handle);
432         }
433         distcc_tcp_port = glb_distcc_tcp_port;
434         dissector_add("tcp.port", distcc_tcp_port, distcc_handle);
435 }