66d99b55bac79316665584610b6b9380609b24b9
[obnox/wireshark/wip.git] / packet-dcerpc-nt.c
1 /* packet-dcerpc-nt.c
2  * Routines for DCERPC over SMB packet disassembly
3  * Copyright 2001, Tim Potter <tpot@samba.org>
4  *
5  * $Id: packet-dcerpc-nt.c,v 1.27 2002/04/18 00:29:17 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <glib.h>
31 #include <epan/packet.h>
32 #include "packet-dcerpc.h"
33 #include "packet-dcerpc-nt.h"
34 #include "smb.h"
35 #include "packet-smb-common.h" /* for dissect_smb_64bit_time() */
36
37 /*
38  * This file contains helper routines that are used by the DCERPC over SMB
39  * dissectors for ethereal.
40  */
41
42 /* Align offset to a n-byte boundary */
43
44 int prs_align(int offset, int n)
45 {
46         if (offset % n)
47                 offset += n - (offset % n);
48         
49         return offset;
50 }
51
52 /* Parse a 8-bit integer */
53
54 int prs_uint8(tvbuff_t *tvb, int offset, packet_info *pinfo,
55               proto_tree *tree, guint8 *data, char *name)
56 {
57         guint8 i;
58         
59         /* No alignment required */
60
61         i = tvb_get_guint8(tvb, offset);
62         offset++;
63
64         if (name && tree)
65                 proto_tree_add_text(tree, tvb, offset - 1, 1, 
66                                     "%s: %u", name, i);
67
68         if (data)
69                 *data = i;
70
71         return offset;
72 }
73
74 int prs_uint8s(tvbuff_t *tvb, int offset, packet_info *pinfo,
75                proto_tree *tree, int count, int *data_offset, char *name)
76 {
77         /* No alignment required */
78
79         if (name && tree)
80                 proto_tree_add_text(tree, tvb, offset, count, "%s", name);
81
82         if (data_offset)
83                 *data_offset = offset;
84
85         offset += count;
86
87         return offset;
88 }
89
90 /* Parse a 16-bit integer */
91
92 int prs_uint16(tvbuff_t *tvb, int offset, packet_info *pinfo,
93                proto_tree *tree, guint16 *data, char *name)
94 {
95         guint16 i;
96         
97         offset = prs_align(offset, 2);
98         
99         i = tvb_get_letohs(tvb, offset);
100         offset += 2;
101
102         if (name && tree)
103                 proto_tree_add_text(tree, tvb, offset - 2, 2, 
104                                     "%s: %u", name, i);
105         if (data)
106                 *data = i;
107
108         return offset;
109 }
110
111 /* Parse a number of uint16's */
112
113 int prs_uint16s(tvbuff_t *tvb, int offset, packet_info *pinfo,
114                 proto_tree *tree, int count, int *data_offset, char *name)
115 {
116         offset = prs_align(offset, 2);
117         
118         if (name && tree)
119                 proto_tree_add_text(tree, tvb, offset, count * 2, 
120                                     "%s", name);
121         if (data_offset)
122                 *data_offset = offset;
123
124         offset += count * 2;
125
126         return offset;
127 }
128
129 /* Parse a 32-bit integer */
130
131 int prs_uint32(tvbuff_t *tvb, int offset, packet_info *pinfo,
132                proto_tree *tree, guint32 *data, char *name)
133 {
134         guint32 i;
135         
136         offset = prs_align(offset, 4);
137         
138         i = tvb_get_letohl(tvb, offset);
139         offset += 4;
140
141         if (name && tree)
142                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
143                                     "%s: %u", name, i);
144
145         if (data)
146                 *data = i;
147
148         return offset;
149 }
150
151 /* Parse a number of 32-bit integers */
152
153 int prs_uint32s(tvbuff_t *tvb, int offset, packet_info *pinfo,
154                 proto_tree *tree, int count, int *data_offset, char *name)
155 {
156         offset = prs_align(offset, 4);
157         
158         if (name && tree)
159                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
160                                     "%s", name);
161         if (data_offset)
162                 *data_offset = offset;
163
164         offset += count * 4;
165
166         return offset;
167 }
168
169 /* Parse a NT status code */
170
171 int prs_ntstatus(tvbuff_t *tvb, int offset, packet_info *pinfo,
172                  proto_tree *tree)
173 {
174         guint32 status;
175
176         offset = prs_uint32(tvb, offset, pinfo, tree, &status, NULL);
177
178         if (tree)
179                 proto_tree_add_text(tree, tvb, offset - 4, 4, "Status: %s",
180                                     val_to_str(status, NT_errors, "???"));
181
182         return offset;
183 }
184
185 /*
186  * We need to keep track of deferred referrents as they appear in the
187  * packet after all the non-pointer objects.
188  * to keep track of pointers as they are parsed as scalars and need to be
189  * remembered for the next call to the prs function.
190  *
191  * Pointers are stored in a linked list and pushed in the PARSE_SCALARS
192  * section of the prs function and popped in the PARSE_BUFFERS section.  If
193  * we try to pop off a referrent that has a different name then we are
194  * expecting then something has gone wrong.
195  */
196
197 #undef DEBUG_PTRS
198
199 struct ptr {
200         char *name;
201         guint32 value;
202 };
203
204 /* Create a new pointer */
205
206 static struct ptr *new_ptr(char *name, guint32 value)
207 {
208         struct ptr *p;
209
210         p = g_malloc(sizeof(struct ptr));
211
212         p->name = g_strdup(name);
213         p->value = value;
214
215         return p;
216 }
217
218 /* Free a pointer */
219
220 static void free_ptr(struct ptr *p)
221 {
222         if (p) {
223                 g_free(p->name);
224                 g_free(p);
225         }
226 }
227
228 /* Parse a pointer and store it's value in a linked list */
229
230 int prs_push_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
231                  proto_tree *tree, GList **ptr_list, char *name)
232 {
233         struct ptr *p;
234         guint32 value;
235
236         offset = prs_uint32(tvb, offset, pinfo, tree, &value, NULL);
237
238         if (name && tree)
239                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
240                                     "%s pointer: 0x%08x", name, value);
241
242         p = new_ptr(name, value);
243
244         *ptr_list = g_list_append(*ptr_list, p);
245
246 #ifdef DEBUG_PTRS
247         fprintf(stderr, "DEBUG_PTRS: pushing %s ptr = 0x%08x, %d ptrs in "
248                 "list\n", name, value, g_list_length(*ptr_list));
249 #endif
250
251         return offset;
252 }
253
254 /* Pop a pointer of a given name.  Return it's value. */
255
256 guint32 prs_pop_ptr(GList **ptr_list, char *name)
257 {
258         GList *elt;
259         struct ptr *p;
260         guint32 result;
261
262         g_assert(g_list_length(*ptr_list) != 0);        /* List too short */
263
264         /* Get pointer at head of list */
265
266         elt = g_list_first(*ptr_list);
267         p = (struct ptr *)elt->data;
268         result = p->value;
269
270 #ifdef DEBUG_PTRS
271         if (strcmp(p->name, name) != 0) {
272                 fprintf(stderr, "DEBUG_PTRS: wrong pointer (%s != %s)\n",
273                         p->name, name);
274         }
275 #endif
276
277         /* Free pointer record */
278
279         *ptr_list = g_list_remove_link(*ptr_list, elt);
280
281 #ifdef DEBUG_PTRS
282         fprintf(stderr, "DEBUG_PTRS: popping %s ptr = 0x%08x, %d ptrs in "
283                 "list\n", p->name, p->value, g_list_length(*ptr_list));
284 #endif
285
286         free_ptr(p);
287
288         return result;
289 }
290
291 /* Convert a string from little-endian unicode to ascii.  At the moment we
292    fake it by taking every odd byte.  )-:  The caller must free the
293    result returned. */
294
295 char *fake_unicode(tvbuff_t *tvb, int offset, int len)
296 {
297         char *buffer;
298         int i;
299         guint16 character;
300
301         /* Make sure we have enough data before allocating the buffer,
302            so we don't blow up if the length is huge.
303            We do so by attempting to fetch the last character; it'll
304            throw an exception if it's past the end. */
305         tvb_get_letohs(tvb, offset + 2*(len - 1));
306
307         /* We know we won't throw an exception, so we don't have to worry
308            about leaking this buffer. */
309         buffer = g_malloc(len + 1);
310
311         for (i = 0; i < len; i++) {
312                 character = tvb_get_letohs(tvb, offset);
313                 buffer[i] = character & 0xff;
314                 offset += 2;
315         }
316
317         buffer[len] = 0;
318
319         return buffer;
320 }
321
322 /* Parse a UNISTR2 structure */
323
324 int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
325                 proto_tree *tree, int flags, char **data, char *name)
326 {
327         guint32 len = 0, unknown = 0, max_len = 0;
328
329         if (flags & PARSE_SCALARS) {
330                 offset = prs_uint32(tvb, offset, pinfo, tree, &len, "Length");
331                 offset = prs_uint32(tvb, offset, pinfo, tree, &unknown, 
332                                     "Offset");
333                 offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, 
334                                     "Max length");
335         }
336
337         if (flags & PARSE_BUFFERS) {
338                 int data16_offset;
339
340                 offset = prs_uint16s(tvb, offset, pinfo, tree, max_len,
341                                      &data16_offset, "Buffer");
342
343                 if (data)
344                         *data = fake_unicode(tvb, data16_offset, max_len);
345         }
346
347         return offset;
348 }
349
350 /* Parse a policy handle. */
351
352 int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo, 
353                    proto_tree *tree, const guint8 **data)
354 {
355         const guint8 *data8;
356
357         offset = prs_align(offset, 4);
358
359         proto_tree_add_text(tree, tvb, offset, 20, "Policy Handle: %s", 
360                 tvb_bytes_to_str(tvb, offset, 20));
361
362         data8 = tvb_get_ptr(tvb, offset, 20);
363         
364         if (data)
365                 *data = data8;
366
367         return offset + 20;
368 }
369
370
371
372 /* following are a few functions for dissecting common structures used by NT 
373    services. These might need to be cleaned up at a later time but at least we get
374    them out of the real service dissectors.
375 */
376
377
378 /* UNICODE_STRING  BEGIN */
379 /* functions to dissect a UNICODE_STRING structure, common to many 
380    NT services
381    struct {
382      short len;
383      short size;
384      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
385    } UNICODE_STRING;
386
387    these variables can be found in packet-dcerpc-samr.c 
388 */
389 extern int hf_nt_str_len;
390 extern int hf_nt_str_off;
391 extern int hf_nt_str_max_len;
392 extern int hf_nt_string_length;
393 extern int hf_nt_string_size;
394
395 gint ett_nt_unicode_string = -1;
396 static gint ett_nt_policy_hnd = -1;
397
398 /* this function will dissect the
399      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
400   part of the unicode string
401
402    struct {
403      short len;
404      short size;
405      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
406    } UNICODE_STRING;
407   structure used by NT to transmit unicode string values.
408
409   This function also looks at di->levels to see if whoever called us wanted us to append
410   the name: string to any higher levels in the tree .
411 */
412 int
413 dissect_ndr_nt_UNICODE_STRING_str(tvbuff_t *tvb, int offset, 
414                         packet_info *pinfo, proto_tree *tree, 
415                         char *drep)
416 {
417         guint32 len, off, max_len;
418         int data16_offset;
419         char *text;
420         int old_offset;
421         dcerpc_info *di;
422
423         di=pinfo->private_data;
424         if(di->conformant_run){
425                 /*just a run to handle conformant arrays, nothing to dissect */
426                 return offset;
427         }
428
429         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
430                         hf_nt_str_max_len, &max_len);
431         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
432                         hf_nt_str_off, &off);
433         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
434                         hf_nt_str_len, &len);
435
436         old_offset=offset;
437         offset = prs_uint16s(tvb, offset, pinfo, tree, len, &data16_offset,
438                         NULL);
439         text = fake_unicode(tvb, data16_offset, len);
440
441         proto_tree_add_string(tree, di->hf_index, tvb, old_offset,
442                 offset-old_offset, text);
443
444         /* need to test di->levels before doing the proto_item_append_text()
445            since netlogon has these objects as top level objects in its representation
446            and trying to append to the tree object in that case will dump core */
447         if(tree && (di->levels>-1)){
448                 proto_item_append_text(tree, ": %s", text);
449                 di->levels--;
450                 if(di->levels>-1){
451                         tree=tree->parent;
452                         proto_item_append_text(tree, ": %s", text);
453                         di->levels--;
454                         while(di->levels>-1){
455                                 tree=tree->parent;
456                                 proto_item_append_text(tree, " %s", text);
457                                 di->levels--;
458                         }
459                 }
460         }
461         g_free(text);
462         return offset;
463 }
464
465 /* this function will dissect the
466    struct {
467      short len;
468      short size;
469      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
470    } UNICODE_STRING;
471   structure used by NT to transmit unicode string values.
472  
473   the function takes one additional parameter, level
474   which specifies how many additional levels up in the tree where we should
475   append the string.  If unsure, specify levels as 0.
476 */
477 int
478 dissect_ndr_nt_UNICODE_STRING(tvbuff_t *tvb, int offset, 
479                         packet_info *pinfo, proto_tree *parent_tree, 
480                         char *drep, int hf_index, int levels)
481 {
482         proto_item *item=NULL;
483         proto_tree *tree=NULL;
484         int old_offset=offset;
485         dcerpc_info *di;
486         char *name;
487
488         ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
489
490         di=pinfo->private_data;
491         if(di->conformant_run){
492                 /*just a run to handle conformant arrays, nothing to dissect */
493                 return offset;
494         }
495
496         name = proto_registrar_get_name(hf_index);
497         if(parent_tree){
498                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
499                         "%s", name);
500                 tree = proto_item_add_subtree(item, ett_nt_unicode_string);
501         }
502
503         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
504                         hf_nt_string_length, NULL);
505         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
506                         hf_nt_string_size, NULL);
507         di->levels=1;   /* XXX - is this necessary? */
508         /* Add 1 level, for the extra level we added */
509         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
510                         dissect_ndr_nt_UNICODE_STRING_str, NDR_POINTER_UNIQUE,
511                         name, hf_index, levels + 1);
512
513         proto_item_set_len(item, offset-old_offset);
514         return offset;
515 }
516 /* UNICODE_STRING  END */
517
518 /* functions to dissect a STRING structure, common to many 
519    NT services
520    struct {
521      short len;
522      short size;
523      [size_is(size), length_is(len), ptr] char *string;
524    } STRING;
525 */
526 int
527 dissect_ndr_nt_STRING_string (tvbuff_t *tvb, int offset, 
528                              packet_info *pinfo, proto_tree *tree, 
529                              char *drep)
530 {
531         guint32 len, off, max_len;
532         int text_offset;
533         const guint8 *text;
534         int old_offset;
535         header_field_info *hfi;
536         dcerpc_info *di;
537
538         di=pinfo->private_data;
539         if(di->conformant_run){
540                 /*just a run to handle conformant arrays, nothing to dissect */
541                 return offset;
542         }
543
544         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
545                                      hf_nt_str_len, &len);
546         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
547                                      hf_nt_str_off, &off);
548         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
549                                      hf_nt_str_max_len, &max_len);
550
551         old_offset=offset;
552         hfi = proto_registrar_get_nth(di->hf_index);
553
554         switch(hfi->type){
555         case FT_STRING:
556                 offset = prs_uint8s(tvb, offset, pinfo, tree, max_len,
557                         &text_offset, NULL);
558                 text = tvb_get_ptr(tvb, text_offset, max_len);
559                 proto_tree_add_string_format(tree, di->hf_index, 
560                         tvb, old_offset, offset-old_offset,
561                         text, "%s: %s", hfi->name, text);
562                 break;
563         case FT_BYTES:
564                 text = NULL;
565                 proto_tree_add_item(tree, di->hf_index, tvb, offset, max_len, FALSE);
566                 offset += max_len;
567                 break;
568         default:
569                 text = NULL;
570                 g_assert_not_reached();
571         }
572
573         if(tree && text && (di->levels>-1)){
574                 proto_item_append_text(tree, ": %s", text);
575                 if(di->levels>-1){
576                         tree=tree->parent;
577                         proto_item_append_text(tree, ": %s", text);
578                         while(di->levels>0){
579                                 tree=tree->parent;
580                                 proto_item_append_text(tree, " %s", text);
581                                 di->levels--;
582                         }
583                 }
584         }
585         return offset;
586 }
587
588 int
589 dissect_ndr_nt_STRING (tvbuff_t *tvb, int offset, 
590                              packet_info *pinfo, proto_tree *parent_tree, 
591                              char *drep, int hf_index, int levels)
592 {
593         proto_item *item=NULL;
594         proto_tree *tree=NULL;
595         int old_offset=offset;
596         dcerpc_info *di;
597         char *name;
598
599         ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
600
601         di=pinfo->private_data;
602         if(di->conformant_run){
603                 /*just a run to handle conformant arrays, nothing to dissect */
604                 return offset;
605         }
606
607         name = proto_registrar_get_name(hf_index);
608         if(parent_tree){
609                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
610                         "%s", name);
611                 tree = proto_item_add_subtree(item, ett_nt_unicode_string);
612         }
613
614         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
615                                      hf_nt_string_length, NULL);
616         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
617                                      hf_nt_string_size, NULL);
618         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
619                         dissect_ndr_nt_STRING_string, NDR_POINTER_UNIQUE,
620                         name, hf_index, levels);
621
622         proto_item_set_len(item, offset-old_offset);
623         return offset;
624 }
625
626
627 /* This function is used to dissect a DCERPC encoded 64 bit time value.
628    XXX it should be fixed both here and in dissect_smb_64bit_time so
629    it can handle both BIG and LITTLE endian encodings 
630  */
631 int
632 dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset, 
633                         packet_info *pinfo, proto_tree *tree, 
634                         char *drep, int hf_index)
635 {
636         dcerpc_info *di;
637
638         di=pinfo->private_data;
639         if(di->conformant_run){
640                 /*just a run to handle conformant arrays, nothing to dissect */
641                 return offset;
642         }
643
644         ALIGN_TO_4_BYTES;
645
646         offset = dissect_smb_64bit_time(tvb, pinfo, tree, offset,
647                  hf_index);
648         return offset;
649 }
650
651 /* Define this symbol to display warnings about request/response and
652    policy handle hash table collisions.  This happens when a packet with
653    the same conversation, smb fid and dcerpc call id occurs.  I think this
654    is due to a bug in the dcerpc/smb fragment reassembly code. */
655
656 #undef DEBUG_HASH_COLL
657
658 /*
659  * Policy handle hashing
660  */
661
662 typedef struct {
663         guint8 policy_hnd[20];
664 } pol_hash_key;
665
666 typedef struct {
667         int open_frame, close_frame; /* Frame numbers for open/close */
668         char *name;                  /* Name of policy handle */
669 } pol_hash_value;
670
671 #define POL_HASH_INIT_COUNT 100
672
673 static GHashTable *pol_hash;
674 static GMemChunk *pol_hash_key_chunk;
675 static GMemChunk *pol_hash_value_chunk;
676
677 /* Hash function */
678
679 static guint pol_hash_fn(gconstpointer k)
680 {
681         pol_hash_key *key = (pol_hash_key *)k;
682
683         /* Bytes 4-7 of the policy handle are a timestamp so should make a
684            reasonable hash value */
685         
686         return key->policy_hnd[4] + (key->policy_hnd[5] << 8) +
687                 (key->policy_hnd[6] << 16) + (key->policy_hnd[7] << 24);
688 }
689
690 /* Return true if a policy handle is all zeros */
691
692 static gboolean is_null_pol(const guint8 *policy_hnd)
693 {
694         static guint8 null_policy_hnd[20];
695
696         return memcmp(policy_hnd, null_policy_hnd, 20) == 0;
697 }
698
699 /* Hash compare function */
700
701 static gint pol_hash_compare(gconstpointer k1, gconstpointer k2)
702 {
703         pol_hash_key *key1 = (pol_hash_key *)k1;
704         pol_hash_key *key2 = (pol_hash_key *)k2;
705
706         return memcmp(key1->policy_hnd, key2->policy_hnd, 
707                       sizeof(key1->policy_hnd)) == 0;
708 }
709
710 /* Store a policy handle */
711
712 void dcerpc_smb_store_pol(const guint8 *policy_hnd, char *name, int open_frame,
713                           int close_frame)
714 {
715         pol_hash_key *key;
716         pol_hash_value *value;
717
718         if (is_null_pol(policy_hnd))
719                 return;
720
721         /* Look up existing value */
722
723         key = g_mem_chunk_alloc(pol_hash_key_chunk);
724
725         memcpy(&key->policy_hnd, policy_hnd, sizeof(key->policy_hnd));
726
727         if ((value = g_hash_table_lookup(pol_hash, key))) {
728
729                 /* Update existing value */
730
731                 if (value->name && name) {
732 #ifdef DEBUG_HASH_COLL
733                         if (strcmp(value->name, name) != 0)
734                                 g_warning("dcerpc_smb: pol_hash name collision %s/%s\n", value->name, name);
735 #endif
736                         free(value->name);
737                         value->name = strdup(name);
738                 }
739
740                 if (open_frame) {
741 #ifdef DEBUG_HASH_COLL
742                         if (value->open_frame != open_frame)
743                                 g_warning("dcerpc_smb: pol_hash open frame collision %d/%d\n", value->open_frame, open_frame);
744 #endif
745                         value->open_frame = open_frame;
746                 }
747
748                 if (close_frame) {
749 #ifdef DEBUG_HASH_COLL
750                         if (value->close_frame != close_frame)
751                                 g_warning("dcerpc_smb: pol_hash close frame collision %d/%d\n", value->close_frame, close_frame);
752 #endif
753                         value->close_frame = close_frame;
754                 }
755
756                 return;
757         }
758
759         /* Create a new value */
760
761         value = g_mem_chunk_alloc(pol_hash_value_chunk);
762
763         value->open_frame = open_frame;
764         value->close_frame = close_frame;
765
766         if (name)
767                 value->name = strdup(name);
768         else
769                 value->name = strdup("UNKNOWN");
770
771         g_hash_table_insert(pol_hash, key, value);
772 }
773
774 /* Retrieve a policy handle */
775
776 gboolean dcerpc_smb_fetch_pol(const guint8 *policy_hnd, char **name, 
777                               int *open_frame, int *close_frame)
778 {
779         pol_hash_key key;
780         pol_hash_value *value;
781
782         /* Prevent uninitialised return vars */
783
784         if (name)
785                 *name = NULL;
786
787         if (open_frame)
788                 *open_frame = 0;
789
790         if (close_frame)
791                 *close_frame = 0;
792
793         /* Look up existing value */
794
795         memcpy(&key.policy_hnd, policy_hnd, sizeof(key.policy_hnd));
796
797         value = g_hash_table_lookup(pol_hash, &key);
798
799         if (!value)
800                 return FALSE;
801
802         /* Return name and frame numbers */
803
804         if (name)
805                 *name = value->name;
806
807         if (open_frame)
808                 *open_frame = value->open_frame;
809
810         if (close_frame)
811                 *close_frame = value->close_frame;
812
813         return TRUE;
814 }
815
816 /* Iterator to free a policy handle key/value pair */
817
818 static void free_pol_keyvalue(gpointer key, gpointer value, gpointer user_data)
819 {
820         pol_hash_value *pol_value = (pol_hash_value *)value;
821
822         /* Free user data */
823
824         if (pol_value->name) {
825                 free(pol_value->name);
826                 pol_value->name = NULL;
827         }
828 }
829
830 /* Initialise policy handle hash */
831
832 static void init_pol_hash(void)
833 {
834         /* Initialise memory chunks */
835
836         if (pol_hash_key_chunk)
837                 g_mem_chunk_destroy(pol_hash_key_chunk);
838
839         pol_hash_key_chunk = g_mem_chunk_new(
840                 "Policy handle hash keys", sizeof(pol_hash_key),
841                 POL_HASH_INIT_COUNT * sizeof(pol_hash_key), G_ALLOC_ONLY);
842                                             
843         if (pol_hash_value_chunk)
844                 g_mem_chunk_destroy(pol_hash_value_chunk);
845
846         pol_hash_value_chunk = g_mem_chunk_new(
847                 "Policy handle hash values", sizeof(pol_hash_value),
848                 POL_HASH_INIT_COUNT * sizeof(pol_hash_value), G_ALLOC_ONLY);
849
850         /* Initialise hash table */
851
852         if (pol_hash) {
853                 g_hash_table_foreach(pol_hash, free_pol_keyvalue, NULL);
854                 g_hash_table_destroy(pol_hash);
855         }
856
857         pol_hash = g_hash_table_new(pol_hash_fn, pol_hash_compare);
858 }
859
860 /*
861  * Initialise global DCERPC/SMB data structures
862  */
863
864 static void dcerpc_smb_init(void)
865 {
866         /* Initialise policy handle hash */
867
868         init_pol_hash();
869 }
870
871 /*
872  * Register ett_ values, and register "dcerpc_smb_init()" as an
873  * initialisation routine.
874  */
875 void proto_register_dcerpc_smb(void)
876 {
877         static gint *ett[] = {
878                 &ett_nt_unicode_string,
879                 &ett_nt_policy_hnd,
880         };
881
882
883         /* Register ett's */
884
885         proto_register_subtree_array(ett, array_length(ett));
886
887         /* Register a routine to be called whenever initialisation
888            is done. */
889
890         register_init_routine(dcerpc_smb_init);
891 }
892
893 /* Check if there is unparsed data remaining in a frame and display an
894    error.  I guess this could be made into an exception like the malformed
895    frame exception.  For the DCERPC over SMB dissectors a long frame
896    indicates a bug in a dissector. */
897
898 void dcerpc_smb_check_long_frame(tvbuff_t *tvb, int offset, 
899                                  packet_info *pinfo, proto_tree *tree)
900 {
901         if (tvb_length_remaining(tvb, offset) != 0) {
902
903                 proto_tree_add_text(tree, tvb, offset, 0, 
904                                     "[Long frame (%d bytes): SPOOLSS]",
905                                     tvb_length_remaining(tvb, offset));
906
907                 if (check_col(pinfo->cinfo, COL_INFO))
908                         col_append_fstr(pinfo->cinfo, COL_INFO,
909                                         "[Long frame (%d bytes): SPOOLSS]",
910                                         tvb_length_remaining(tvb, offset));
911         }
912 }
913
914 /* Dissect a NT status code */
915
916 int
917 dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
918                  proto_tree *tree, char *drep, 
919                  int hfindex, guint32 *pdata)
920 {
921         guint32 status;
922
923         offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
924                                     hfindex, &status);
925
926         if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
927                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
928                                 val_to_str(status, NT_errors, 
929                                            "Unknown error"));
930         if (pdata)
931                 *pdata = status;
932
933         return offset;
934 }
935
936 /* Dissect a NT policy handle */
937
938 int
939 dissect_nt_policy_hnd(tvbuff_t *tvb, gint offset, packet_info *pinfo,
940                       proto_tree *tree, char *drep, int hfindex, 
941                       e_ctx_hnd *pdata)
942 {
943         dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
944         proto_item *item;
945         proto_tree *subtree;
946         e_ctx_hnd hnd;
947         int open_frame = 0, close_frame = 0;
948         char *name;
949
950         /* Add to proto tree */
951
952         item = proto_tree_add_text(tree, tvb, offset, 0, "Policy Handle");
953
954         subtree = proto_item_add_subtree(item, ett_nt_policy_hnd);
955
956         offset = dissect_ndr_ctx_hnd(tvb, offset, pinfo, subtree, drep, 
957                                      hfindex, &hnd); 
958
959         /* Insert request/reply information if known */
960
961         if (dcerpc_smb_fetch_pol((const guint8 *)&hnd, &name, &open_frame, 
962                                  &close_frame)) {
963
964                 if (open_frame)
965                         proto_tree_add_text(subtree, tvb, offset, 0,
966                                             "Opened in frame %d", open_frame);
967
968                 if (close_frame)
969                         proto_tree_add_text(subtree, tvb, offset, 0,
970                                             "Closed in frame %d", close_frame);
971         }
972
973         /* Store request/reply information */
974                 
975         if (di->request)
976                 dcerpc_smb_store_pol((const guint8 *)&hnd, NULL, 0,
977                                      pinfo->fd->num); 
978         else
979                 dcerpc_smb_store_pol((const guint8 *)&hnd, NULL, 
980                                      pinfo->fd->num, 0);
981
982         if (pdata)
983                 *pdata = hnd;
984
985         return offset;
986 }