summaryrefslogtreecommitdiff
path: root/packet-rpc.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>1999-10-29 01:11:23 +0000
committerGuy Harris <guy@alum.mit.edu>1999-10-29 01:11:23 +0000
commitdc6963e2d4b2b2ca06221b80313326170bb1a1b7 (patch)
tree349f3a8026531057a93589a1e200dfb370228fa8 /packet-rpc.c
parent21c466ed25733e4f13d786458d70bd3e46280b8a (diff)
downloadwireshark-dc6963e2d4b2b2ca06221b80313326170bb1a1b7.tar.gz
Uwe Girlich's ONC RPC and NFS dissectors.
svn path=/trunk/; revision=946
Diffstat (limited to 'packet-rpc.c')
-rw-r--r--packet-rpc.c861
1 files changed, 861 insertions, 0 deletions
diff --git a/packet-rpc.c b/packet-rpc.c
new file mode 100644
index 0000000000..9d456c6fec
--- /dev/null
+++ b/packet-rpc.c
@@ -0,0 +1,861 @@
+/* packet-rpc.c
+ * Routines for rpc dissection
+ * Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
+ *
+ * $Id: packet-rpc.c,v 1.1 1999/10/29 01:11:22 guy Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@unicom.net>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copied from packet-smb.c
+ *
+ * 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
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include "packet.h"
+#include "conversation.h"
+#include "packet-rpc.h"
+
+
+const value_string rpc_msg_type[3] = {
+ { RPC_CALL, "Call" },
+ { RPC_REPLY, "Reply" },
+ { 0, NULL }
+};
+
+const value_string rpc_reply_state[3] = {
+ { MSG_ACCEPTED, "accepted" },
+ { MSG_DENIED, "denied" },
+ { 0, NULL }
+};
+
+const value_string rpc_auth_flavor[5] = {
+ { AUTH_NULL, "AUTH_NULL" },
+ { AUTH_UNIX, "AUTH_UNIX" },
+ { AUTH_SHORT, "AUTH_SHORT" },
+ { AUTH_DES, "AUTH_DES" },
+ { 0, NULL }
+};
+
+const value_string rpc_accept_state[6] = {
+ { SUCCESS, "RPC executed successfully" },
+ { PROG_UNAVAIL, "remote hasn't exported program" },
+ { PROG_MISMATCH, "remote can't support version #" },
+ { PROC_UNAVAIL, "program can't support procedure" },
+ { GARBAGE_ARGS, "procedure can't decode params" },
+ { 0, NULL }
+};
+
+const value_string rpc_reject_state[3] = {
+ { RPC_MISMATCH, "RPC_MISMATCH" },
+ { AUTH_ERROR, "AUTH_ERROR" },
+ { 0, NULL }
+};
+
+const value_string rpc_auth_state[6] = {
+ { AUTH_BADCRED, "bad credential (seal broken)" },
+ { AUTH_REJECTEDCRED, "client must begin new session" },
+ { AUTH_BADVERF, "bad verifier (seal broken)" },
+ { AUTH_REJECTEDVERF, "verifier expired or replayed" },
+ { AUTH_TOOWEAK, "rejected for security reasons" },
+ { 0, NULL }
+};
+
+
+/* the protocol number */
+static int proto_rpc = -1;
+
+
+/* Hash table with info on RPC program numbers */
+GHashTable *rpc_progs;
+
+/* Hash table with info on RPC procedure numbers */
+GHashTable *rpc_procs;
+
+
+/***********************************/
+/* Hash array with procedure names */
+/***********************************/
+
+/* compare 2 keys */
+gint
+rpc_proc_equal(gconstpointer k1, gconstpointer k2)
+{
+ rpc_proc_info_key* key1 = (rpc_proc_info_key*) k1;
+ rpc_proc_info_key* key2 = (rpc_proc_info_key*) k2;
+
+ return ((key1->prog == key2->prog &&
+ key1->vers == key2->vers &&
+ key1->proc == key2->proc) ?
+ TRUE : FALSE);
+}
+
+/* calculate a hash key */
+guint
+rpc_proc_hash(gconstpointer k)
+{
+ rpc_proc_info_key* key = (rpc_proc_info_key*) k;
+
+ return (key->prog ^ (key->vers<<16) ^ (key->proc<<24));
+}
+
+
+/* insert some entries */
+void
+rpc_init_proc_table(guint prog, guint vers, const vsff *proc_table)
+{
+ const vsff *proc;
+
+ for (proc = proc_table ; proc->strptr!=NULL; proc++) {
+ rpc_proc_info_key *key;
+ rpc_proc_info_value *value;
+
+ key = (rpc_proc_info_key *) g_malloc(sizeof(rpc_proc_info_key));
+ key->prog = prog;
+ key->vers = vers;
+ key->proc = proc->value;
+
+ value = (rpc_proc_info_value *) g_malloc(sizeof(rpc_proc_info_value));
+ value->name = proc->strptr;
+ value->dissect_call = proc->dissect_call;
+ value->dissect_reply = proc->dissect_reply;
+
+ g_hash_table_insert(rpc_procs,key,value);
+ }
+}
+
+/*----------------------------------------*/
+/* end of Hash array with procedure names */
+/*----------------------------------------*/
+
+
+/*********************************/
+/* Hash array with program names */
+/*********************************/
+
+/* compare 2 keys */
+gint
+rpc_prog_equal(gconstpointer k1, gconstpointer k2)
+{
+ rpc_prog_info_key* key1 = (rpc_prog_info_key*) k1;
+ rpc_prog_info_key* key2 = (rpc_prog_info_key*) k2;
+
+ return ((key1->prog == key2->prog) ?
+ TRUE : FALSE);
+}
+
+
+/* calculate a hash key */
+guint
+rpc_prog_hash(gconstpointer k)
+{
+ rpc_prog_info_key* key = (rpc_prog_info_key*) k;
+
+ return (key->prog);
+}
+
+
+void
+rpc_init_prog(int proto, guint32 prog, int ett)
+{
+ rpc_prog_info_key *key;
+ rpc_prog_info_value *value;
+
+ key = (rpc_prog_info_key *) g_malloc(sizeof(rpc_prog_info_key));
+ key->prog = prog;
+
+ value = (rpc_prog_info_value *) g_malloc(sizeof(rpc_prog_info_value));
+ value->proto = proto;
+ value->ett = ett;
+ value->progname = proto_registrar_get_abbrev(proto);
+
+ g_hash_table_insert(rpc_progs,key,value);
+}
+
+/*--------------------------------------*/
+/* end of Hash array with program names */
+/*--------------------------------------*/
+
+
+/* Placeholder for future dissectors.
+It should vanish, if they are finally present. Up to this point, this
+minimal variant serves as a detector for RPC services and can even find
+request/reply pairs. */
+
+#define BOOT_PROGRAM 100026
+#define MNT_PROGRAM 100005
+#define NLM_PROGRAM 100021
+#define PMAP_PROGRAM 100000
+#define STAT_PROGRAM 100024
+#define YPBIND_PROGRAM 100007
+#define YPSERV_PROGRAM 100004
+
+static int proto_boot = -1;
+static int proto_mnt = -1;
+static int proto_nlm = -1;
+static int proto_pmap = -1;
+static int proto_stat = -1;
+static int proto_ypbind = -1;
+static int proto_ypserv = -1;
+
+void init_incomplete_dissect(void)
+{
+ proto_boot = proto_register_protocol("Bootparameters", "BOOT");
+ rpc_init_prog(proto_boot, BOOT_PROGRAM, ETT_BOOT);
+
+ proto_mnt = proto_register_protocol("Mount", "MNT");
+ rpc_init_prog(proto_mnt, MNT_PROGRAM, ETT_MNT);
+
+ proto_nlm = proto_register_protocol("Network Lock Manager", "NLM");
+ rpc_init_prog(proto_nlm, NLM_PROGRAM, ETT_NLM);
+
+ proto_pmap = proto_register_protocol("Portmapper", "PMAP");
+ rpc_init_prog(proto_pmap, PMAP_PROGRAM, ETT_PMAP);
+
+ proto_stat = proto_register_protocol("Status", "STAT");
+ rpc_init_prog(proto_stat, STAT_PROGRAM, ETT_STAT);
+
+ proto_ypbind = proto_register_protocol("Yellow Page Bind", "YPBINB");
+ rpc_init_prog(proto_ypbind, YPBIND_PROGRAM, ETT_YPBIND);
+
+ proto_ypserv = proto_register_protocol("Yellow Page Server", "YPSERV");
+ rpc_init_prog(proto_ypserv, YPSERV_PROGRAM, ETT_YPSERV);
+}
+
+
+/*
+ * Init the hash tables. It will be called from ethereal_proto_init().
+ * ethereal_proto_init() calls later proto_init(), which calls
+ * register_all_protocols().
+ * The proto_register_<some rpc program> functions use these hash tables
+ * here, so we need this order!
+ */
+void
+init_dissect_rpc()
+{
+ rpc_progs = g_hash_table_new(rpc_prog_hash, rpc_prog_equal);
+ rpc_procs = g_hash_table_new(rpc_proc_hash, rpc_proc_equal);
+}
+
+
+/* static array, first quick implementation, I'll switch over to GList soon */
+rpc_call_info rpc_call_table[RPC_CALL_TABLE_LENGTH];
+guint32 rpc_call_index = 0;
+guint32 rpc_call_firstfree = 0;
+
+void
+rpc_call_insert(rpc_call_info *call)
+{
+ /* some space left? */
+ if (rpc_call_firstfree<RPC_CALL_TABLE_LENGTH) {
+ /* some space left */
+ /* take the first free entry */
+ rpc_call_index = rpc_call_firstfree;
+ /* increase this limit */
+ rpc_call_firstfree++;
+ /* rpc_call_firstfree may now be RPC_CALL_TABLE_LENGTH */
+ }
+ else {
+ /* no space left */
+ /* the next entry, with wrap around */
+ rpc_call_index = (rpc_call_index+1) % rpc_call_firstfree;
+ }
+
+ /* put the entry in */
+ memcpy(&rpc_call_table[rpc_call_index],call,sizeof(*call));
+ return;
+}
+
+
+rpc_call_info*
+rpc_call_lookup(rpc_call_info *call)
+{
+ int i;
+
+ i = rpc_call_index;
+ do {
+ if (
+ rpc_call_table[i].xid == call->xid &&
+ rpc_call_table[i].conversation == call->conversation
+ ) {
+ return &rpc_call_table[i];
+ }
+ if (rpc_call_firstfree) {
+ /* decrement by one, go to rpc_call_firstfree-1
+ at the start of the list */
+ i = (i-1+rpc_call_firstfree) % rpc_call_firstfree;
+ }
+ } while (i!=rpc_call_index);
+ return NULL;
+}
+
+
+unsigned int
+roundup(unsigned int a)
+{
+ unsigned int mod = a % 4;
+ return a + ((mod)? 4-mod : 0);
+}
+
+
+void
+dissect_rpc_auth( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+{
+ guint flavor;
+ guint length;
+ guint length_full;
+
+ /* both checks are made outside */
+ /* if (!BYTES_ARE_IN_FRAME(offset,8)) return; */
+ flavor = EXTRACT_UINT(pd,offset+0);
+ length = EXTRACT_UINT(pd,offset+4);
+ length_full = roundup(length);
+ /* if (!BYTES_ARE_IN_FRAME(offset+8,full_length)) return; */
+
+ if (tree) {
+ proto_tree_add_text(tree,offset+0,4,
+ "Flavor: %s (%d)", val_to_str(flavor,rpc_auth_flavor,"Unknown"),flavor);
+ proto_tree_add_text(tree,offset+4,4,
+ "Length: %d", length);
+ }
+
+ offset += 8;
+
+ switch (flavor) {
+ case AUTH_UNIX: {
+ guint stamp;
+ guint machinename_count;
+ guint machinename_full;
+ char machinename_buffer[256];
+ guint uid;
+ guint gid;
+ guint gids_count;
+ guint gids_i;
+ guint gids_entry;
+ proto_item *gitem;
+ proto_tree *gtree = NULL;
+
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ stamp = EXTRACT_UINT(pd,offset+0);
+ if (tree)
+ proto_tree_add_text(tree,offset+0,4,
+ "stamp: 0x%08x", stamp);
+ offset += 4;
+
+ /* all the following stuff should go into something
+ like dissect_rpc_string() */
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ machinename_count = EXTRACT_UINT(pd,offset+0);
+ machinename_full = roundup(machinename_count);
+ if (!BYTES_ARE_IN_FRAME(offset+4,machinename_full)) return;
+ if (machinename_count>=sizeof(machinename_buffer)) return;
+ memcpy(machinename_buffer,pd+offset+4,machinename_count);
+ machinename_buffer[machinename_count] = '\0';
+ if (tree)
+ proto_tree_add_text(tree,offset+0,4+machinename_full,
+ "machinename: %s", machinename_buffer);
+ offset += 4 + machinename_full;
+
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ uid = EXTRACT_UINT(pd,offset+0);
+ if (tree)
+ proto_tree_add_text(tree,offset+0,4,
+ "uid: %d", uid);
+ offset += 4;
+
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ gid = EXTRACT_UINT(pd,offset+0);
+ if (tree)
+ proto_tree_add_text(tree,offset+0,4,
+ "gid: %d", gid);
+ offset += 4;
+
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ gids_count = EXTRACT_UINT(pd,offset+0);
+ if (tree) {
+ gitem = proto_tree_add_text(tree, offset, 4+gids_count*4,
+ "gids");
+ gtree = proto_item_add_subtree(gitem, ETT_RPC_GIDS);
+ }
+ offset += 4;
+ if (!BYTES_ARE_IN_FRAME(offset,4*gids_count)) return;
+ for (gids_i = 0 ; gids_i < gids_count ; gids_i++) {
+ gids_entry = EXTRACT_UINT(pd,offset+0);
+ if (gtree)
+ proto_tree_add_text(gtree, offset, 4,
+ "%d", gids_entry);
+ offset+=4;
+ }
+ /* how can I NOW change the gitem to print a list with
+ the first 16 gids? */
+ }
+ break;
+ /*
+ case AUTH_SHORT:
+
+ break;
+ */
+ /* I have no tcpdump file with such a packet to verify the
+ info from the RFC 1050 */
+ /*
+ case AUTH_DES:
+
+ break;
+ */
+ default:
+ if (length_full) {
+ if (tree)
+ proto_tree_add_text(tree,offset,
+ length_full, "opaque data");
+ }
+ }
+}
+
+int
+dissect_rpc_cred( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
+{
+ guint length;
+ guint length_full;
+ proto_item *citem;
+ proto_tree *ctree;
+
+ if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
+ length = EXTRACT_UINT(pd,offset+4);
+ length_full = roundup(length);
+ if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
+
+ if (tree) {
+ citem = proto_tree_add_text(tree, offset, 8+length_full,
+ "Credentials");
+ ctree = proto_item_add_subtree(citem, ETT_RPC_CRED);
+ dissect_rpc_auth(pd, offset, fd, ctree);
+ }
+ offset += 8 + length_full;
+
+ return offset;
+}
+
+
+int
+dissect_rpc_verf( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
+{
+ unsigned int length;
+ unsigned int length_full;
+ proto_item *vitem;
+ proto_tree *vtree;
+
+ if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
+ length = EXTRACT_UINT(pd,offset+4);
+ length_full = roundup(length);
+ if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
+
+ if (tree) {
+ vitem = proto_tree_add_text(tree, offset, 8+length_full,
+ "Verifier");
+ vtree = proto_item_add_subtree(vitem, ETT_RPC_VERF);
+ dissect_rpc_auth(pd, offset, fd, vtree);
+ }
+ offset += 8 + length_full;
+
+ return offset;
+}
+
+
+void
+dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
+ guint32 msg_type, void* info)
+{
+ unsigned int xid;
+ unsigned int rpcvers;
+ unsigned int prog;
+ unsigned int vers = 0;
+ unsigned int proc = 0;
+ int proto = 0;
+ int ett = 0;
+
+ unsigned int reply_state;
+ unsigned int accept_state;
+ unsigned int reject_state;
+
+ char *msg_type_name = NULL;
+ char *progname;
+ char *procname = NULL;
+ static char procname_static[20];
+
+ unsigned int vers_low;
+ unsigned int vers_high;
+
+ unsigned int auth_state;
+
+ proto_item *rpc_item=NULL;
+ proto_tree *rpc_tree = NULL;
+
+ proto_item *pitem=NULL;
+ proto_tree *ptree = NULL;
+ int offset_old = offset;
+
+ rpc_call_info rpc_call_msg;
+ rpc_proc_info_key key;
+ rpc_proc_info_value *value = NULL;
+ conversation_t* conversation;
+
+ /* the last parameter can be either of these two types */
+ rpc_call_info *rpc_call;
+ rpc_prog_info_key rpc_prog_key;
+ rpc_prog_info_value *rpc_prog;
+
+ dissect_function_t *dissect_function = NULL;
+
+ if (check_col(fd, COL_PROTOCOL))
+ col_add_str(fd, COL_PROTOCOL, "RPC");
+
+ if (tree) {
+ rpc_item = proto_tree_add_item(tree, proto_rpc, offset, END_OF_FRAME, NULL);
+ if (rpc_item) {
+ rpc_tree = proto_item_add_subtree(rpc_item, ETT_RPC);
+ }
+ }
+
+ xid = EXTRACT_UINT(pd,offset+0);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+0,4,
+ "XID: 0x%x (%d)", xid, xid);
+ }
+
+ if (check_col(fd, COL_INFO)) {
+ col_add_fstr(fd, COL_INFO, "XID 0x%x", xid);
+ }
+
+ /* we should better compare this with the argument?! */
+ msg_type = EXTRACT_UINT(pd,offset+4);
+ msg_type_name = val_to_str(msg_type,rpc_msg_type,"%d");
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+4,4,
+ "msg_type: %s (%d)",
+ msg_type_name, msg_type);
+ }
+
+ if (check_col(fd, COL_PROTOCOL)) {
+ col_add_fstr(fd, COL_PROTOCOL, "RPC %s",
+ val_to_str(msg_type,rpc_msg_type,"%d"));
+ }
+
+ offset += 8;
+
+ if (msg_type==RPC_CALL) {
+ /* we know already the proto-entry and the ETT-const */
+ rpc_prog = (rpc_prog_info_value*)info;
+ proto = rpc_prog->proto;
+ ett = rpc_prog->ett;
+ progname = rpc_prog->progname;
+
+ rpcvers = EXTRACT_UINT(pd,offset+0);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+0,4,
+ "RPC Version: %d", rpcvers);
+ }
+
+ prog = EXTRACT_UINT(pd,offset+4);
+
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+4,4,
+ "Program: %s (%d)", progname, prog);
+ }
+
+ if (check_col(fd, COL_PROTOCOL)) {
+ col_add_fstr(fd, COL_PROTOCOL,"%s %s",
+ progname,
+ msg_type_name);
+ }
+
+ if (!BYTES_ARE_IN_FRAME(offset+8,4)) return;
+ vers = EXTRACT_UINT(pd,offset+8);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+8,4,
+ "Program Version: %d",vers);
+ }
+
+ if (!BYTES_ARE_IN_FRAME(offset+12,4)) return;
+ proc = EXTRACT_UINT(pd,offset+12);
+
+ key.prog = prog;
+ key.vers = vers;
+ key.proc = proc;
+
+ value = g_hash_table_lookup(rpc_procs,&key);
+ if (value != NULL) {
+ dissect_function = value->dissect_call;
+ procname = value->name;
+ }
+ else {
+ /* happens only with strange program versions or
+ non-existing dissectors */
+ dissect_function = NULL;
+ sprintf(procname_static, "proc-%d", proc);
+ procname = procname_static;
+ }
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+12,4,
+ "Procedure: %s (%d)", procname, proc);
+ }
+
+ if (check_col(fd, COL_PROTOCOL)) {
+ col_add_fstr(fd, COL_PROTOCOL,"%s %s %s",
+ progname,
+ procname,
+ msg_type_name);
+ }
+
+ conversation = find_conversation(&pi.src, &pi.dst, pi.ptype,
+ pi.srcport, pi.destport);
+ if (conversation == NULL) {
+ /* It's not part of any conversation - create a new one. */
+ conversation = conversation_new(&pi.src, &pi.dst, pi.ptype,
+ pi.srcport, pi.destport, NULL);
+ }
+
+ /* prepare the key data */
+ rpc_call_msg.xid = xid;
+ rpc_call_msg.conversation = conversation;
+
+ /* look up the request */
+ if (rpc_call_lookup(&rpc_call_msg)) {
+ /* duplicate request */
+ if (check_col(fd, COL_INFO)) {
+ col_add_fstr(fd, COL_INFO, "dup XID 0x%x", xid);
+ }
+ }
+ else {
+ /* prepare the value data */
+ rpc_call_msg.replies = 0;
+ rpc_call_msg.prog = prog;
+ rpc_call_msg.vers = vers;
+ rpc_call_msg.proc = proc;
+ rpc_call_msg.proc_info = value;
+ /* store it */
+ rpc_call_insert(&rpc_call_msg);
+ }
+
+ offset += 16;
+
+ offset = dissect_rpc_cred(pd, offset, fd, rpc_tree);
+ offset = dissect_rpc_verf(pd, offset, fd, rpc_tree);
+
+ /* go to the next dissector */
+ /* goto dissect_rpc_prog; */
+
+ } /* end of RPC call */
+ else if (msg_type == RPC_REPLY)
+ {
+ /* we know already the type from the calling routine */
+ rpc_call = (rpc_call_info*)info;
+ prog = rpc_call->prog;
+ vers = rpc_call->vers;
+ proc = rpc_call->proc;
+ if (rpc_call->proc_info != NULL) {
+ dissect_function = rpc_call->proc_info->dissect_reply;
+ if (rpc_call->proc_info->name != NULL) {
+ procname = rpc_call->proc_info->name;
+ }
+ else {
+ sprintf(procname_static, "proc-%d", proc);
+ procname = procname_static;
+ }
+ }
+ else {
+ dissect_function = NULL;
+ sprintf(procname_static, "proc-%d", proc);
+ procname = procname_static;
+ }
+ rpc_call->replies++;
+
+ rpc_prog_key.prog = prog;
+ if ((rpc_prog = g_hash_table_lookup(rpc_progs,&rpc_prog_key)) == NULL) {
+ proto = 0;
+ ett = 0;
+ progname = "Unknown";
+ }
+ else {
+ proto = rpc_prog->proto;
+ ett = rpc_prog->ett;
+ progname = rpc_prog->progname;
+ }
+
+ if (check_col(fd, COL_PROTOCOL)) {
+ col_add_fstr(fd, COL_PROTOCOL,"%s %s %s",
+ progname,
+ procname,
+ msg_type_name);
+ }
+
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,0,0,
+ "Program: %s (%d)",
+ progname, prog);
+ proto_tree_add_text(rpc_tree,0,0,
+ "Program Version: %d", vers);
+ proto_tree_add_text(rpc_tree,0,0,
+ "Procedure: %s (%d)", procname, proc);
+ }
+
+ if (rpc_call->replies>1) {
+ if (check_col(fd, COL_INFO)) {
+ col_add_fstr(fd, COL_INFO, "dup XID 0x%x", xid);
+ }
+ }
+
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ reply_state = EXTRACT_UINT(pd,offset+0);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+0, 4,
+ "Reply State: %s (%d)",
+ val_to_str(reply_state,rpc_reply_state,"Unknown"),
+ reply_state);
+ }
+ offset += 4;
+
+ if (reply_state == MSG_ACCEPTED) {
+ offset = dissect_rpc_verf(pd, offset, fd, rpc_tree);
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ accept_state = EXTRACT_UINT(pd,offset+0);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,offset+0, 4,
+ "Accept State: %s (%d)",
+ val_to_str(accept_state,rpc_accept_state,"Unknown"),
+ accept_state);
+ }
+ offset += 4;
+ switch (accept_state) {
+ case SUCCESS:
+ /* now goto the lower protocol */
+ goto dissect_rpc_prog;
+ break;
+ case PROG_MISMATCH:
+ if (!BYTES_ARE_IN_FRAME(offset,8)) return;
+ vers_low = EXTRACT_UINT(pd,offset+0);
+ vers_high = EXTRACT_UINT(pd,offset+4);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,
+ offset+0, 4,
+ "min. Program Version: %d",
+ vers_low);
+ proto_tree_add_text(rpc_tree,
+ offset+4, 4,
+ "max. Program Version: %d",
+ vers_high);
+ }
+ offset += 8;
+ break;
+ default:
+ /* void */
+ break;
+ }
+ } else if (reply_state == MSG_DENIED) {
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ reject_state = EXTRACT_UINT(pd,offset+0);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree, offset+0, 4,
+ "Reject State: %s (%d)",
+ val_to_str(reject_state,rpc_reject_state,"Unknown"),
+ reject_state);
+ }
+ offset += 4;
+
+ if (reject_state==RPC_MISMATCH) {
+ if (!BYTES_ARE_IN_FRAME(offset,8)) return;
+ vers_low = EXTRACT_UINT(pd,offset+0);
+ vers_high = EXTRACT_UINT(pd,offset+4);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,
+ offset+0, 4,
+ "min. RPC Version: %d",
+ vers_low);
+ proto_tree_add_text(rpc_tree,
+ offset+4, 4,
+ "max. RPC Version: %d",
+ vers_high);
+ }
+ offset += 8;
+ } else if (reject_state==AUTH_ERROR) {
+ if (!BYTES_ARE_IN_FRAME(offset,4)) return;
+ auth_state = EXTRACT_UINT(pd,offset+0);
+ if (rpc_tree) {
+ proto_tree_add_text(rpc_tree,
+ offset+0, 4,
+ "Authentication error: %s (%d)",
+ val_to_str(auth_state,rpc_auth_state,"Unknown"),
+ auth_state);
+ }
+ offset += 4;
+ }
+ }
+ } /* end of RPC reply */
+
+dissect_rpc_prog:
+ /* I know, goto is evil but it works as it is. */
+
+ /* now we know, that RPC was shorter */
+ if (rpc_item) {
+ proto_item_set_len(rpc_item, offset - offset_old);
+ }
+
+ /* create here the program specific sub-tree */
+ if (tree) {
+ pitem = proto_tree_add_item(tree, proto, offset, END_OF_FRAME);
+ if (pitem)
+ ptree = proto_item_add_subtree(pitem, ett);
+ }
+
+ /* call a specific dissection */
+ if (dissect_function != NULL) {
+ offset = dissect_function(pd, offset, fd, ptree);
+ }
+
+ /* dissect any remaining bytes (incomplete dissection) as pure data in
+ the ptree */
+ dissect_data(pd, offset, fd, ptree);
+}
+
+/* will be called from file.c on every new file open */
+void
+rpc_init_protocol(void)
+{
+ rpc_call_index = 0;
+ rpc_call_firstfree = 0;
+}
+
+
+/* will be called once from register.c at startup time */
+void
+proto_register_rpc(void)
+{
+ proto_rpc = proto_register_protocol("Remote Procedure Call", "rpc");
+
+ /* please remove this, if all specific dissectors are ready */
+ init_incomplete_dissect();
+}
+