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