From dc6963e2d4b2b2ca06221b80313326170bb1a1b7 Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Fri, 29 Oct 1999 01:11:23 +0000 Subject: [PATCH] Uwe Girlich's ONC RPC and NFS dissectors. svn path=/trunk/; revision=946 --- packet-nfs.c | 180 +++++++++++ packet-nfs.h | 18 ++ packet-rpc.c | 861 +++++++++++++++++++++++++++++++++++++++++++++++++++ packet-rpc.h | 103 ++++++ 4 files changed, 1162 insertions(+) create mode 100644 packet-nfs.c create mode 100644 packet-nfs.h create mode 100644 packet-rpc.c create mode 100644 packet-rpc.h diff --git a/packet-nfs.c b/packet-nfs.c new file mode 100644 index 0000000000..eed7bbad01 --- /dev/null +++ b/packet-nfs.c @@ -0,0 +1,180 @@ +/* packet-nfs.c + * Routines for nfs dissection + * Copyright 1999, Uwe Girlich + * + * $Id: packet-nfs.c,v 1.1 1999/10/29 01:11:23 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 +#endif + + +#include "packet-rpc.h" +#include "packet-nfs.h" + + +static int proto_nfs = -1; + +int dissect_fh2(const u_char *pd, int offset, frame_data *fd, proto_tree *tree); + +/* +This is the table with the dissector functions. As almost all functions +start with a file handle and I had no more time, this is all I did up to now. +The RPC layer will cope with any following data and interpret it as data. +I'm not sure, if all compilers fill undefined structure members with zeros, +so I give the NULL value in all cases. +*/ + +/* proc number, "proc name", dissect_request, dissect_reply */ +/* NULL as function pointer means: take the generic one. */ +const vsff nfs2_proc[] = { + { 0, "NULL", NULL, NULL }, + { 1, "GETATTR", dissect_fh2, NULL }, + { 2, "SETATTR", dissect_fh2, NULL }, + { 3, "ROOT", NULL, NULL }, + { 4, "LOOKUP", dissect_fh2, NULL }, + { 5, "READLINK", dissect_fh2, NULL }, + { 6, "READ", dissect_fh2, NULL }, + { 7, "WRITECACHE", dissect_fh2, NULL }, + { 8, "WRITE", dissect_fh2, NULL }, + { 9, "CREATE", dissect_fh2, NULL }, + { 10, "REMOVE", dissect_fh2, NULL }, + { 11, "RENAME", dissect_fh2, NULL }, + { 12, "LINK", dissect_fh2, NULL }, + { 13, "SYMLINK", dissect_fh2, NULL }, + { 14, "MKDIR", dissect_fh2, NULL }, + { 15, "RMDIR", dissect_fh2, NULL }, + { 16, "READDIR", dissect_fh2, NULL }, + { 17, "STATFS", dissect_fh2, NULL }, + { 0, NULL, NULL, NULL } +}; + +int dissect_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree); +int dissect_nfs3_getattr_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree); + + +const vsff nfs3_proc[] = { + { 0, "NULL", NULL, NULL }, + { 1, "GETATTR", dissect_nfs3_getattr_call, NULL }, + { 2, "SETATTR", dissect_fh3, NULL }, + { 3, "LOOKUP", dissect_fh3, NULL }, + { 4, "ACCESS", dissect_fh3, NULL }, + { 5, "READLINK", dissect_fh3, NULL }, + { 6, "READ", dissect_fh3, NULL }, + { 7, "WRITE", dissect_fh3, NULL }, + { 8, "CREATE", dissect_fh3, NULL }, + { 9, "MKDIR", dissect_fh3, NULL }, + { 10, "SYMLINK", dissect_fh3, NULL }, + { 11, "MKNOD", dissect_fh3, NULL }, + { 12, "REMOVE", dissect_fh3, NULL }, + { 13, "RMDIR", dissect_fh3, NULL }, + { 14, "RENAME", dissect_fh3, NULL }, + { 15, "LINK", dissect_fh3, NULL }, + { 16, "READDIR", dissect_fh3, NULL }, + { 17, "READDIRPLUS", dissect_fh3, NULL }, + { 18, "FSSTAT", dissect_fh3, NULL }, + { 19, "FSINFO", dissect_fh3, NULL }, + { 20, "PATHCONF", dissect_fh3, NULL }, + { 21, "COMMIT", dissect_fh3, NULL }, + { 0, NULL, NULL, NULL } +}; + + +int +dissect_fh2(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) +{ + proto_item* fitem; + proto_tree* ftree = NULL; + + if (tree) { + fitem = proto_tree_add_text(tree, offset, FHSIZE, + "file handle"); + if (fitem) + ftree = proto_item_add_subtree(fitem, ETT_NFS2_FH); + } + + if (ftree) { + proto_tree_add_text(ftree,offset+0,FHSIZE, + "opaque data"); + } + offset += FHSIZE; + return offset; +} + +int +dissect_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) +{ + guint fh_len; + guint fh_len_full; + proto_item* fitem; + proto_tree* ftree = NULL; + + fh_len = EXTRACT_UINT(pd, offset+0); + fh_len_full = roundup(fh_len); + + if (tree) { + fitem = proto_tree_add_text(tree, offset, 4+fh_len_full, + "file handle"); + if (fitem) + ftree = proto_item_add_subtree(fitem, ETT_NFS3_FH); + } + + if (ftree) { + proto_tree_add_text(ftree,offset+0,4, + "length: %d", fh_len); + proto_tree_add_text(ftree,offset+4,fh_len, + "opaque data"); + } + offset += 4 + fh_len_full; + return offset; +} + + +/* In fact, this routine serves only as a place to copy some ideas for +more complicated dissectors. */ + +int +dissect_nfs3_getattr_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree) +{ + offset = dissect_fh3(pd, offset, fd, tree); + return offset; +} + +void +proto_register_nfs(void) +{ + proto_nfs = proto_register_protocol("Network File System", "NFS"); + + /* Register the protocol as RPC */ + rpc_init_prog(proto_nfs, NFS_PROGRAM, ETT_NFS); + /* Register the procedure tables */ + rpc_init_proc_table(NFS_PROGRAM, 2, nfs2_proc); + rpc_init_proc_table(NFS_PROGRAM, 3, nfs3_proc); +} + diff --git a/packet-nfs.h b/packet-nfs.h new file mode 100644 index 0000000000..52c12151f8 --- /dev/null +++ b/packet-nfs.h @@ -0,0 +1,18 @@ +/* packet-nfs.h (c) 1999 Uwe Girlich */ +/* $Id: packet-nfs.h,v 1.1 1999/10/29 01:11:23 guy Exp $ */ + +#ifndef __PACKET_NFS_H__ +#define __PACKET_NFS_H__ + +#include "packet-rpc.h" + +#define NFS_PROGRAM 100003 + +#define FHSIZE 32 + +/* the RPC mount protocol needs both function to decode a MNT reply */ +int dissect_fh2(const u_char *pd, int offset, frame_data *fd, proto_tree *tree); +int dissect_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree); + +#endif /* packet-nfs.h */ + 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 + * + * $Id: packet-rpc.c,v 1.1 1999/10/29 01:11:22 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 +#endif + +#include +#include +#include +#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_ 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_firstfreexid && + 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(); +} + diff --git a/packet-rpc.h b/packet-rpc.h new file mode 100644 index 0000000000..1208c85709 --- /dev/null +++ b/packet-rpc.h @@ -0,0 +1,103 @@ +/* packet-rpc.h (c) 1999 Uwe Girlich */ +/* $Id: packet-rpc.h,v 1.1 1999/10/29 01:11:22 guy Exp $ */ + +#ifndef __PACKET_RPC_H__ +#define __PACKET_RPC_H__ + +#include +#include "packet.h" +#include "conversation.h" + +#define EXTRACT_UINT(p,o) pntohl(&p[o]) + +#define RPC_CALL 0 +#define RPC_REPLY 1 + +#define AUTH_NULL 0 +#define AUTH_UNIX 1 +#define AUTH_SHORT 2 +#define AUTH_DES 3 + +#define MSG_ACCEPTED 0 +#define MSG_DENIED 1 + +#define AUTH_NULL 0 +#define AUTH_UNIX 1 +#define AUTH_SHORT 2 +#define AUTH_DES 3 + +#define SUCCESS 0 +#define PROG_UNAVAIL 1 +#define PROG_MISMATCH 2 +#define PROC_UNAVAIL 3 +#define GARBAGE_ARGS 4 + +#define RPC_MISMATCH 0 +#define AUTH_ERROR 1 + +#define AUTH_BADCRED 1 +#define AUTH_REJECTEDCRED 2 +#define AUTH_BADVERF 3 +#define AUTH_REJECTEDVERF 4 +#define AUTH_TOOWEAK 5 + +typedef int (dissect_function_t)(const u_char* pd, int offset, frame_data* fd, proto_tree* tree); + +extern GHashTable *rpc_progs; + +typedef struct _vsff { + guint32 value; + gchar *strptr; + dissect_function_t *dissect_call; + dissect_function_t *dissect_reply; +} vsff; + +typedef struct _rpc_proc_info_key { + guint32 prog; + guint32 vers; + guint32 proc; +} rpc_proc_info_key; + +typedef struct _rpc_proc_info_value { + gchar *name; + dissect_function_t *dissect_call; + dissect_function_t *dissect_reply; +} rpc_proc_info_value; + +typedef struct _rpc_prog_info_key { + guint32 prog; +} rpc_prog_info_key; + +typedef struct _rpc_prog_info_value { + int proto; + int ett; + char* progname; +} rpc_prog_info_value; + +typedef struct _rpc_call_info { + guint32 xid; + conversation_t *conversation; + guint32 replies; + guint32 prog; + guint32 vers; + guint32 proc; + rpc_proc_info_value* proc_info; +} rpc_call_info; + +#define RPC_CALL_TABLE_LENGTH 1000 + +extern void rpc_call_insert(rpc_call_info *call); +extern rpc_call_info* rpc_call_lookup(rpc_call_info *call); + +extern void rpc_init_proc_table(guint prog, guint vers, const vsff *proc_table); +extern void rpc_init_prog(int proto, guint32 prog, int ett); + +extern void init_dissect_rpc(); +extern void cleanup_dissect_rpc(); + +extern unsigned int roundup(unsigned int a); + + + +#endif /* packet-rpc.h */ + -- 2.34.1