forked from osmocom/wireshark
Uwe Girlich's ONC RPC and NFS dissectors.
svn path=/trunk/; revision=946
This commit is contained in:
parent
21c466ed25
commit
dc6963e2d4
|
@ -0,0 +1,180 @@
|
|||
/* packet-nfs.c
|
||||
* Routines for nfs dissection
|
||||
* Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
|
||||
*
|
||||
* $Id: packet-nfs.c,v 1.1 1999/10/29 01:11:23 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 "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);
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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 <glib.h>
|
||||
#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 */
|
||||
|
Loading…
Reference in New Issue