Fixup: tvb_get_string(z) -> tvb_get_string(z)_enc
[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  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #define NEW_PROTO_TREE_API
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 #include <epan/wmem/wmem.h>
37 #include <epan/prefs.h>
38
39 void proto_register_rsync(void);
40
41 #define RSYNCD_MAGIC_HEADER "@RSYNCD:"
42 #define RSYNCD_MAGIC_HEADER_LEN 8
43
44 #define RSYNCD_AUTHREQD "@RSYNCD: AUTHREQD "
45 #define RSYNCD_AUTHREQD_LEN 18
46
47 #define RSYNCD_EXIT "@RSYNCD: EXIT"
48 #define RSYNCD_EXIT_LEN 13
49
50 #define RSYNC_MODULE_LIST_QUERY "\n"
51 #define RSYNC_MODULE_LIST_QUERY_LEN 1
52
53 /* what states make sense here ? */
54 typedef enum _rsync_state {
55     RSYNC_INIT         = 0,
56     RSYNC_SERV_INIT    = 1,
57     RSYNC_CLIENT_QUERY = 2,
58     RSYNC_MODULE_LIST  = 4,
59     RSYNC_COMMAND      = 5,
60     RSYNC_SERV_MOTD    = 6,
61     RSYNC_DATA         = 7
62 } rsync_state_t;
63
64 enum rsync_who {
65     CLIENT,
66     SERVER
67 };
68
69 static gboolean rsync_desegment = TRUE;
70
71 /* this is a guide to the current conversation state */
72 struct rsync_conversation_data {
73     rsync_state_t client_state;
74     rsync_state_t server_state;
75 };
76
77 struct rsync_frame_data {
78     rsync_state_t state;
79 };
80
81 static header_field_info *hfi_rsync = NULL;
82
83 #define RSYNC_HF_INIT HFI_INIT(proto_rsync)
84
85 static header_field_info hfi_rsync_hdr_magic RSYNC_HF_INIT = {
86     "Magic Header", "rsync.hdr_magic",
87     FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL };
88
89 static header_field_info hfi_rsync_hdr_version RSYNC_HF_INIT = {
90     "Header Version", "rsync.hdr_version",
91     FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL };
92
93 static header_field_info hfi_rsync_query_string RSYNC_HF_INIT = {
94     "Client Query String", "rsync.query",
95     FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL };
96
97 static header_field_info hfi_rsync_motd_string RSYNC_HF_INIT = {
98     "Server MOTD String", "rsync.motd",
99     FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL };
100
101 static header_field_info hfi_rsync_module_list_string RSYNC_HF_INIT = {
102     "Server Module List", "rsync.module_list",
103     FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL };
104
105 static header_field_info hfi_rsync_rsyncdok_string RSYNC_HF_INIT = {
106     "RSYNCD Response String", "rsync.response",
107     FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL };
108
109 static header_field_info hfi_rsync_command_string RSYNC_HF_INIT = {
110     "Client Command String", "rsync.command",
111     FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL };
112
113 static header_field_info hfi_rsync_data RSYNC_HF_INIT = {
114     "rsync data", "rsync.data",
115     FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL };
116
117 static gint ett_rsync = -1;
118
119 static dissector_handle_t rsync_handle;
120
121
122 #define TCP_PORT_RSYNC  873
123
124 static guint glb_rsync_tcp_port = TCP_PORT_RSYNC;
125
126 #define VERSION_LEN     4           /* 2 digits for main version; '.'; 1 digit for sub version */
127
128 static void
129 dissect_rsync_version_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rsync_tree, enum rsync_who me)
130 {
131     int   offset = 0;
132     guint8 *version;
133
134     proto_tree_add_item(rsync_tree, &hfi_rsync_hdr_magic, tvb, offset, RSYNCD_MAGIC_HEADER_LEN, ENC_ASCII|ENC_NA);
135     offset += RSYNCD_MAGIC_HEADER_LEN;
136     offset += 1; /* skip the space */
137     proto_tree_add_item(rsync_tree, &hfi_rsync_hdr_version, tvb, offset, VERSION_LEN, ENC_ASCII|ENC_NA);
138     version = tvb_get_string_enc(wmem_packet_scope(),tvb, offset, VERSION_LEN, ENC_ASCII|ENC_NA);
139
140     col_add_fstr(pinfo->cinfo, COL_INFO, "%s Initialisation (Version %s)", (me == SERVER ? "Server" : "Client"), version);
141 }
142
143 /* Packet dissection routine called by tcp (& udp) when port 873 detected */
144 static int
145 dissect_rsync_encap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
146                     gboolean desegment _U_)
147 {
148     conversation_t                 *conversation;
149     struct rsync_conversation_data *conversation_data;
150     struct rsync_frame_data        *rsync_frame_data_p;
151     proto_item                     *ti;
152     proto_tree                     *rsync_tree;
153     enum rsync_who                  me;
154     int                             offset = 0;
155     guint                           buff_length;
156
157     col_set_str(pinfo->cinfo, COL_PROTOCOL, "RSYNC");
158
159     col_clear(pinfo->cinfo, COL_INFO);
160
161     me = pinfo->srcport == glb_rsync_tcp_port ? SERVER : CLIENT;
162
163     conversation = find_or_create_conversation(pinfo);
164
165     conversation_data = (struct rsync_conversation_data *)conversation_get_proto_data(conversation, hfi_rsync->id);
166
167     if (conversation_data == NULL) { /* new conversation */
168     conversation_data = wmem_new(wmem_file_scope(), struct rsync_conversation_data);
169     conversation_data->client_state = RSYNC_INIT;
170     conversation_data->server_state = RSYNC_SERV_INIT;
171     conversation_add_proto_data(conversation, hfi_rsync->id, conversation_data);
172     }
173
174     conversation_set_dissector(conversation, rsync_handle);
175
176     ti = proto_tree_add_item(tree, hfi_rsync, tvb, 0, -1, ENC_NA);
177
178     rsync_tree = proto_item_add_subtree(ti, ett_rsync);
179
180     rsync_frame_data_p = (struct rsync_frame_data *)p_get_proto_data(wmem_file_scope(), pinfo, hfi_rsync->id, 0);
181     if (!rsync_frame_data_p) {
182     /* then we haven't seen this frame before */
183     rsync_frame_data_p = wmem_new(wmem_file_scope(), struct rsync_frame_data);
184     rsync_frame_data_p->state = (me == SERVER) ? conversation_data->server_state : conversation_data->client_state;
185     p_add_proto_data(wmem_file_scope(), pinfo, hfi_rsync->id, 0, rsync_frame_data_p);
186     }
187
188     if (me == SERVER) {
189     switch (rsync_frame_data_p->state) {
190         case RSYNC_SERV_INIT:
191             dissect_rsync_version_header(tvb, pinfo, rsync_tree, me);
192
193             conversation_data->server_state = RSYNC_SERV_MOTD;
194
195             break;
196
197         case RSYNC_SERV_MOTD:
198             proto_tree_add_item(rsync_tree, &hfi_rsync_motd_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
199
200             col_set_str(pinfo->cinfo, COL_INFO, "Server MOTD");
201
202             conversation_data->server_state = RSYNC_SERV_MOTD;
203
204             break;
205
206         case RSYNC_MODULE_LIST:
207             /* there are two cases - file list, or authentication */
208             if (0 == tvb_strneql(tvb, offset, RSYNCD_AUTHREQD, RSYNCD_AUTHREQD_LEN)) {
209                 /* matches, so we assume its an authentication message */
210                 proto_tree_add_item(rsync_tree, &hfi_rsync_rsyncdok_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
211
212                 col_set_str(pinfo->cinfo, COL_INFO, "Authentication");
213                 conversation_data->server_state = RSYNC_DATA;
214
215             } else { /*  it didn't match, so it is probably a module list */
216
217                 proto_tree_add_item(rsync_tree, &hfi_rsync_module_list_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
218
219                 /* we need to check the end of the buffer for magic string */
220                 buff_length = tvb_length_remaining(tvb, offset);
221                 if (buff_length > RSYNCD_EXIT_LEN &&
222                     0 == tvb_strneql(tvb, buff_length-RSYNCD_EXIT_LEN-1, RSYNCD_EXIT, RSYNCD_EXIT_LEN)) {
223             /* that's all, folks */
224             col_set_str(pinfo->cinfo, COL_INFO, "Final module list");
225             conversation_data->server_state = RSYNC_DATA;
226                 } else { /* there must be more data */
227             col_set_str(pinfo->cinfo, COL_INFO, "Module list");
228             conversation_data->server_state = RSYNC_MODULE_LIST;
229                 }
230             }
231
232             break;
233
234         case RSYNC_DATA:
235             proto_tree_add_item(rsync_tree, &hfi_rsync_data, tvb, offset, -1, ENC_NA);
236
237             col_set_str(pinfo->cinfo, COL_INFO, "Data");
238
239             conversation_data->server_state = RSYNC_DATA;
240
241             break;
242
243     default:
244         /* Unknown state */
245         break;
246         }
247     } else { /* me == CLIENT */
248     switch (rsync_frame_data_p->state) {
249         case RSYNC_INIT:
250             dissect_rsync_version_header(tvb, pinfo, rsync_tree, me);
251
252             conversation_data->client_state = RSYNC_CLIENT_QUERY;
253
254             break;
255
256         case RSYNC_CLIENT_QUERY:
257             proto_tree_add_item(rsync_tree, &hfi_rsync_query_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
258
259             col_set_str(pinfo->cinfo, COL_INFO, "Client Query");
260
261             conversation_data->client_state = RSYNC_COMMAND;
262
263             if (tvb_length(tvb) == RSYNC_MODULE_LIST_QUERY_LEN &&
264                 0 == tvb_strneql(tvb, offset, RSYNC_MODULE_LIST_QUERY, RSYNC_MODULE_LIST_QUERY_LEN)) {
265         conversation_data->server_state = RSYNC_MODULE_LIST;
266             } else {
267                 conversation_data->server_state = RSYNC_DATA;
268             }
269
270             break;
271
272         case RSYNC_COMMAND:
273             /* then we are still sending commands */
274             proto_tree_add_item(rsync_tree, &hfi_rsync_command_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
275
276             col_set_str(pinfo->cinfo, COL_INFO, "Client Command");
277
278             conversation_data->client_state = RSYNC_COMMAND;
279
280             break;
281
282         case RSYNC_DATA:
283             /* then we are still sending commands */
284             proto_tree_add_item(rsync_tree, &hfi_rsync_data, tvb, offset, -1, ENC_NA);
285
286             col_set_str(pinfo->cinfo, COL_INFO, "Data");
287
288             conversation_data->client_state = RSYNC_DATA;
289
290             break;
291
292     default:
293         /* Unknown state */
294         break;
295         }
296     }
297     return tvb_length(tvb);
298 }
299
300 /* Packet dissection routine called by tcp (& udp) when port 873 detected */
301 static int
302 dissect_rsync(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
303 {
304     return dissect_rsync_encap(tvb, pinfo, tree, rsync_desegment);
305 }
306
307 /* Register protocol with Wireshark. */
308
309 void proto_reg_handoff_rsync(void);
310
311 void
312 proto_register_rsync(void)
313 {
314 #ifndef HAVE_HFI_SECTION_INIT
315     static header_field_info *hfi[] = {
316         &hfi_rsync_hdr_magic,
317         &hfi_rsync_hdr_version,
318         &hfi_rsync_query_string,
319         &hfi_rsync_module_list_string,
320         &hfi_rsync_motd_string,
321         &hfi_rsync_rsyncdok_string,
322         &hfi_rsync_command_string,
323         &hfi_rsync_data,
324     };
325 #endif
326
327     static gint *ett[] = {
328         &ett_rsync,
329     };
330
331     module_t *rsync_module;
332
333     int proto_rsync;
334
335     proto_rsync = proto_register_protocol("RSYNC File Synchroniser",
336                                           "RSYNC", "rsync");
337     hfi_rsync = proto_registrar_get_nth(proto_rsync);
338
339     proto_register_fields(proto_rsync, hfi, array_length(hfi));
340     proto_register_subtree_array(ett, array_length(ett));
341
342     rsync_module = prefs_register_protocol(proto_rsync, proto_reg_handoff_rsync);
343     prefs_register_uint_preference(rsync_module, "tcp_port",
344                                    "rsync TCP Port",
345                                    "Set the TCP port for RSYNC messages",
346                                    10,
347                                    &glb_rsync_tcp_port);
348     prefs_register_bool_preference(rsync_module, "desegment",
349                                    "Reassemble RSYNC messages spanning multiple TCP segments",
350                                    "Whether the RSYNC dissector should reassemble messages spanning multiple TCP segments."
351                                    " To use this option, you must also enable"
352                                    " \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
353                                    &rsync_desegment);
354
355     rsync_handle = new_create_dissector_handle(dissect_rsync, proto_rsync);
356 }
357 void
358 proto_reg_handoff_rsync(void)
359 {
360     static gboolean initialized = FALSE;
361     static guint    saved_rsync_tcp_port;
362
363     if (!initialized) {
364         initialized = TRUE;
365     } else {
366         dissector_delete_uint("tcp.port", saved_rsync_tcp_port, rsync_handle);
367     }
368
369     dissector_add_uint("tcp.port", glb_rsync_tcp_port, rsync_handle);
370     saved_rsync_tcp_port = glb_rsync_tcp_port;
371 }
372
373 /*
374  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
375  *
376  * Local variables:
377  * c-basic-offset: 4
378  * tab-width: 8
379  * indent-tabs-mode: nil
380  * End:
381  *
382  * vi: set shiftwidth=4 tabstop=8 expandtab:
383  * :indentSize=4:tabSize=8:noTabs=true:
384  */