Use "ip_to_str()" and "ether_to_str()" to extract IP and MAC addresses
[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.3 1998/10/14 22:37:02 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: %s",
333                                     ip_to_str((guint8 *)dptr));
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: %s",
401                             ether_to_str((guint8 *)dptr));
402                         dptr += 6;
403                         offset += 6;
404                         data_len -= 6;
405
406                         if (data_len < 1) {
407                                 add_item_to_tree(rr_tree, offset,
408                                     data_len, "(incomplete entry)");
409                                 break;
410                         }
411                         add_item_to_tree(rr_tree, offset, 1,
412                             "Jumpers: 0x%x", *dptr);
413                         dptr += 1;
414                         offset += 1;
415                         data_len -= 1;
416
417                         if (data_len < 1) {
418                                 add_item_to_tree(rr_tree, offset,
419                                     data_len, "(incomplete entry)");
420                                 break;
421                         }
422                         add_item_to_tree(rr_tree, offset, 1,
423                             "Test result: 0x%x", *dptr);
424                         dptr += 1;
425                         offset += 1;
426                         data_len -= 1;
427
428                         if (data_len < 2) {
429                                 add_item_to_tree(rr_tree, offset,
430                                     data_len, "(incomplete entry)");
431                                 break;
432                         }
433                         add_item_to_tree(rr_tree, offset, 2,
434                             "Version number: 0x%x", pntohs(dptr));
435                         dptr += 2;
436                         offset += 2;
437                         data_len -= 2;
438
439                         if (data_len < 2) {
440                                 add_item_to_tree(rr_tree, offset,
441                                     data_len, "(incomplete entry)");
442                                 break;
443                         }
444                         add_item_to_tree(rr_tree, offset, 2,
445                             "Period of statistics: 0x%x", pntohs(dptr));
446                         dptr += 2;
447                         offset += 2;
448                         data_len -= 2;
449
450                         if (data_len < 2) {
451                                 add_item_to_tree(rr_tree, offset,
452                                     data_len, "(incomplete entry)");
453                                 break;
454                         }
455                         add_item_to_tree(rr_tree, offset, 2,
456                             "Number of CRCs: %u", pntohs(dptr));
457                         dptr += 2;
458                         offset += 2;
459                         data_len -= 2;
460
461                         if (data_len < 2) {
462                                 add_item_to_tree(rr_tree, offset,
463                                     data_len, "(incomplete entry)");
464                                 break;
465                         }
466                         add_item_to_tree(rr_tree, offset, 2,
467                             "Number of alignment errors: %u", pntohs(dptr));
468                         dptr += 2;
469                         offset += 2;
470                         data_len -= 2;
471
472                         if (data_len < 2) {
473                                 add_item_to_tree(rr_tree, offset,
474                                     data_len, "(incomplete entry)");
475                                 break;
476                         }
477                         add_item_to_tree(rr_tree, offset, 2,
478                             "Number of collisions: %u", pntohs(dptr));
479                         dptr += 2;
480                         offset += 2;
481                         data_len -= 2;
482
483                         if (data_len < 2) {
484                                 add_item_to_tree(rr_tree, offset,
485                                     data_len, "(incomplete entry)");
486                                 break;
487                         }
488                         add_item_to_tree(rr_tree, offset, 2,
489                             "Number of send aborts: %u", pntohs(dptr));
490                         dptr += 2;
491                         offset += 2;
492                         data_len -= 2;
493
494                         if (data_len < 4) {
495                                 add_item_to_tree(rr_tree, offset,
496                                     data_len, "(incomplete entry)");
497                                 break;
498                         }
499                         add_item_to_tree(rr_tree, offset, 4,
500                             "Number of good sends: %u", pntohl(dptr));
501                         dptr += 4;
502                         offset += 4;
503                         data_len -= 4;
504
505                         if (data_len < 4) {
506                                 add_item_to_tree(rr_tree, offset,
507                                     data_len, "(incomplete entry)");
508                                 break;
509                         }
510                         add_item_to_tree(rr_tree, offset, 4,
511                             "Number of good receives: %u", pntohl(dptr));
512                         dptr += 4;
513                         offset += 4;
514                         data_len -= 4;
515
516                         if (data_len < 2) {
517                                 add_item_to_tree(rr_tree, offset,
518                                     data_len, "(incomplete entry)");
519                                 break;
520                         }
521                         add_item_to_tree(rr_tree, offset, 2,
522                             "Number of retransmits: %u", pntohs(dptr));
523                         dptr += 2;
524                         offset += 2;
525                         data_len -= 2;
526
527                         if (data_len < 2) {
528                                 add_item_to_tree(rr_tree, offset,
529                                     data_len, "(incomplete entry)");
530                                 break;
531                         }
532                         add_item_to_tree(rr_tree, offset, 2,
533                             "Number of no resource conditions: %u", pntohs(dptr));
534                         dptr += 2;
535                         offset += 2;
536                         data_len -= 2;
537
538                         if (data_len < 2) {
539                                 add_item_to_tree(rr_tree, offset,
540                                     data_len, "(incomplete entry)");
541                                 break;
542                         }
543                         add_item_to_tree(rr_tree, offset, 2,
544                             "Number of command blocks: %u", pntohs(dptr));
545                         dptr += 2;
546                         offset += 2;
547                         data_len -= 2;
548
549                         if (data_len < 2) {
550                                 add_item_to_tree(rr_tree, offset,
551                                     data_len, "(incomplete entry)");
552                                 break;
553                         }
554                         add_item_to_tree(rr_tree, offset, 2,
555                             "Number of pending sessions: %u", pntohs(dptr));
556                         dptr += 2;
557                         offset += 2;
558                         data_len -= 2;
559
560                         if (data_len < 2) {
561                                 add_item_to_tree(rr_tree, offset,
562                                     data_len, "(incomplete entry)");
563                                 break;
564                         }
565                         add_item_to_tree(rr_tree, offset, 2,
566                             "Max number of pending sessions: %u", pntohs(dptr));
567                         dptr += 2;
568                         offset += 2;
569
570                         add_item_to_tree(rr_tree, offset, 2,
571                             "Max total sessions possible: %u", pntohs(dptr));
572                         dptr += 2;
573                         offset += 2;
574                         data_len -= 2;
575
576                         if (data_len < 2) {
577                                 add_item_to_tree(rr_tree, offset,
578                                     data_len, "(incomplete entry)");
579                                 break;
580                         }
581                         add_item_to_tree(rr_tree, offset, 2,
582                             "Session data packet size: %u", pntohs(dptr));
583                         dptr += 2;
584                         offset += 2;
585                         data_len -= 2;
586                 }
587         out:
588                 break;
589
590         default:
591                 trr = add_item_to_tree(nbns_tree, offset,
592                     (dptr - data_start) + data_len,
593                     "%s: type %s, class %s",
594                     name, type_name, class_name);
595                 rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
596                     name_len, type_name, class_name, ttl, data_len);
597                 offset += (dptr - data_start);
598                 add_item_to_tree(rr_tree, offset, data_len, "Data");
599                 break;
600         }
601         dptr += data_len;
602         
603         return dptr - data_start;
604 }
605
606 static int
607 dissect_query_records(const u_char *nbns_data_ptr, int count, const u_char *pd, 
608     int cur_off, GtkWidget *nbns_tree)
609 {
610         int start_off;
611         GtkWidget *qatree, *ti;
612         
613         qatree = gtk_tree_new();
614         start_off = cur_off;
615         
616         while (count-- > 0)
617                 cur_off += dissect_nbns_query(nbns_data_ptr, pd, cur_off, qatree);
618         ti = add_item_to_tree(GTK_WIDGET(nbns_tree), 
619                         start_off, cur_off - start_off, "Queries");
620         add_subtree(ti, qatree, ETT_NBNS_QRY);
621
622         return cur_off - start_off;
623 }
624
625
626
627 static int
628 dissect_answer_records(const u_char *nbns_data_ptr, int count,
629     const u_char *pd, int cur_off, GtkWidget *nbns_tree, int opcode, char *name)
630 {
631         int start_off;
632         GtkWidget *qatree, *ti;
633         
634         qatree = gtk_tree_new();
635         start_off = cur_off;
636
637         while (count-- > 0)
638                 cur_off += dissect_nbns_answer(nbns_data_ptr, pd, cur_off,
639                                         qatree, opcode);
640         ti = add_item_to_tree(GTK_WIDGET(nbns_tree), start_off, cur_off - start_off, name);
641         add_subtree(ti, qatree, ETT_NBNS_ANS);
642
643         return cur_off - start_off;
644 }
645
646 void
647 dissect_nbns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree)
648 {
649         GtkWidget               *nbns_tree, *ti;
650         struct nbns_header      header;
651         int                     nm_flags;
652         const u_char            *nbns_data_ptr;
653         int                     cur_off;
654
655         char *opcode[] = {
656                 "Query",
657                 "Unknown operation (1)",
658                 "Unknown operation (2)",
659                 "Unknown operation (3)",
660                 "Unknown operation (4)",
661                 "Registration",
662                 "Release",
663                 "Wait and Acknowledge",
664                 "Refresh",
665                 "Refresh(altcode)",
666                 "Unknown operation (10)",
667                 "Unknown operation (11)",
668                 "Unknown operation (12)",
669                 "Unknown operation (13)",
670                 "Unknown operation (14)",
671                 "Multi-Homed Registration",
672         };
673
674         nbns_data_ptr = &pd[offset];
675
676         /* This is taken from samba/source/nmlib.c, parse_nmb() */
677         header.name_tran_id = pntohs(&pd[offset]);
678         header.opcode = (pd[offset+2] >> 3) & 0xf;
679         header.r = (pd[offset+2] >> 7) & 1;
680
681         nm_flags = ((pd[offset+2] & 0x7) << 4) + (pd[offset+3] >> 4);
682         header.nm_flags.bcast = (nm_flags & 1) ? 1 : 0;
683         header.nm_flags.recursion_available = (nm_flags & 8) ? 1 : 0;
684         header.nm_flags.recursion_desired = (nm_flags & 0x10) ? 1 : 0;
685         header.nm_flags.trunc = (nm_flags & 0x20) ? 1 : 0;
686         header.nm_flags.authoritative = (nm_flags & 0x40) ? 1 : 0;
687
688         header.rcode = pd[offset+3] & 0xf;
689         header.qdcount = pntohs(&pd[offset+4]);
690         header.ancount = pntohs(&pd[offset+6]);
691         header.nscount = pntohs(&pd[offset+8]);
692         header.arcount = pntohs(&pd[offset+10]);
693
694         if (fd->win_info[COL_NUM]) {
695                 strcpy(fd->win_info[COL_PROTOCOL], "NBNS (UDP)");
696                 if (header.opcode <= 15) {
697                         sprintf(fd->win_info[COL_INFO], "%s %s",
698                             opcode[header.opcode], header.r ? "reply" : "request");
699                 } else {
700                         sprintf(fd->win_info[COL_INFO], "Unknown operation (%d) %s",
701                             header.opcode, header.r ? "reply" : "request");
702                 }
703         }
704
705         if (tree) {
706                 ti = add_item_to_tree(GTK_WIDGET(tree), offset, END_OF_FRAME,
707                                 "NetBIOS Name Service");
708                 nbns_tree = gtk_tree_new();
709                 add_subtree(ti, nbns_tree, ETT_NBNS);
710
711                 add_item_to_tree(nbns_tree, offset,      2, "Transaction ID: 0x%04X",
712                                 header.name_tran_id);
713                 add_item_to_tree(nbns_tree, offset +  2, 1, "Type: %s",
714                                 header.r == 0 ? "Request" : "Response" );
715                 
716                 if (header.opcode <= 15) {
717                         add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: %s (%d)",
718                                         opcode[header.opcode], header.opcode);
719                 }
720                 else {
721                         add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: Unknown (%d)",
722                                         header.opcode);
723                 }
724                 add_item_to_tree(nbns_tree, offset +  4, 2, "Questions: %d",
725                                         header.qdcount);
726                 add_item_to_tree(nbns_tree, offset +  6, 2, "Answer RRs: %d",
727                                         header.ancount);
728                 add_item_to_tree(nbns_tree, offset +  8, 2, "Authority RRs: %d",
729                                         header.nscount);
730                 add_item_to_tree(nbns_tree, offset + 10, 2, "Additional RRs: %d",
731                                         header.arcount);
732
733                 cur_off = offset + 12;
734     
735                 if (header.qdcount > 0)
736                         cur_off += dissect_query_records(nbns_data_ptr,
737                                         header.qdcount, pd, cur_off, nbns_tree);
738
739                 if (header.ancount > 0)
740                         cur_off += dissect_answer_records(nbns_data_ptr,
741                                         header.ancount, pd, cur_off, nbns_tree,
742                                         header.opcode, "Answers");
743
744                 if (header.nscount > 0)
745                         cur_off += dissect_answer_records(nbns_data_ptr,
746                                         header.nscount, pd, cur_off, nbns_tree, 
747                                         header.opcode,
748                                         "Authoritative nameservers");
749
750                 if (header.arcount > 0)
751                         cur_off += dissect_answer_records(nbns_data_ptr,
752                                         header.arcount, pd, cur_off, nbns_tree, 
753                                         header.opcode, "Additional records");
754         }
755 }