Move 3 ASN1 dissectors to 'clean' group; move 1 PIDL dissector to 'dirty' group.
[metze/wireshark/wip.git] / epan / dissectors / packet-rsync.c
1 /* packet-rsync.c
2  * Routines for rsync dissection
3  * [ very rough, but mininally functional ]
4  * Copyright 2003, Brad Hards <bradh@frogmouth.net>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26
27 #include "config.h"
28
29 #include <string.h>
30
31 #include <glib.h>
32
33 #include <epan/packet.h>
34 #include <epan/strutil.h>
35 #include <epan/conversation.h>
36
37 #include <epan/prefs.h>
38
39
40 /* what states make sense here ? */
41 typedef enum _rsync_state {
42   RSYNC_INIT = 0,
43   RSYNC_SERV_INIT = 1,
44   RSYNC_CLIENT_QUERY = 2,
45   RSYNC_SERV_RESPONSE = 4,
46   RSYNC_COMMAND = 5,
47   RSYNC_SERV_MOTD = 6,
48   RSYNC_DATA = 7
49 } rsync_state_t;
50
51 static gboolean rsync_desegment = TRUE;
52
53 /* this is a guide to the current conversation state */
54 struct rsync_conversation_data {
55     rsync_state_t       state;
56 };
57
58 struct rsync_frame_data {
59     rsync_state_t       state;
60 };
61
62 static int proto_rsync = -1;
63
64 static int hf_rsync_hdr_magic = -1;
65 static int hf_rsync_hdr_version = -1;
66 static int hf_rsync_query_string = -1;
67 static int hf_rsync_motd_string = -1;
68 static int hf_rsync_response_string = -1;
69 static int hf_rsync_rsyncdok_string = -1;
70 static int hf_rsync_command_string = -1;
71 static int hf_rsync_data = -1;
72
73 static gint ett_rsync = -1;
74
75 static dissector_handle_t rsync_handle;
76
77
78 #define TCP_PORT_RSYNC  873
79
80 static guint glb_rsync_tcp_port = TCP_PORT_RSYNC;
81
82 /* Packet dissection routine called by tcp (& udp) when port 873 detected */
83 static void
84 dissect_rsync_encap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
85                     gboolean desegment _U_)
86 {
87     conversation_t                      *conversation;
88     struct rsync_conversation_data      *conversation_data;
89     struct rsync_frame_data             *rsync_frame_data_p;
90     proto_item                          *ti;
91     proto_tree                          *rsync_tree;
92     int                                 offset = 0;
93     gchar                               version[5];
94     gchar                               auth_string[10];
95     guint                               buff_length;
96     gchar                               magic_string[14];
97     gchar                               *version_out;
98
99     col_set_str(pinfo->cinfo, COL_PROTOCOL, "RSYNC");
100
101     col_clear(pinfo->cinfo, COL_INFO);
102
103     conversation = find_or_create_conversation(pinfo);
104
105     conversation_data = conversation_get_proto_data(conversation, proto_rsync);
106
107     if (conversation_data == NULL) {
108         conversation_data = se_alloc(sizeof(struct rsync_conversation_data));
109         conversation_data->state = RSYNC_INIT;
110         conversation_add_proto_data(conversation, proto_rsync, conversation_data);
111     }
112
113     conversation_set_dissector(conversation, rsync_handle);
114
115     ti = proto_tree_add_item(tree, proto_rsync, tvb, 0, -1, ENC_NA);
116
117     rsync_tree = proto_item_add_subtree(ti, ett_rsync);
118
119     rsync_frame_data_p = p_get_proto_data(pinfo->fd, proto_rsync);
120     if (!rsync_frame_data_p) {
121         /* then we haven't seen this frame before */
122         rsync_frame_data_p = se_alloc(sizeof(struct rsync_frame_data));
123         rsync_frame_data_p->state = conversation_data->state;
124         p_add_proto_data(pinfo->fd, proto_rsync, rsync_frame_data_p);
125     }
126
127     switch (rsync_frame_data_p->state) {
128     case RSYNC_INIT:
129         proto_tree_add_item(rsync_tree, hf_rsync_hdr_magic, tvb, offset, 8, ENC_ASCII|ENC_NA);
130         offset += 8;
131         proto_tree_add_item(rsync_tree, hf_rsync_hdr_version, tvb, offset, 4, ENC_ASCII|ENC_NA);
132         tvb_get_nstringz0(tvb, offset, sizeof(version), version);
133         offset += 4;
134
135         if (check_col(pinfo->cinfo, COL_INFO)) {
136             /* XXX - is this really a string? */
137             version_out = format_text(version, 4);
138             col_append_fstr(pinfo->cinfo, COL_INFO,
139                             "Client Initialisation (Version %s)",
140                             version_out);
141         }
142
143         conversation_data->state = RSYNC_SERV_INIT;
144
145         break;
146     case RSYNC_SERV_INIT:
147         proto_tree_add_item(rsync_tree, hf_rsync_hdr_magic, tvb, offset, 8, ENC_ASCII|ENC_NA);
148         offset += 8;
149         proto_tree_add_item(rsync_tree, hf_rsync_hdr_version, tvb, offset, 4, ENC_ASCII|ENC_NA);
150         tvb_get_nstringz0(tvb, offset, sizeof(version), version);
151         offset += 4;
152
153         if (check_col(pinfo->cinfo, COL_INFO)) {
154             /* XXX - is this really a string? */
155             version_out = format_text(version, 4);
156             col_append_fstr(pinfo->cinfo, COL_INFO,
157                             "Server Initialisation (Version %s)",
158                             version_out);
159         }
160
161         conversation_data->state = RSYNC_CLIENT_QUERY;
162
163         break;
164     case RSYNC_CLIENT_QUERY:
165         proto_tree_add_item(rsync_tree, hf_rsync_query_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
166
167         col_append_str(pinfo->cinfo, COL_INFO, "Client Query");
168
169         conversation_data->state = RSYNC_SERV_MOTD;
170
171         break;
172     case RSYNC_SERV_MOTD:
173         proto_tree_add_item(rsync_tree, hf_rsync_motd_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
174
175         col_append_str(pinfo->cinfo, COL_INFO, "Server MOTD");
176
177         conversation_data->state = RSYNC_SERV_RESPONSE;
178
179         break;
180     case RSYNC_SERV_RESPONSE:
181         /* there are two cases - file list, or authentication */
182         tvb_get_nstringz0(tvb, offset, sizeof(auth_string), auth_string);
183         if (0 == strncmp("@RSYNCD:", auth_string, 8)) {
184           /* matches, so we assume its an authentication message */
185           /* needs to handle the AUTHREQD case, but doesn't - FIXME */
186           proto_tree_add_item(rsync_tree, hf_rsync_rsyncdok_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
187
188           col_append_str(pinfo->cinfo, COL_INFO, "Authentication");
189           conversation_data->state = RSYNC_COMMAND;
190
191         } else { /*  it didn't match, so it is probably a module list */
192
193           proto_tree_add_item(rsync_tree, hf_rsync_response_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
194
195           col_append_str(pinfo->cinfo, COL_INFO, "Module list");
196
197           /* we need to check the end of the buffer for magic string */
198           buff_length = tvb_length_remaining(tvb, offset);
199           tvb_get_nstringz0(tvb, buff_length-14, sizeof(magic_string), magic_string);
200           if (0 == strncmp("@RSYNCD: EXIT", magic_string, 14)) {
201             /* that's all, folks */
202             conversation_data->state = RSYNC_COMMAND;
203           } else { /* there must be more data */
204             conversation_data->state = RSYNC_SERV_RESPONSE;
205           }
206         }
207
208         break;
209
210     case RSYNC_COMMAND:
211         if (pinfo->destport == glb_rsync_tcp_port) {
212           /* then we are still sending commands */
213           proto_tree_add_item(rsync_tree, hf_rsync_command_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
214
215           col_append_str(pinfo->cinfo, COL_INFO, "Command");
216
217           conversation_data->state = RSYNC_COMMAND;
218
219           break;
220         } /* else we fall through to the data phase */
221
222     case RSYNC_DATA:
223       /* then we are still sending commands */
224       proto_tree_add_item(rsync_tree, hf_rsync_data, tvb, offset, -1, ENC_NA);
225
226       col_append_str(pinfo->cinfo, COL_INFO, "Data");
227
228       conversation_data->state = RSYNC_DATA;
229
230       break;
231
232     }
233
234 }
235
236 /* Packet dissection routine called by tcp (& udp) when port 873 detected */
237 static void
238 dissect_rsync(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
239 {
240   dissect_rsync_encap(tvb, pinfo, tree, rsync_desegment);
241 }
242
243 /* Register protocol with Wireshark. */
244
245 void proto_reg_handoff_rsync(void);
246
247 void
248 proto_register_rsync(void)
249 {
250     static hf_register_info hf[] = {
251         {&hf_rsync_hdr_magic,
252          {"Magic Header", "rsync.hdr_magic",
253           FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
254         },
255         {&hf_rsync_hdr_version,
256          {"Header Version", "rsync.hdr_version",
257           FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
258         },
259         {&hf_rsync_query_string,
260          {"Client Query String", "rsync.query",
261           FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
262         },
263         {&hf_rsync_response_string,
264          {"Server Response String", "rsync.response",
265           FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
266         },
267         {&hf_rsync_motd_string,
268          {"Server MOTD String", "rsync.motd",
269           FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
270         },
271         {&hf_rsync_rsyncdok_string,
272          {"RSYNCD Response String", "rsync.response",
273           FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
274         },
275         {&hf_rsync_command_string,
276          {"Command String", "rsync.command",
277           FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
278         },
279         {&hf_rsync_data,
280          {"rsync data", "rsync.data",
281           FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
282         },
283
284     };
285
286     static gint *ett[] = {
287         &ett_rsync,
288     };
289
290     module_t *rsync_module;
291
292     proto_rsync = proto_register_protocol("RSYNC File Synchroniser",
293                                            "RSYNC", "rsync");
294     proto_register_field_array(proto_rsync, hf, array_length(hf));
295     proto_register_subtree_array(ett, array_length(ett));
296
297     rsync_module = prefs_register_protocol(proto_rsync, proto_reg_handoff_rsync);
298     prefs_register_uint_preference(rsync_module, "tcp_port",
299                                    "rsync TCP Port",
300                                    "Set the TCP port for RSYNC messages",
301                                    10,
302                                    &glb_rsync_tcp_port);
303     prefs_register_bool_preference(rsync_module, "desegment",
304             "Reassemble RSYNC messages spanning multiple TCP segments",
305             "Whether the RSYNC dissector should reassemble messages spanning multiple TCP segments."
306             " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
307             &rsync_desegment);
308 }
309 void
310 proto_reg_handoff_rsync(void)
311 {
312     static gboolean initialized = FALSE;
313     static guint saved_rsync_tcp_port;
314
315     if (!initialized) {
316         rsync_handle = create_dissector_handle(dissect_rsync, proto_rsync);
317         initialized = TRUE;
318     } else {
319         dissector_delete_uint("tcp.port", saved_rsync_tcp_port, rsync_handle);
320     }
321
322     dissector_add_uint("tcp.port", glb_rsync_tcp_port, rsync_handle);
323     saved_rsync_tcp_port = glb_rsync_tcp_port;
324 }