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