+static gint ett_nfs_secinfo4_flavor_info = -1;
+static gint ett_nfs_stateid4 = -1;
+static gint ett_nfs_fattr4_fh_expire_type = -1;
+
+
+/* file name snooping */
+gboolean nfs_file_name_snooping = FALSE;
+gboolean nfs_file_name_full_snooping = FALSE;
+typedef struct nfs_name_snoop {
+ int fh_length;
+ unsigned char *fh;
+ int name_len;
+ unsigned char *name;
+ int parent_len;
+ unsigned char *parent;
+ int full_name_len;
+ unsigned char *full_name;
+} nfs_name_snoop_t;
+
+typedef struct nfs_name_snoop_key {
+ int key;
+ int fh_length;
+ unsigned char *fh;
+} nfs_name_snoop_key_t;
+
+static GMemChunk *nfs_name_snoop_chunk = NULL;
+static int nfs_name_snoop_init_count = 100;
+static GHashTable *nfs_name_snoop_unmatched = NULL;
+
+static GMemChunk *nfs_name_snoop_key_chunk = NULL;
+static int nfs_name_snoop_key_init_count = 100;
+static GHashTable *nfs_name_snoop_matched = NULL;
+
+static GHashTable *nfs_name_snoop_known = NULL;
+
+static gint
+nfs_name_snoop_matched_equal(gconstpointer k1, gconstpointer k2)
+{
+ nfs_name_snoop_key_t *key1 = (nfs_name_snoop_key_t *)k1;
+ nfs_name_snoop_key_t *key2 = (nfs_name_snoop_key_t *)k2;
+
+ return (key1->key==key2->key)
+ &&(key1->fh_length==key2->fh_length)
+ &&(!memcmp(key1->fh, key2->fh, key1->fh_length));
+}
+static guint
+nfs_name_snoop_matched_hash(gconstpointer k)
+{
+ nfs_name_snoop_key_t *key = (nfs_name_snoop_key_t *)k;
+ int i;
+ int hash;
+
+ hash=key->key;
+ for(i=0;i<key->fh_length;i++)
+ hash ^= key->fh[i];
+
+ return hash;
+}
+static gint
+nfs_name_snoop_unmatched_equal(gconstpointer k1, gconstpointer k2)
+{
+ guint32 key1 = (guint32)k1;
+ guint32 key2 = (guint32)k2;
+
+ return key1==key2;
+}
+static guint
+nfs_name_snoop_unmatched_hash(gconstpointer k)
+{
+ guint32 key = (guint32)k;
+
+ return key;
+}
+static gboolean
+nfs_name_snoop_unmatched_free_all(gpointer key_arg _U_, gpointer value, gpointer user_data _U_)
+{
+ nfs_name_snoop_t *nns = (nfs_name_snoop_t *)value;
+
+ if(nns->name){
+ g_free((gpointer)nns->name);
+ nns->name=NULL;
+ nns->name_len=0;
+ }
+ if(nns->full_name){
+ g_free((gpointer)nns->full_name);
+ nns->full_name=NULL;
+ nns->full_name_len=0;
+ }
+ if(nns->parent){
+ g_free((gpointer)nns->parent);
+ nns->parent=NULL;
+ nns->parent_len=0;
+ }
+ if(nns->fh){
+ g_free((gpointer)nns->fh);
+ nns->fh=NULL;
+ nns->fh_length=0;
+ }
+ return TRUE;
+}
+
+static void
+nfs_name_snoop_init(void)
+{
+ if (nfs_name_snoop_unmatched != NULL) {
+ g_hash_table_foreach_remove(nfs_name_snoop_unmatched,
+ nfs_name_snoop_unmatched_free_all, NULL);
+ } else {
+ /* The fragment table does not exist. Create it */
+ nfs_name_snoop_unmatched=g_hash_table_new(nfs_name_snoop_unmatched_hash,
+ nfs_name_snoop_unmatched_equal);
+ }
+ if (nfs_name_snoop_matched != NULL) {
+ g_hash_table_foreach_remove(nfs_name_snoop_matched,
+ nfs_name_snoop_unmatched_free_all, NULL);
+ } else {
+ /* The fragment table does not exist. Create it */
+ nfs_name_snoop_matched=g_hash_table_new(nfs_name_snoop_matched_hash,
+ nfs_name_snoop_matched_equal);
+ }
+ if (nfs_name_snoop_known != NULL) {
+ g_hash_table_foreach_remove(nfs_name_snoop_known,
+ nfs_name_snoop_unmatched_free_all, NULL);
+ } else {
+ /* The fragment table does not exist. Create it */
+ nfs_name_snoop_known=g_hash_table_new(nfs_name_snoop_matched_hash,
+ nfs_name_snoop_matched_equal);
+ }
+
+ if(nfs_name_snoop_chunk){
+ g_mem_chunk_destroy(nfs_name_snoop_chunk);
+ nfs_name_snoop_chunk = NULL;
+ }
+ if(nfs_name_snoop_key_chunk){
+ g_mem_chunk_destroy(nfs_name_snoop_key_chunk);
+ nfs_name_snoop_key_chunk = NULL;
+ }
+
+ if(nfs_file_name_snooping){
+ nfs_name_snoop_chunk = g_mem_chunk_new("nfs_name_snoop_chunk",
+ sizeof(nfs_name_snoop_t),
+ nfs_name_snoop_init_count * sizeof(nfs_name_snoop_t),
+ G_ALLOC_ONLY);
+ nfs_name_snoop_key_chunk = g_mem_chunk_new("nfs_name_snoop_key_chunk",
+ sizeof(nfs_name_snoop_key_t),
+ nfs_name_snoop_key_init_count * sizeof(nfs_name_snoop_key_t),
+ G_ALLOC_ONLY);
+ }
+
+}
+
+void
+nfs_name_snoop_add_name(int xid, tvbuff_t *tvb, int name_offset, int name_len, int parent_offset, int parent_len, unsigned char *name)
+{
+ nfs_name_snoop_t *nns, *old_nns;
+ unsigned char *ptr=NULL;
+
+ /* filter out all '.' and '..' names */
+ if(!name){
+ ptr=(unsigned char *)tvb_get_ptr(tvb, name_offset, name_len);
+ if(ptr[0]=='.'){
+ if(ptr[1]==0){
+ return;
+ }
+ if(ptr[1]=='.'){
+ if(ptr[2]==0){
+ return;
+ }
+ }
+ }
+ }
+
+ nns=g_mem_chunk_alloc(nfs_name_snoop_chunk);
+
+ nns->fh_length=0;
+ nns->fh=NULL;
+
+ if(parent_len){
+ nns->parent_len=parent_len;
+ nns->parent=g_malloc(parent_len);
+ memcpy(nns->parent, tvb_get_ptr(tvb, parent_offset, parent_len), parent_len);
+ } else {
+ nns->parent_len=0;
+ nns->parent=NULL;
+ }
+
+ nns->name_len=name_len;
+ if(name){
+ nns->name=name;
+ } else {
+ nns->name=g_malloc(name_len+1);
+ memcpy(nns->name, ptr, name_len);
+ }
+ nns->name[name_len]=0;
+
+ nns->full_name_len=0;
+ nns->full_name=NULL;
+
+ /* remove any old entry for this */
+ old_nns=g_hash_table_lookup(nfs_name_snoop_unmatched, (gconstpointer)xid);
+ if(old_nns){
+ /* if we havnt seen the reply yet, then there are no
+ matched entries for it, thus we can dealloc the arrays*/
+ if(!old_nns->fh){
+ g_free(old_nns->name);
+ old_nns->name=NULL;
+ old_nns->name_len=0;
+
+ g_free(old_nns->parent);
+ old_nns->parent=NULL;
+ old_nns->parent_len=0;
+
+ g_mem_chunk_free(nfs_name_snoop_chunk, old_nns);
+ }
+ g_hash_table_remove(nfs_name_snoop_unmatched, (gconstpointer)xid);
+ }
+
+ g_hash_table_insert(nfs_name_snoop_unmatched, (gpointer)xid, nns);
+}
+
+static void
+nfs_name_snoop_add_fh(int xid, tvbuff_t *tvb, int fh_offset, int fh_length)
+{
+ nfs_name_snoop_t *nns, *old_nns;
+ nfs_name_snoop_key_t *key;
+
+ /* find which request we correspond to */
+ nns=g_hash_table_lookup(nfs_name_snoop_unmatched, (gconstpointer)xid);
+ if(!nns){
+ /* oops couldnt find matching request, bail out */
+ return;
+ }
+
+ /* if we have already seen this response earlier */
+ if(nns->fh){
+ return;
+ }
+
+ /* oki, we have a new entry */
+ nns->fh=g_malloc(fh_length);
+ memcpy(nns->fh, tvb_get_ptr(tvb, fh_offset, fh_length), fh_length);
+ nns->fh_length=fh_length;
+
+ key=g_mem_chunk_alloc(nfs_name_snoop_key_chunk);
+ key->key=0;
+ key->fh_length=nns->fh_length;
+ key->fh =nns->fh;
+
+ /* already have something matched for this fh, remove it from
+ the table */
+ old_nns=g_hash_table_lookup(nfs_name_snoop_matched, key);
+ if(old_nns){
+ g_hash_table_remove(nfs_name_snoop_matched, key);
+ }
+
+ g_hash_table_remove(nfs_name_snoop_unmatched, (gconstpointer)xid);
+ g_hash_table_insert(nfs_name_snoop_matched, key, nns);
+}
+
+static void
+nfs_full_name_snoop(nfs_name_snoop_t *nns, int *len, unsigned char **name, unsigned char **pos)
+{
+ nfs_name_snoop_t *parent_nns = NULL;
+ nfs_name_snoop_key_t key;
+
+ /* check if the nns component ends with a '/' else we just allocate
+ an extra byte to len to accommodate for it later */
+ if(nns->name[nns->name_len-1]!='/'){
+ (*len)++;
+ }
+
+ (*len) += nns->name_len;
+
+ if(nns->parent==NULL){
+ *name = g_malloc((*len)+1);
+ *pos = *name;
+
+ strcpy(*pos, nns->name);
+ *pos += nns->name_len;
+ return;
+ }
+
+ key.key=0;
+ key.fh_length=nns->parent_len;
+ key.fh=nns->parent;
+
+ parent_nns=g_hash_table_lookup(nfs_name_snoop_matched, &key);
+
+ if(parent_nns){
+ nfs_full_name_snoop(parent_nns, len, name, pos);
+ if(*name){
+ /* make sure components are '/' separated */
+ if( (*pos)[-1] != '/'){
+ **pos='/';
+ (*pos)++;
+ **pos=0;
+ }
+ strcpy(*pos, nns->name);
+ *pos += nns->name_len;
+ }
+ return;
+ }
+
+ return;
+}
+
+static void
+nfs_name_snoop_fh(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int fh_offset, int fh_length)
+{
+ nfs_name_snoop_key_t key;
+ nfs_name_snoop_t *nns = NULL;
+
+ /* if this is a new packet, see if we can register the mapping */
+ if(!pinfo->fd->flags.visited){
+ key.key=0;
+ key.fh_length=fh_length;
+ key.fh=(unsigned char *)tvb_get_ptr(tvb, fh_offset, fh_length);
+
+ nns=g_hash_table_lookup(nfs_name_snoop_matched, &key);
+ if(nns){
+ nfs_name_snoop_key_t *k;
+ k=g_mem_chunk_alloc(nfs_name_snoop_key_chunk);
+ k->key=pinfo->fd->num;
+ k->fh_length=nns->fh_length;
+ k->fh=nns->fh;
+ g_hash_table_insert(nfs_name_snoop_known, k, nns);
+
+ if(nfs_file_name_full_snooping){
+ unsigned char *name=NULL, *pos=NULL;
+ int len=0;
+
+ nfs_full_name_snoop(nns, &len, &name, &pos);
+ if(name){
+ nns->full_name=name;
+ nns->full_name_len=len;
+ }
+ }
+ }
+ }
+
+ /* see if we know this mapping */
+ if(!nns){
+ key.key=pinfo->fd->num;
+ key.fh_length=fh_length;
+ key.fh=(unsigned char *)tvb_get_ptr(tvb, fh_offset, fh_length);
+
+ nns=g_hash_table_lookup(nfs_name_snoop_known, &key);
+ }
+
+ /* if we know the mapping, print the filename */
+ if(nns){
+ proto_tree_add_string_format(tree, hf_nfs_name, tvb,
+ fh_offset, 0, nns->name, "Name: %s", nns->name);
+ if(nns->full_name){
+ proto_tree_add_string_format(tree, hf_nfs_full_name, tvb,
+ fh_offset, 0, nns->name, "Full Name: %s", nns->full_name);
+ }
+ }
+}