More patches from Guy to make wiretap compile better. I definitely
[obnox/wireshark/wip.git] / packet-nbns.c
1 /* packet-nbns.c
2  * Routines for NetBIOS Name Service packet disassembly
3  * Gilbert Ramirez <gram@verdict.uthscsa.edu>
4  * Much stuff added by Guy Harris <guy@netapp.com>
5  *
6  * $Id: packet-nbns.c,v 1.6 1998/11/12 00:06:32 gram Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <gtk/gtk.h>
33
34 #include <stdio.h>
35 #include <memory.h>
36
37 #ifdef HAVE_SYS_TYPES_H
38 # include <sys/types.h>
39 #endif
40
41 #ifdef HAVE_NETINET_IN_H
42 # include <netinet/in.h>
43 #endif
44
45 #include "ethereal.h"
46 #include "packet.h"
47 #include "packet-dns.h"
48
49 /* Packet structure taken from RFC 1002. See also RFC 1001.
50  * The Samba source code, specifically nmblib.c, also helps a lot. */
51
52 struct nbns_header {
53
54         guint16         name_tran_id;
55         guint8          r;
56         guint8          opcode;
57         struct {
58                 guint8  bcast;
59                 guint8  recursion_available;
60                 guint8  recursion_desired;
61                 guint8  trunc;
62                 guint8  authoritative;
63         } nm_flags;
64         guint8          rcode;
65         guint16         qdcount;
66         guint16         ancount;
67         guint16         nscount;
68         guint16         arcount;
69 };
70
71 /* type values  */
72 #define T_NB            32              /* NetBIOS name service RR */
73 #define T_NBSTAT        33              /* NetBIOS node status RR */
74
75
76 static char *
77 nbns_type_name (int type)
78 {
79         switch (type) {
80         case T_NB:
81                 return "NB";
82         case T_NBSTAT:
83                 return "NBSTAT";
84         }
85         
86         return "unknown";
87 }
88
89 /* "Canonicalize" a 16-character NetBIOS name by:
90  *
91  *      removing and saving the last byte;
92  *
93  *      stripping trailing blanks;
94  *
95  *      appending the trailing byte, as a hex number, in square brackets. */
96 static char *
97 canonicalize_netbios_name(char *nbname)
98 {
99         char *pnbname;
100         u_char lastchar;
101
102         /* Get the last character of the name, as it's a special number
103          * indicating the type of the name, rather than part of the name
104          * *per se*. */
105         pnbname = nbname + 15;  /* point to the 16th character */
106         lastchar = *(unsigned char *)pnbname;
107
108         /* Now strip off any trailing blanks used to pad it to
109          * 16 bytes. */
110         while (pnbname > &nbname[0]) {
111                 if (*(pnbname - 1) != ' ')
112                         break;          /* found non-blank character */
113                 pnbname--;              /* blank - skip over it */
114         }
115
116         /* Replace the last character with its hex value, in square
117          * brackets, to make it easier to tell what it is. */
118         sprintf(pnbname, "[%02X]", lastchar);
119         pnbname += 4;
120         return pnbname;
121 }
122
123 static int
124 get_nbns_name_type_class(const u_char *nbns_data_ptr, const u_char *pd,
125     int offset, char *name_ret, int *name_len_ret, int *type_ret,
126     int *class_ret)
127 {
128         int len;
129         int name_len;
130         int type;
131         int class;
132         char name[MAXDNAME];
133         char nbname[MAXDNAME+4];        /* 4 for [<last char>] */
134         char *pname, *pnbname, cname, cnbname;
135         const u_char *pd_save;
136
137         name_len = get_dns_name(nbns_data_ptr, pd, offset, name, sizeof(name));
138         pd += offset;
139         pd_save = pd;
140         pd += name_len;
141         
142         type = pntohs(pd);
143         pd += 2;
144         class = pntohs(pd);
145         pd += 2;
146
147         /* OK, now undo the first-level encoding. */
148         pname = &name[0];
149         pnbname = &nbname[0];
150         for (;;) {
151                 /* Every two characters of the first level-encoded name
152                  * turn into one character in the decoded name. */
153                 cname = *pname;
154                 if (cname == '\0')
155                         break;          /* no more characters */
156                 if (cname == '.')
157                         break;          /* scope ID follows */
158                 if (cname < 'A' || cname > 'Z') {
159                         /* Not legal. */
160                         strcpy(nbname,
161                             "Illegal NetBIOS name (character not between A and Z in first-level encoding)");
162                         goto bad;
163                 }
164                 cname -= 'A';
165                 cnbname = cname << 4;
166                 pname++;
167
168                 cname = *pname;
169                 if (cname == '\0' || cname == '.') {
170                         /* No more characters in the name - but we're in
171                          * the middle of a pair.  Not legal. */
172                         strcpy(nbname,
173                             "Illegal NetBIOS name (odd number of bytes)");
174                         goto bad;
175                 }
176                 if (cname < 'A' || cname > 'Z') {
177                         /* Not legal. */
178                         strcpy(nbname,
179                             "Illegal NetBIOS name (character not between A and Z in first-level encoding)");
180                         goto bad;
181                 }
182                 cname -= 'A';
183                 cnbname |= cname;
184                 pname++;
185
186                 /* Store the character. */
187                 *pnbname++ = cnbname;
188         }
189
190         /* NetBIOS names are supposed to be exactly 16 bytes long. */
191         if (pnbname - nbname == 16) {
192                 /* This one is; canonicalize its name. */
193                 pnbname = canonicalize_netbios_name(nbname);
194         } else {
195                 sprintf(nbname, "Illegal NetBIOS name (%ld bytes long)",
196                     (long)(pnbname - nbname));
197                 goto bad;
198         }
199         if (cname == '.') {
200                 /* We have a scope ID, starting at "pname"; append that to
201                  * the decoded host name. */
202                 strcpy(pnbname, pname);
203         } else {
204                 /* Terminate the decoded host name. */
205                 *pnbname = '\0';
206         }
207
208 bad:
209         strcpy (name_ret, nbname);
210         *type_ret = type;
211         *class_ret = class;
212         *name_len_ret = name_len;
213
214         len = pd - pd_save;
215         return len;
216 }
217
218
219 static int
220 dissect_nbns_query(const u_char *nbns_data_ptr, const u_char *pd, int offset,
221     GtkWidget *nbns_tree)
222 {
223         int len;
224         char name[MAXDNAME];
225         int name_len;
226         int type;
227         int class;
228         char *class_name;
229         char *type_name;
230         const u_char *dptr;
231         const u_char *data_start;
232         GtkWidget *q_tree, *tq;
233
234         data_start = dptr = pd + offset;
235
236         len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name,
237             &name_len, &type, &class);
238         dptr += len;
239
240         type_name = nbns_type_name(type);
241         class_name = dns_class_name(class);
242
243         tq = add_item_to_tree(nbns_tree, offset, len, "%s: type %s, class %s", 
244             name, type_name, class_name);
245         q_tree = gtk_tree_new();
246         add_subtree(tq, q_tree, ETT_NBNS_QD);
247
248         add_item_to_tree(q_tree, offset, name_len, "Name: %s", name);
249         offset += name_len;
250
251         add_item_to_tree(q_tree, offset, 2, "Type: %s", type_name);
252         offset += 2;
253
254         add_item_to_tree(q_tree, offset, 2, "Class: %s", class_name);
255         offset += 2;
256         
257         return dptr - data_start;
258 }
259
260
261 static int
262 dissect_nbns_answer(const u_char *nbns_data_ptr, const u_char *pd, int offset,
263     GtkWidget *nbns_tree, int opcode)
264 {
265         int len;
266         char name[MAXDNAME];
267         int name_len;
268         int type;
269         int class;
270         char *class_name;
271         char *type_name;
272         const u_char *dptr;
273         const u_char *data_start;
274         u_int ttl;
275         u_short data_len;
276         u_short flags;
277         GtkWidget *rr_tree, *trr;
278
279         data_start = dptr = pd + offset;
280
281         len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name,
282             &name_len, &type, &class);
283         dptr += len;
284
285         type_name = nbns_type_name(type);
286         class_name = dns_class_name(class);
287
288         ttl = pntohl(dptr);
289         dptr += 4;
290
291         data_len = pntohs(dptr);
292         dptr += 2;
293
294         switch (type) {
295         case T_NB:              /* "NB" record */
296                 trr = add_item_to_tree(nbns_tree, offset,
297                     (dptr - data_start) + data_len,
298                     "%s: type %s, class %s",
299                     name, type_name, class_name);
300                 rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
301                     name_len, type_name, class_name, ttl, data_len);
302                 offset += (dptr - data_start);
303                 while (data_len > 0) {
304                         if (opcode == 0x7) {
305                                 /* WACK response.  This doesn't contain the
306                                  * same type of RR data as other T_NB
307                                  * responses.  */
308                                 if (data_len < 2) {
309                                         add_item_to_tree(rr_tree, offset,
310                                             data_len, "(incomplete entry)");
311                                         break;
312                                 }
313                                 flags = pntohs(dptr);
314                                 dptr += 2;
315                                 add_item_to_tree(rr_tree, offset, 2,
316                                     "Flags: 0x%x", flags);
317                                 offset += 2;
318                                 data_len -= 2;
319                         } else {
320                                 if (data_len < 2) {
321                                         add_item_to_tree(rr_tree, offset,
322                                             data_len, "(incomplete entry)");
323                                         break;
324                                 }
325                                 flags = pntohs(dptr);
326                                 dptr += 2;
327                                 add_item_to_tree(rr_tree, offset, 2,
328                                     "Flags: 0x%x", flags);
329                                 offset += 2;
330                                 data_len -= 2;
331
332                                 if (data_len < 4) {
333                                         add_item_to_tree(rr_tree, offset,
334                                             data_len, "(incomplete entry)");
335                                         break;
336                                 }
337                                 add_item_to_tree(rr_tree, offset, 4,
338                                     "Addr: %s",
339                                     ip_to_str((guint8 *)dptr));
340                                 dptr += 4;
341                                 offset += 4;
342                                 data_len -= 4;
343                         }
344                 }
345                 break;
346
347         case T_NBSTAT:  /* "NBSTAT" record */
348                 {
349                         u_int num_names;
350                         char nbname[16+4+1];    /* 4 for [<last char>] */
351                         u_short name_flags;
352                         
353                         trr = add_item_to_tree(nbns_tree, offset,
354                             (dptr - data_start) + data_len,
355                             "%s: type %s, class %s",
356                             name, type_name, class_name);
357                         rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
358                             name_len, type_name, class_name, ttl, data_len);
359                         offset += (dptr - data_start);
360                         if (data_len < 1) {
361                                 add_item_to_tree(rr_tree, offset,
362                                     data_len, "(incomplete entry)");
363                                 break;
364                         }
365                         num_names = *dptr;
366                         dptr += 1;
367                         add_item_to_tree(rr_tree, offset, 2,
368                             "Number of names: %u", num_names);
369                         offset += 1;
370
371                         while (num_names != 0) {
372                                 if (data_len < 16) {
373                                         add_item_to_tree(rr_tree, offset,
374                                             data_len, "(incomplete entry)");
375                                         goto out;
376                                 }
377                                 memcpy(nbname, dptr, 16);
378                                 dptr += 16;
379                                 canonicalize_netbios_name(nbname);
380                                 add_item_to_tree(rr_tree, offset, 16,
381                                     "Name: %s", nbname);
382                                 offset += 16;
383                                 data_len -= 16;
384
385                                 if (data_len < 2) {
386                                         add_item_to_tree(rr_tree, offset,
387                                             data_len, "(incomplete entry)");
388                                         goto out;
389                                 }
390                                 name_flags = pntohs(dptr);
391                                 dptr += 2;
392                                 add_item_to_tree(rr_tree, offset, 2,
393                                     "Name flags: 0x%x", name_flags);
394                                 offset += 2;
395                                 data_len -= 2;
396
397                                 num_names--;
398                         }
399
400                         if (data_len < 6) {
401                                 add_item_to_tree(rr_tree, offset,
402                                     data_len, "(incomplete entry)");
403                                 break;
404                         }
405                         add_item_to_tree(rr_tree, offset, 6,
406                             "Unit ID: %s",
407                             ether_to_str((guint8 *)dptr));
408                         dptr += 6;
409                         offset += 6;
410                         data_len -= 6;
411
412                         if (data_len < 1) {
413                                 add_item_to_tree(rr_tree, offset,
414                                     data_len, "(incomplete entry)");
415                                 break;
416                         }
417                         add_item_to_tree(rr_tree, offset, 1,
418                             "Jumpers: 0x%x", *dptr);
419                         dptr += 1;
420                         offset += 1;
421                         data_len -= 1;
422
423                         if (data_len < 1) {
424                                 add_item_to_tree(rr_tree, offset,
425                                     data_len, "(incomplete entry)");
426                                 break;
427                         }
428                         add_item_to_tree(rr_tree, offset, 1,
429                             "Test result: 0x%x", *dptr);
430                         dptr += 1;
431                         offset += 1;
432                         data_len -= 1;
433
434                         if (data_len < 2) {
435                                 add_item_to_tree(rr_tree, offset,
436                                     data_len, "(incomplete entry)");
437                                 break;
438                         }
439                         add_item_to_tree(rr_tree, offset, 2,
440                             "Version number: 0x%x", pntohs(dptr));
441                         dptr += 2;
442                         offset += 2;
443                         data_len -= 2;
444
445                         if (data_len < 2) {
446                                 add_item_to_tree(rr_tree, offset,
447                                     data_len, "(incomplete entry)");
448                                 break;
449                         }
450                         add_item_to_tree(rr_tree, offset, 2,
451                             "Period of statistics: 0x%x", pntohs(dptr));
452                         dptr += 2;
453                         offset += 2;
454                         data_len -= 2;
455
456                         if (data_len < 2) {
457                                 add_item_to_tree(rr_tree, offset,
458                                     data_len, "(incomplete entry)");
459                                 break;
460                         }
461                         add_item_to_tree(rr_tree, offset, 2,
462                             "Number of CRCs: %u", pntohs(dptr));
463                         dptr += 2;
464                         offset += 2;
465                         data_len -= 2;
466
467                         if (data_len < 2) {
468                                 add_item_to_tree(rr_tree, offset,
469                                     data_len, "(incomplete entry)");
470                                 break;
471                         }
472                         add_item_to_tree(rr_tree, offset, 2,
473                             "Number of alignment errors: %u", pntohs(dptr));
474                         dptr += 2;
475                         offset += 2;
476                         data_len -= 2;
477
478                         if (data_len < 2) {
479                                 add_item_to_tree(rr_tree, offset,
480                                     data_len, "(incomplete entry)");
481                                 break;
482                         }
483                         add_item_to_tree(rr_tree, offset, 2,
484                             "Number of collisions: %u", pntohs(dptr));
485                         dptr += 2;
486                         offset += 2;
487                         data_len -= 2;
488
489                         if (data_len < 2) {
490                                 add_item_to_tree(rr_tree, offset,
491                                     data_len, "(incomplete entry)");
492                                 break;
493                         }
494                         add_item_to_tree(rr_tree, offset, 2,
495                             "Number of send aborts: %u", pntohs(dptr));
496                         dptr += 2;
497                         offset += 2;
498                         data_len -= 2;
499
500                         if (data_len < 4) {
501                                 add_item_to_tree(rr_tree, offset,
502                                     data_len, "(incomplete entry)");
503                                 break;
504                         }
505                         add_item_to_tree(rr_tree, offset, 4,
506                             "Number of good sends: %u", pntohl(dptr));
507                         dptr += 4;
508                         offset += 4;
509                         data_len -= 4;
510
511                         if (data_len < 4) {
512                                 add_item_to_tree(rr_tree, offset,
513                                     data_len, "(incomplete entry)");
514                                 break;
515                         }
516                         add_item_to_tree(rr_tree, offset, 4,
517                             "Number of good receives: %u", pntohl(dptr));
518                         dptr += 4;
519                         offset += 4;
520                         data_len -= 4;
521
522                         if (data_len < 2) {
523                                 add_item_to_tree(rr_tree, offset,
524                                     data_len, "(incomplete entry)");
525                                 break;
526                         }
527                         add_item_to_tree(rr_tree, offset, 2,
528                             "Number of retransmits: %u", pntohs(dptr));
529                         dptr += 2;
530                         offset += 2;
531                         data_len -= 2;
532
533                         if (data_len < 2) {
534                                 add_item_to_tree(rr_tree, offset,
535                                     data_len, "(incomplete entry)");
536                                 break;
537                         }
538                         add_item_to_tree(rr_tree, offset, 2,
539                             "Number of no resource conditions: %u", pntohs(dptr));
540                         dptr += 2;
541                         offset += 2;
542                         data_len -= 2;
543
544                         if (data_len < 2) {
545                                 add_item_to_tree(rr_tree, offset,
546                                     data_len, "(incomplete entry)");
547                                 break;
548                         }
549                         add_item_to_tree(rr_tree, offset, 2,
550                             "Number of command blocks: %u", pntohs(dptr));
551                         dptr += 2;
552                         offset += 2;
553                         data_len -= 2;
554
555                         if (data_len < 2) {
556                                 add_item_to_tree(rr_tree, offset,
557                                     data_len, "(incomplete entry)");
558                                 break;
559                         }
560                         add_item_to_tree(rr_tree, offset, 2,
561                             "Number of pending sessions: %u", pntohs(dptr));
562                         dptr += 2;
563                         offset += 2;
564                         data_len -= 2;
565
566                         if (data_len < 2) {
567                                 add_item_to_tree(rr_tree, offset,
568                                     data_len, "(incomplete entry)");
569                                 break;
570                         }
571                         add_item_to_tree(rr_tree, offset, 2,
572                             "Max number of pending sessions: %u", pntohs(dptr));
573                         dptr += 2;
574                         offset += 2;
575
576                         add_item_to_tree(rr_tree, offset, 2,
577                             "Max total sessions possible: %u", pntohs(dptr));
578                         dptr += 2;
579                         offset += 2;
580                         data_len -= 2;
581
582                         if (data_len < 2) {
583                                 add_item_to_tree(rr_tree, offset,
584                                     data_len, "(incomplete entry)");
585                                 break;
586                         }
587                         add_item_to_tree(rr_tree, offset, 2,
588                             "Session data packet size: %u", pntohs(dptr));
589                         dptr += 2;
590                         offset += 2;
591                         data_len -= 2;
592                 }
593         out:
594                 break;
595
596         default:
597                 trr = add_item_to_tree(nbns_tree, offset,
598                     (dptr - data_start) + data_len,
599                     "%s: type %s, class %s",
600                     name, type_name, class_name);
601                 rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
602                     name_len, type_name, class_name, ttl, data_len);
603                 offset += (dptr - data_start);
604                 add_item_to_tree(rr_tree, offset, data_len, "Data");
605                 break;
606         }
607         dptr += data_len;
608         
609         return dptr - data_start;
610 }
611
612 static int
613 dissect_query_records(const u_char *nbns_data_ptr, int count, const u_char *pd, 
614     int cur_off, GtkWidget *nbns_tree)
615 {
616         int start_off;
617         GtkWidget *qatree, *ti;
618         
619         qatree = gtk_tree_new();
620         start_off = cur_off;
621         
622         while (count-- > 0)
623                 cur_off += dissect_nbns_query(nbns_data_ptr, pd, cur_off, qatree);
624         ti = add_item_to_tree(GTK_WIDGET(nbns_tree), 
625                         start_off, cur_off - start_off, "Queries");
626         add_subtree(ti, qatree, ETT_NBNS_QRY);
627
628         return cur_off - start_off;
629 }
630
631
632
633 static int
634 dissect_answer_records(const u_char *nbns_data_ptr, int count,
635     const u_char *pd, int cur_off, GtkWidget *nbns_tree, int opcode, char *name)
636 {
637         int start_off;
638         GtkWidget *qatree, *ti;
639         
640         qatree = gtk_tree_new();
641         start_off = cur_off;
642
643         while (count-- > 0)
644                 cur_off += dissect_nbns_answer(nbns_data_ptr, pd, cur_off,
645                                         qatree, opcode);
646         ti = add_item_to_tree(GTK_WIDGET(nbns_tree), start_off, cur_off - start_off, name);
647         add_subtree(ti, qatree, ETT_NBNS_ANS);
648
649         return cur_off - start_off;
650 }
651
652 void
653 dissect_nbns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree)
654 {
655         GtkWidget               *nbns_tree, *ti;
656         struct nbns_header      header;
657         int                     nm_flags;
658         const u_char            *nbns_data_ptr;
659         int                     cur_off;
660
661         char *opcode[] = {
662                 "Query",
663                 "Unknown operation (1)",
664                 "Unknown operation (2)",
665                 "Unknown operation (3)",
666                 "Unknown operation (4)",
667                 "Registration",
668                 "Release",
669                 "Wait and Acknowledge",
670                 "Refresh",
671                 "Refresh(altcode)",
672                 "Unknown operation (10)",
673                 "Unknown operation (11)",
674                 "Unknown operation (12)",
675                 "Unknown operation (13)",
676                 "Unknown operation (14)",
677                 "Multi-Homed Registration",
678         };
679
680         nbns_data_ptr = &pd[offset];
681
682         /* This is taken from samba/source/nmlib.c, parse_nmb() */
683         header.name_tran_id = pntohs(&pd[offset]);
684         header.opcode = (pd[offset+2] >> 3) & 0xf;
685         header.r = (pd[offset+2] >> 7) & 1;
686
687         nm_flags = ((pd[offset+2] & 0x7) << 4) + (pd[offset+3] >> 4);
688         header.nm_flags.bcast = (nm_flags & 1) ? 1 : 0;
689         header.nm_flags.recursion_available = (nm_flags & 8) ? 1 : 0;
690         header.nm_flags.recursion_desired = (nm_flags & 0x10) ? 1 : 0;
691         header.nm_flags.trunc = (nm_flags & 0x20) ? 1 : 0;
692         header.nm_flags.authoritative = (nm_flags & 0x40) ? 1 : 0;
693
694         header.rcode = pd[offset+3] & 0xf;
695         header.qdcount = pntohs(&pd[offset+4]);
696         header.ancount = pntohs(&pd[offset+6]);
697         header.nscount = pntohs(&pd[offset+8]);
698         header.arcount = pntohs(&pd[offset+10]);
699
700         if (fd->win_info[COL_NUM]) {
701                 strcpy(fd->win_info[COL_PROTOCOL], "NBNS (UDP)");
702                 if (header.opcode <= 15) {
703                         sprintf(fd->win_info[COL_INFO], "%s %s",
704                             opcode[header.opcode], header.r ? "reply" : "request");
705                 } else {
706                         sprintf(fd->win_info[COL_INFO], "Unknown operation (%d) %s",
707                             header.opcode, header.r ? "reply" : "request");
708                 }
709         }
710
711         if (tree) {
712                 ti = add_item_to_tree(GTK_WIDGET(tree), offset, END_OF_FRAME,
713                                 "NetBIOS Name Service");
714                 nbns_tree = gtk_tree_new();
715                 add_subtree(ti, nbns_tree, ETT_NBNS);
716
717                 add_item_to_tree(nbns_tree, offset,      2, "Transaction ID: 0x%04X",
718                                 header.name_tran_id);
719                 add_item_to_tree(nbns_tree, offset +  2, 1, "Type: %s",
720                                 header.r == 0 ? "Request" : "Response" );
721                 
722                 if (header.opcode <= 15) {
723                         add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: %s (%d)",
724                                         opcode[header.opcode], header.opcode);
725                 }
726                 else {
727                         add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: Unknown (%d)",
728                                         header.opcode);
729                 }
730                 add_item_to_tree(nbns_tree, offset +  4, 2, "Questions: %d",
731                                         header.qdcount);
732                 add_item_to_tree(nbns_tree, offset +  6, 2, "Answer RRs: %d",
733                                         header.ancount);
734                 add_item_to_tree(nbns_tree, offset +  8, 2, "Authority RRs: %d",
735                                         header.nscount);
736                 add_item_to_tree(nbns_tree, offset + 10, 2, "Additional RRs: %d",
737                                         header.arcount);
738
739                 cur_off = offset + 12;
740     
741                 if (header.qdcount > 0)
742                         cur_off += dissect_query_records(nbns_data_ptr,
743                                         header.qdcount, pd, cur_off, nbns_tree);
744
745                 if (header.ancount > 0)
746                         cur_off += dissect_answer_records(nbns_data_ptr,
747                                         header.ancount, pd, cur_off, nbns_tree,
748                                         header.opcode, "Answers");
749
750                 if (header.nscount > 0)
751                         cur_off += dissect_answer_records(nbns_data_ptr,
752                                         header.nscount, pd, cur_off, nbns_tree, 
753                                         header.opcode,
754                                         "Authoritative nameservers");
755
756                 if (header.arcount > 0)
757                         cur_off += dissect_answer_records(nbns_data_ptr,
758                                         header.arcount, pd, cur_off, nbns_tree, 
759                                         header.opcode, "Additional records");
760         }
761 }