Updates from Tim Potter.
[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.3 2002/01/07 19:55:48 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 "packet.h"
32 #include "packet-dcerpc.h"
33 #include "packet-dcerpc-nt.h"
34 #include "smb.h"
35
36 /*
37  * This file contains helper routines that are used by the DCERPC over SMB
38  * dissectors for ethereal.
39  */
40
41 /* Align offset to a n-byte boundary */
42
43 int prs_align(int offset, int n)
44 {
45         if (offset % n)
46                 offset += n - (offset % n);
47         
48         return offset;
49 }
50
51 /* Parse a 8-bit integer */
52
53 int prs_uint8(tvbuff_t *tvb, int offset, packet_info *pinfo,
54               proto_tree *tree, guint8 *data, char *name)
55 {
56         guint8 i;
57         
58         /* No alignment required */
59
60         i = tvb_get_guint8(tvb, offset);
61         offset++;
62
63         if (name && tree)
64                 proto_tree_add_text(tree, tvb, offset - 1, 1, 
65                                     "%s: %d", name, i);
66
67         if (data)
68                 *data = i;
69
70         return offset;
71 }
72
73 int prs_uint8s(tvbuff_t *tvb, int offset, packet_info *pinfo,
74                proto_tree *tree, int count, guint8 **data, char *name)
75 {
76         const guint8 *ptr;
77         
78         /* The tvb_get_ptr() function fails an assertion if count < -1 */
79         
80         if (count < -1)
81                 THROW(BoundsError);
82
83         /* No alignment required */
84
85         ptr = tvb_get_ptr(tvb, offset, count);
86
87         if (name && tree)
88                 proto_tree_add_text(tree, tvb, offset, count, "%s", name);
89
90         if (data)
91                 *data = (guint8 *)ptr;
92
93         offset += count;
94
95         return offset;
96 }
97
98 /* Parse a 16-bit integer */
99
100 int prs_uint16(tvbuff_t *tvb, int offset, packet_info *pinfo,
101                proto_tree *tree, guint16 *data, char *name)
102 {
103         guint16 i;
104         
105         offset = prs_align(offset, 2);
106         
107         i = tvb_get_letohs(tvb, offset);
108         offset += 2;
109
110         if (name && tree)
111                 proto_tree_add_text(tree, tvb, offset - 2, 2, 
112                                     "%s: %d", name, i);
113         if (data)
114                 *data = i;
115
116         return offset;
117 }
118
119 /* Parse a number of uint16's */
120
121 int prs_uint16s(tvbuff_t *tvb, int offset, packet_info *pinfo,
122                 proto_tree *tree, int count, guint16 **data, char *name)
123 {
124         const guint8 *ptr;
125         
126         /* The tvb_get_ptr() function fails an assertion if count < -1 */
127         
128         if (count < -1)
129                 THROW(BoundsError);
130
131         offset = prs_align(offset, 2);
132         
133         ptr = tvb_get_ptr(tvb, offset, count * 2);
134
135         if (name && tree)
136                 proto_tree_add_text(tree, tvb, offset, count * 2, 
137                                     "%s", name);
138         if (data)
139                 *data = (guint16 *)ptr;
140
141         offset += count * 2;
142
143         return offset;
144 }
145
146 /* Parse a 32-bit integer */
147
148 int prs_uint32(tvbuff_t *tvb, int offset, packet_info *pinfo,
149                proto_tree *tree, guint32 *data, char *name)
150 {
151         guint32 i;
152         
153         offset = prs_align(offset, 4);
154         
155         i = tvb_get_letohl(tvb, offset);
156         offset += 4;
157
158         if (name && tree)
159                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
160                                     "%s: %d", name, i);
161
162         if (data)
163                 *data = i;
164
165         return offset;
166 }
167
168 /* Parse a number of 32-bit integers */
169
170 int prs_uint32s(tvbuff_t *tvb, int offset, packet_info *pinfo,
171                 proto_tree *tree, int count, guint32 **data, char *name)
172 {
173         const guint8 *ptr;
174         
175         /* The tvb_get_ptr() function fails an assertion if count < -1 */
176         
177         if (count < -1)
178                 THROW(BoundsError);
179
180         offset = prs_align(offset, 4);
181         
182         ptr = tvb_get_ptr(tvb, offset, count * 4);
183
184         if (name && tree)
185                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
186                                     "%s", name);
187         if (data)
188                 *data = (guint32 *)ptr;
189
190         offset += count * 4;
191
192         return offset;
193 }
194
195 /* Parse a NT status code */
196
197 int prs_ntstatus(tvbuff_t *tvb, int offset, packet_info *pinfo,
198                  proto_tree *tree)
199 {
200         guint32 status;
201
202         offset = prs_uint32(tvb, offset, pinfo, tree, &status, NULL);
203
204         if (tree)
205                 proto_tree_add_text(tree, tvb, offset - 4, 4, "Status: %s",
206                                     val_to_str(status, NT_errors, "???"));
207
208         return offset;
209 }
210
211 /*
212  * We need to keep track of deferred referrents as they appear in the
213  * packet after all the non-pointer objects.
214  * to keep track of pointers as they are parsed as scalars and need to be
215  * remembered for the next call to the prs function.
216  *
217  * Pointers are stored in a linked list and pushed in the PARSE_SCALARS
218  * section of the prs function and popped in the PARSE_BUFFERS section.  If
219  * we try to pop off a referrent that has a different name then we are
220  * expecting then something has gone wrong.
221  */
222
223 #undef DEBUG_PTRS
224
225 struct ptr {
226         char *name;
227         guint32 value;
228 };
229
230 /* Create a new pointer */
231
232 static struct ptr *new_ptr(char *name, guint32 value)
233 {
234         struct ptr *p;
235
236         p = g_malloc(sizeof(struct ptr));
237
238         p->name = g_strdup(name);
239         p->value = value;
240
241         return p;
242 }
243
244 /* Free a pointer */
245
246 static void free_ptr(struct ptr *p)
247 {
248         if (p) {
249                 g_free(p->name);
250                 g_free(p);
251         }
252 }
253
254 /* Parse a pointer and store it's value in a linked list */
255
256 int prs_push_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
257                  proto_tree *tree, GList **ptr_list, char *name)
258 {
259         struct ptr *p;
260         guint32 value;
261
262         offset = prs_uint32(tvb, offset, pinfo, tree, &value, NULL);
263
264         if (name && tree)
265                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
266                                     "%s pointer: 0x%08x", name, value);
267
268         p = new_ptr(name, value);
269
270         *ptr_list = g_list_append(*ptr_list, p);
271
272 #ifdef DEBUG_PTRS
273         fprintf(stderr, "DEBUG_PTRS: pushing %s ptr = 0x%08x, %d ptrs in "
274                 "list\n", name, value, g_list_length(*ptr_list));
275 #endif
276
277         return offset;
278 }
279
280 /* Pop a pointer of a given name.  Return it's value. */
281
282 guint32 prs_pop_ptr(GList **ptr_list, char *name)
283 {
284         GList *elt;
285         struct ptr *p;
286         guint32 result;
287
288         g_assert(g_list_length(*ptr_list) != 0);        /* List too short */
289
290         /* Get pointer at head of list */
291
292         elt = g_list_first(*ptr_list);
293         p = (struct ptr *)elt->data;
294         result = p->value;
295
296 #ifdef DEBUG_PTRS
297         if (strcmp(p->name, name) != 0) {
298                 fprintf(stderr, "DEBUG_PTRS: wrong pointer (%s != %s)\n",
299                         p->name, name);
300         }
301 #endif
302
303         /* Free pointer record */
304
305         *ptr_list = g_list_remove_link(*ptr_list, elt);
306
307 #ifdef DEBUG_PTRS
308         fprintf(stderr, "DEBUG_PTRS: popping %s ptr = 0x%08x, %d ptrs in "
309                 "list\n", p->name, p->value, g_list_length(*ptr_list));
310 #endif
311
312         free_ptr(p);
313
314         return result;
315 }
316
317 /*
318  * Parse a UNISTR2 structure 
319  *
320  * typedef struct {
321  *   short length;
322  *   short size;
323  *   [size_is(size/2)] [length_is(length/2)] [unique] wchar_t *string;
324  * } UNICODE_STRING;
325  *
326  */
327
328 /* Convert a string from little-endian unicode to ascii.  At the moment we
329    fake it by taking every odd byte.  )-:  The caller must free the
330    result returned. */
331
332 char *fake_unicode(guint16 *data, int len)
333 {
334         char *buffer;
335         int i;
336
337         buffer = malloc(len + 1);
338
339         for (i = 0; i < len; i++)
340                 buffer[i] = data[i] & 0xff;
341
342         buffer[len] = 0;
343
344         return buffer;
345 }
346
347 /* Parse a UNISTR2 structure */
348
349 int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
350                 proto_tree *tree, int flags, char **data, char *name)
351 {
352         guint32 len = 0, unknown = 0, max_len = 0;
353
354         if (flags & PARSE_SCALARS) {
355                 offset = prs_uint32(tvb, offset, pinfo, tree, &len, "Length");
356                 offset = prs_uint32(tvb, offset, pinfo, tree, &unknown, 
357                                     "Offset");
358                 offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, 
359                                     "Max length");
360         }
361
362         if (flags & PARSE_BUFFERS) {
363                 guint16 *data16;
364
365                 offset = prs_uint16s(tvb, offset, pinfo, tree, max_len,
366                                      &data16, "Buffer");
367
368                 if (data)
369                         *data = fake_unicode(data16, max_len);
370         }
371
372         return offset;
373 }
374
375 /* Parse a policy handle. */
376
377 int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo, 
378                    proto_tree *tree, const guint8 **data)
379 {
380         const guint8 *data8;
381
382         offset = prs_align(offset, 4);
383
384         proto_tree_add_text(tree, tvb, offset, 20, "Policy Handle");
385
386         data8 = tvb_get_ptr(tvb, offset, 20);
387         
388         if (data)
389                 *data = data8;
390
391         return offset + 20;
392 }