2 * Routines for rsync dissection
3 * [ very rough, but mininally functional ]
4 * Copyright 2003, Brad Hards <bradh@frogmouth.net>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
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.
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.
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.
33 #include <epan/packet.h>
34 #include <epan/strutil.h>
35 #include <epan/conversation.h>
37 #include <epan/prefs.h>
40 /* what states make sense here ? */
41 typedef enum _rsync_state {
44 RSYNC_CLIENT_QUERY = 2,
45 RSYNC_SERV_RESPONSE = 4,
51 static gboolean rsync_desegment = TRUE;
53 /* this is a guide to the current conversation state */
54 struct rsync_conversation_data {
58 struct rsync_frame_data {
62 static int proto_rsync = -1;
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;
73 static gint ett_rsync = -1;
75 static dissector_handle_t rsync_handle;
78 #define TCP_PORT_RSYNC 873
80 static guint glb_rsync_tcp_port = TCP_PORT_RSYNC;
82 /* Packet dissection routine called by tcp (& udp) when port 873 detected */
84 dissect_rsync_encap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
85 gboolean desegment _U_)
87 conversation_t *conversation;
88 struct rsync_conversation_data *conversation_data;
89 struct rsync_frame_data *rsync_frame_data_p;
91 proto_tree *rsync_tree;
94 gchar auth_string[10];
96 gchar magic_string[14];
99 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RSYNC");
101 col_clear(pinfo->cinfo, COL_INFO);
103 conversation = find_or_create_conversation(pinfo);
105 conversation_data = conversation_get_proto_data(conversation, proto_rsync);
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);
113 conversation_set_dissector(conversation, rsync_handle);
115 ti = proto_tree_add_item(tree, proto_rsync, tvb, 0, -1, ENC_NA);
117 rsync_tree = proto_item_add_subtree(ti, ett_rsync);
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);
127 switch (rsync_frame_data_p->state) {
129 proto_tree_add_item(rsync_tree, hf_rsync_hdr_magic, tvb, offset, 8, ENC_ASCII|ENC_NA);
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);
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)",
143 conversation_data->state = RSYNC_SERV_INIT;
146 case RSYNC_SERV_INIT:
147 proto_tree_add_item(rsync_tree, hf_rsync_hdr_magic, tvb, offset, 8, ENC_ASCII|ENC_NA);
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);
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)",
161 conversation_data->state = RSYNC_CLIENT_QUERY;
164 case RSYNC_CLIENT_QUERY:
165 proto_tree_add_item(rsync_tree, hf_rsync_query_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
167 col_append_str(pinfo->cinfo, COL_INFO, "Client Query");
169 conversation_data->state = RSYNC_SERV_MOTD;
172 case RSYNC_SERV_MOTD:
173 proto_tree_add_item(rsync_tree, hf_rsync_motd_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
175 col_append_str(pinfo->cinfo, COL_INFO, "Server MOTD");
177 conversation_data->state = RSYNC_SERV_RESPONSE;
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);
188 col_append_str(pinfo->cinfo, COL_INFO, "Authentication");
189 conversation_data->state = RSYNC_COMMAND;
191 } else { /* it didn't match, so it is probably a module list */
193 proto_tree_add_item(rsync_tree, hf_rsync_response_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
195 col_append_str(pinfo->cinfo, COL_INFO, "Module list");
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;
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);
215 col_append_str(pinfo->cinfo, COL_INFO, "Command");
217 conversation_data->state = RSYNC_COMMAND;
220 } /* else we fall through to the data phase */
223 /* then we are still sending commands */
224 proto_tree_add_item(rsync_tree, hf_rsync_data, tvb, offset, -1, ENC_NA);
226 col_append_str(pinfo->cinfo, COL_INFO, "Data");
228 conversation_data->state = RSYNC_DATA;
236 /* Packet dissection routine called by tcp (& udp) when port 873 detected */
238 dissect_rsync(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
240 dissect_rsync_encap(tvb, pinfo, tree, rsync_desegment);
243 /* Register protocol with Wireshark. */
245 void proto_reg_handoff_rsync(void);
248 proto_register_rsync(void)
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 }
255 {&hf_rsync_hdr_version,
256 {"Header Version", "rsync.hdr_version",
257 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
259 {&hf_rsync_query_string,
260 {"Client Query String", "rsync.query",
261 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
263 {&hf_rsync_response_string,
264 {"Server Response String", "rsync.response",
265 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
267 {&hf_rsync_motd_string,
268 {"Server MOTD String", "rsync.motd",
269 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
271 {&hf_rsync_rsyncdok_string,
272 {"RSYNCD Response String", "rsync.response",
273 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
275 {&hf_rsync_command_string,
276 {"Command String", "rsync.command",
277 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
280 {"rsync data", "rsync.data",
281 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
286 static gint *ett[] = {
290 module_t *rsync_module;
292 proto_rsync = proto_register_protocol("RSYNC File Synchroniser",
294 proto_register_field_array(proto_rsync, hf, array_length(hf));
295 proto_register_subtree_array(ett, array_length(ett));
297 rsync_module = prefs_register_protocol(proto_rsync, proto_reg_handoff_rsync);
298 prefs_register_uint_preference(rsync_module, "tcp_port",
300 "Set the TCP port for RSYNC messages",
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.",
310 proto_reg_handoff_rsync(void)
312 static gboolean initialized = FALSE;
313 static guint saved_rsync_tcp_port;
316 rsync_handle = create_dissector_handle(dissect_rsync, proto_rsync);
319 dissector_delete_uint("tcp.port", saved_rsync_tcp_port, rsync_handle);
322 dissector_add_uint("tcp.port", glb_rsync_tcp_port, rsync_handle);
323 saved_rsync_tcp_port = glb_rsync_tcp_port;