Ethereal-dev: [Ethereal-dev] [patch] network dissector for rsync

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Brad Hards <bhards@xxxxxxxxxxxxxx>
Date: Sun, 16 Feb 2003 21:40:00 +1100
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

G'day.

Please find attached a basic ethereal dissector for the rsync network 
(client/server) protocol. I have only lightly tested it.

I am not sure how much more time I can spend on this, so I am releasing it "as 
is". Admitedly, this dissector doesn't do a real lot, but it should provide a 
basis for identifying further work required, including bug fixes :) 

Please review and apply.

Brad
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQE+T2qBW6pHgIdAuOMRAtsQAJ4ge4dyS+LmMC6gi4kHxlk/Vdu6uQCffnzV
zSSr4peT3oR0F/PyqYNKaL8=
=mRpk
-----END PGP SIGNATURE-----
diff -Naur -X ethereal-dontdiff clean/ethereal-0.9.9/Makefile.am ethereal-0.9.9/Makefile.am
--- clean/ethereal-0.9.9/Makefile.am	2003-01-24 10:50:12.000000000 +1100
+++ ethereal-0.9.9/Makefile.am	2003-02-15 20:42:07.000000000 +1100
@@ -317,6 +317,7 @@
 	packet-rpl.c   \
 	packet-rquota.c \
 	packet-rsh.c   \
+	packet-rsync.c \
 	packet-rstat.c \
 	packet-rsvp.c  \
 	packet-rtcp.c  \
diff -Naur -X ethereal-dontdiff clean/ethereal-0.9.9/Makefile.nmake ethereal-0.9.9/Makefile.nmake
--- clean/ethereal-0.9.9/Makefile.nmake	2003-01-23 13:45:42.000000000 +1100
+++ ethereal-0.9.9/Makefile.nmake	2003-02-15 20:42:26.000000000 +1100
@@ -260,6 +260,7 @@
 	packet-rpl.c   \
 	packet-rquota.c \
 	packet-rsh.c   \
+	packet-rsync.c \
 	packet-rstat.c \
 	packet-rsvp.c  \
 	packet-rtcp.c  \
diff -Naur -X ethereal-dontdiff clean/ethereal-0.9.9/packet-rsync.c ethereal-0.9.9/packet-rsync.c
--- clean/ethereal-0.9.9/packet-rsync.c	1970-01-01 10:00:00.000000000 +1000
+++ ethereal-0.9.9/packet-rsync.c	2003-02-16 21:34:15.000000000 +1100
@@ -0,0 +1,335 @@
+/* packet-rsync.c
+ * Routines for rsync dissection
+ * [ very rough, but mininally functional ]
+ * Copyright 2003, Brad Hards <bradh@xxxxxxxxxxxxx>
+ *
+ * $Id: README.developer,v 1.65 2002/11/09 08:37:00 guy Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+#include <time.h>
+#include <glib.h>
+
+#include <epan/packet.h>
+#include <epan/strutil.h>
+#include <epan/conversation.h>
+
+#include "prefs.h"
+
+
+/* what states make sense here ? */
+typedef enum _rsync_state {
+  RSYNC_INIT = 0,
+  RSYNC_SERV_INIT = 1,
+  RSYNC_CLIENT_QUERY = 2,
+  RSYNC_SERV_RESPONSE = 4,
+  RSYNC_COMMAND = 5,
+  RSYNC_SERV_MOTD = 6,
+  RSYNC_DATA = 7,
+} rsync_state_t;
+
+static gboolean rsync_desegment = TRUE;
+
+/* this is a guide to the current conversation state */
+struct rsync_conversation_data {
+    rsync_state_t 	state;
+};
+
+struct rsync_frame_data {
+    rsync_state_t 	state;
+};
+
+static int proto_rsync = -1;
+
+static int hf_rsync_hdr_magic = -1;
+static int hf_rsync_hdr_version = -1;
+static int hf_rsync_query_string = -1;
+static int hf_rsync_motd_string = -1;
+static int hf_rsync_response_string = -1;
+static int hf_rsync_rsyncdok_string = -1;
+static int hf_rsync_command_string = -1;
+static int hf_rsync_data = -1;
+
+static gint ett_rsync = -1;
+
+dissector_handle_t rsync_handle;
+
+
+#define TCP_PORT_RSYNC	873
+
+static unsigned int glb_rsync_tcp_port = TCP_PORT_RSYNC;
+
+/* Packet dissection routine called by tcp (& udp) when port 873 detected */
+static void
+dissect_rsync_encap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+		    gboolean desegment)
+{
+    conversation_t			*conversation;
+    struct rsync_conversation_data	*conversation_data;
+    struct rsync_frame_data		*frame_data;
+    proto_item				*ti;
+    proto_tree				*rsync_tree;
+    int					offset = 0;
+    gchar				version[5];
+    gchar				auth_string[10];
+    guint				buff_length;
+    gchar				magic_string[14];
+
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, "RSYNC");
+
+    if (check_col(pinfo->cinfo, COL_INFO))
+        col_clear(pinfo->cinfo, COL_INFO);
+
+    conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
+				     pinfo->srcport, pinfo->destport, 0);
+    if (conversation == NULL) {
+	conversation = conversation_new(&pinfo->src, &pinfo->dst,
+					pinfo->ptype, pinfo->srcport,
+					pinfo->destport, 0);
+	conversation_data = malloc(sizeof(struct rsync_conversation_data));
+	conversation_data->state = RSYNC_INIT;
+	conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+    }
+
+    conversation_set_dissector(conversation, rsync_handle);
+   
+    ti = proto_tree_add_item(tree, proto_rsync, tvb, 0, -1, FALSE);
+
+    rsync_tree = proto_item_add_subtree(ti, ett_rsync);
+
+    conversation_data = conversation_get_proto_data(conversation, proto_rsync);
+
+    frame_data = p_get_proto_data(pinfo->fd, proto_rsync);
+    if (!frame_data) {
+	/* then we haven't seen this frame before */
+	frame_data = malloc(sizeof(struct rsync_frame_data));
+	frame_data->state = conversation_data->state;
+	p_add_proto_data(pinfo->fd, proto_rsync, frame_data);
+    }
+
+    switch (frame_data->state) {
+    case RSYNC_INIT:
+	proto_tree_add_item(rsync_tree, hf_rsync_hdr_magic, tvb, offset, 8, TRUE);
+	offset += 8;
+	proto_tree_add_item(rsync_tree, hf_rsync_hdr_version, tvb, offset, 4, TRUE);
+	tvb_get_nstringz0(tvb, offset, 3, version);
+	offset += 4;
+
+        if (check_col(pinfo->cinfo, COL_INFO)) {
+            col_append_fstr(pinfo->cinfo, COL_INFO,
+			    "Client Initialisation (Version %s)",
+			    version);
+	}
+
+	conversation_data->state = RSYNC_SERV_INIT;
+        conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+
+	break;
+    case RSYNC_SERV_INIT:
+	proto_tree_add_item(rsync_tree, hf_rsync_hdr_magic, tvb, offset, 8, TRUE);
+	offset += 8;
+	proto_tree_add_item(rsync_tree, hf_rsync_hdr_version, tvb, offset, 4, TRUE);
+	tvb_get_nstringz0(tvb, offset, 3, version);
+	offset += 4;
+
+        if (check_col(pinfo->cinfo, COL_INFO)) {
+            col_append_fstr(pinfo->cinfo, COL_INFO,
+			    "Server Initialisation (Version %s)",
+			    version);
+	}
+
+	conversation_data->state = RSYNC_CLIENT_QUERY;
+        conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+
+	break;
+    case RSYNC_CLIENT_QUERY:
+	proto_tree_add_item(rsync_tree, hf_rsync_query_string, tvb, offset, -1, TRUE);
+
+        if (check_col(pinfo->cinfo, COL_INFO)) {
+	  col_append_str(pinfo->cinfo, COL_INFO, "Client Query");
+	}
+
+	conversation_data->state = RSYNC_SERV_MOTD;
+        conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+
+	break;
+    case RSYNC_SERV_MOTD:
+	proto_tree_add_item(rsync_tree, hf_rsync_motd_string, tvb, offset, -1, TRUE);
+
+        if (check_col(pinfo->cinfo, COL_INFO)) {
+	  col_append_fstr(pinfo->cinfo, COL_INFO, "Server MOTD");
+	}
+
+	conversation_data->state = RSYNC_SERV_RESPONSE;
+        conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+
+	break;
+    case RSYNC_SERV_RESPONSE:
+        /* there are two cases - file list, or authentication */
+        tvb_get_nstringz0(tvb, offset, 8, auth_string);
+	if (0 == strncmp("@RSYNCD:", auth_string, 8)) {
+	  /* matches, so we assume its an authentication message */
+	  /* needs to handle the AUTHREQD case, but doesn't - FIXME */
+	  proto_tree_add_item(rsync_tree, hf_rsync_rsyncdok_string, tvb, offset, -1, TRUE);
+
+	  if (check_col(pinfo->cinfo, COL_INFO)) {
+	    col_append_str(pinfo->cinfo, COL_INFO, "Authenication");
+	  }
+	  conversation_data->state = RSYNC_COMMAND;
+
+	} else { /*  it didn't match, so it is probably a module list */
+
+	  proto_tree_add_item(rsync_tree, hf_rsync_response_string, tvb, offset, -1, TRUE);
+
+	  if (check_col(pinfo->cinfo, COL_INFO)) {
+	    col_append_fstr(pinfo->cinfo, COL_INFO, "Module list");
+	  }
+
+	  /* we need to check the end of the buffer for magic string */
+	  buff_length = tvb_length_remaining(tvb, offset);
+	  tvb_get_nstringz0(tvb, buff_length-14, 14, magic_string);
+	  if (0 == strncmp("@RSYNCD: EXIT", magic_string, 14)) {
+	    /* that's all, folks */
+	    conversation_data->state = RSYNC_COMMAND;
+	  } else { /* there must be more data */
+	    conversation_data->state = RSYNC_SERV_RESPONSE;
+	  }
+	}
+
+	conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+
+	break;
+
+    case RSYNC_COMMAND:
+        if (pinfo->destport == glb_rsync_tcp_port) {
+	  /* then we are still sending commands */
+	  proto_tree_add_item(rsync_tree, hf_rsync_command_string, tvb, offset, -1, TRUE);
+
+	  if (check_col(pinfo->cinfo, COL_INFO)) {
+	    col_append_str(pinfo->cinfo, COL_INFO, "Command");
+	  }
+
+	  conversation_data->state = RSYNC_COMMAND;
+	  conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+
+	  break;
+	} /* else we fall through to the data phase */
+
+    case RSYNC_DATA:
+      /* then we are still sending commands */
+      proto_tree_add_item(rsync_tree, hf_rsync_data, tvb, offset, -1, TRUE);
+
+      if (check_col(pinfo->cinfo, COL_INFO)) {
+	col_append_str(pinfo->cinfo, COL_INFO, "Data");
+      }
+
+      conversation_data->state = RSYNC_DATA;
+      conversation_add_proto_data(conversation, proto_rsync, conversation_data);
+
+      break;
+
+    }
+
+}
+
+/* Packet dissection routine called by tcp (& udp) when port 873 detected */
+static void
+dissect_rsync(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_rsync_encap(tvb, pinfo, tree, rsync_desegment);
+}
+
+/* Register protocol with Ethereal. */
+void
+proto_register_rsync(void)
+{
+    static hf_register_info hf[] = {
+	{&hf_rsync_hdr_magic,
+	 {"Magic Header", "rsync.hdr_magic",
+	  FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+	{&hf_rsync_hdr_version,
+	 {"Header Version", "rsync.hdr_version",
+	  FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+	{&hf_rsync_query_string,
+	 {"Client Query String", "rsync.query",
+	  FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+	{&hf_rsync_response_string,
+	 {"Server Response String", "rsync.response",
+	  FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+	{&hf_rsync_motd_string,
+	 {"Server MOTD String", "rsync.motd",
+	  FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+	{&hf_rsync_rsyncdok_string,
+	 {"RSYNCD Response String", "rsync.response",
+	  FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+	{&hf_rsync_command_string,
+	 {"Command String", "rsync.command",
+	  FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+	{&hf_rsync_data,
+	 {"rsync data", "rsync.data",
+	  FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }
+	},
+
+    };
+
+    static gint *ett[] = {
+	&ett_rsync,
+    };
+
+    module_t *rsync_module;
+
+    proto_rsync = proto_register_protocol("RSYNC File Synchroniser",
+					   "RSYNC", "rsync");
+    proto_register_field_array(proto_rsync, hf, array_length(hf));
+    proto_register_subtree_array(ett, array_length(ett));
+
+    rsync_module = prefs_register_protocol(proto_rsync, NULL);
+    prefs_register_uint_preference(rsync_module, "tcp.port",
+				   "rsync TCP Port",
+				   "Set the TCP port for RSYNC messages",
+				   10,
+				   &glb_rsync_tcp_port);
+    prefs_register_bool_preference(rsync_module, "desegment",
+        "Desegment all RSYNC messages spanning multiple TCP segments",
+	"Whether the RSYNC dissector should desegment all messages spanning multiple TCP segments",
+				   &rsync_desegment);
+}
+void
+proto_reg_handoff_rsync(void)
+{
+    rsync_handle = create_dissector_handle(dissect_rsync, proto_rsync);
+    dissector_add("tcp.port", glb_rsync_tcp_port, rsync_handle);
+}