c74c1c3eba998804e43b12cbb9cba20b94b9fbfb
[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.9 1998/11/21 04:00:31 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
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 /* NetBIOS datagram packet, from RFC 1002, page 32 */
76 struct nbdgm_header {
77         guint8          msg_type;
78         struct {
79                 guint8  more;
80                 guint8  first;
81                 guint8  node_type;
82         } flags;
83         guint16         dgm_id;
84         guint32         src_ip;
85         guint16         src_port;
86
87         /* For packets with data */
88         guint16         dgm_length;
89         guint16         pkt_offset;
90
91         /* For error packets */
92         guint8          error_code;
93 };
94
95
96 static char *
97 nbns_type_name (int type)
98 {
99         switch (type) {
100         case T_NB:
101                 return "NB";
102         case T_NBSTAT:
103                 return "NBSTAT";
104         }
105         
106         return "unknown";
107 }
108
109 /* "Canonicalize" a 16-character NetBIOS name by:
110  *
111  *      removing and saving the last byte;
112  *
113  *      stripping trailing blanks;
114  *
115  *      appending the trailing byte, as a hex number, in square brackets. */
116 static char *
117 canonicalize_netbios_name(char *nbname)
118 {
119         char *pnbname;
120         u_char lastchar;
121
122         /* Get the last character of the name, as it's a special number
123          * indicating the type of the name, rather than part of the name
124          * *per se*. */
125         pnbname = nbname + 15;  /* point to the 16th character */
126         lastchar = *(unsigned char *)pnbname;
127
128         /* Now strip off any trailing blanks used to pad it to
129          * 16 bytes. */
130         while (pnbname > &nbname[0]) {
131                 if (*(pnbname - 1) != ' ')
132                         break;          /* found non-blank character */
133                 pnbname--;              /* blank - skip over it */
134         }
135
136         /* Replace the last character with its hex value, in square
137          * brackets, to make it easier to tell what it is. */
138         sprintf(pnbname, "[%02X]", lastchar);
139         pnbname += 4;
140         return pnbname;
141 }
142
143 static int
144 get_nbns_name(const u_char *nbns_data_ptr, const u_char *pd,
145     int offset, char *name_ret)
146 {
147         int name_len;
148         char name[MAXDNAME];
149         char nbname[MAXDNAME+4];        /* 4 for [<last char>] */
150         char *pname, *pnbname, cname, cnbname;
151
152         name_len = get_dns_name(nbns_data_ptr, pd, offset, name, sizeof(name));
153         
154         /* OK, now undo the first-level encoding. */
155         pname = &name[0];
156         pnbname = &nbname[0];
157         for (;;) {
158                 /* Every two characters of the first level-encoded name
159                  * turn into one character in the decoded name. */
160                 cname = *pname;
161                 if (cname == '\0')
162                         break;          /* no more characters */
163                 if (cname == '.')
164                         break;          /* scope ID follows */
165                 if (cname < 'A' || cname > 'Z') {
166                         /* Not legal. */
167                         strcpy(nbname,
168                             "Illegal NetBIOS name (character not between A and Z in first-level encoding)");
169                         goto bad;
170                 }
171                 cname -= 'A';
172                 cnbname = cname << 4;
173                 pname++;
174
175                 cname = *pname;
176                 if (cname == '\0' || cname == '.') {
177                         /* No more characters in the name - but we're in
178                          * the middle of a pair.  Not legal. */
179                         strcpy(nbname,
180                             "Illegal NetBIOS name (odd number of bytes)");
181                         goto bad;
182                 }
183                 if (cname < 'A' || cname > 'Z') {
184                         /* Not legal. */
185                         strcpy(nbname,
186                             "Illegal NetBIOS name (character not between A and Z in first-level encoding)");
187                         goto bad;
188                 }
189                 cname -= 'A';
190                 cnbname |= cname;
191                 pname++;
192
193                 /* Store the character. */
194                 *pnbname++ = cnbname;
195         }
196
197         /* NetBIOS names are supposed to be exactly 16 bytes long. */
198         if (pnbname - nbname == 16) {
199                 /* This one is; canonicalize its name. */
200                 pnbname = canonicalize_netbios_name(nbname);
201         } else {
202                 sprintf(nbname, "Illegal NetBIOS name (%ld bytes long)",
203                     (long)(pnbname - nbname));
204                 goto bad;
205         }
206         if (cname == '.') {
207                 /* We have a scope ID, starting at "pname"; append that to
208                  * the decoded host name. */
209                 strcpy(pnbname, pname);
210         } else {
211                 /* Terminate the decoded host name. */
212                 *pnbname = '\0';
213         }
214
215 bad:
216         strcpy (name_ret, nbname);
217         return name_len;
218 }
219
220
221 static int
222 get_nbns_name_type_class(const u_char *nbns_data_ptr, const u_char *pd,
223     int offset, char *name_ret, int *name_len_ret, int *type_ret,
224     int *class_ret)
225 {
226         int name_len;
227         int type;
228         int class;
229
230         name_len = get_nbns_name(nbns_data_ptr, pd, offset, name_ret);
231         offset += name_len;
232         
233         type = pntohs(&pd[offset]);
234         offset += 2;
235         class = pntohs(&pd[offset]);
236
237         *type_ret = type;
238         *class_ret = class;
239         *name_len_ret = name_len;
240
241         return name_len + 4;
242 }
243
244
245 static int
246 dissect_nbns_query(const u_char *nbns_data_ptr, const u_char *pd, int offset,
247     GtkWidget *nbns_tree)
248 {
249         int len;
250         char name[MAXDNAME];
251         int name_len;
252         int type;
253         int class;
254         char *class_name;
255         char *type_name;
256         const u_char *dptr;
257         const u_char *data_start;
258         GtkWidget *q_tree, *tq;
259
260         data_start = dptr = pd + offset;
261
262         len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name,
263             &name_len, &type, &class);
264         dptr += len;
265
266         type_name = nbns_type_name(type);
267         class_name = dns_class_name(class);
268
269         tq = add_item_to_tree(nbns_tree, offset, len, "%s: type %s, class %s", 
270             name, type_name, class_name);
271         q_tree = gtk_tree_new();
272         add_subtree(tq, q_tree, ETT_NBNS_QD);
273
274         add_item_to_tree(q_tree, offset, name_len, "Name: %s", name);
275         offset += name_len;
276
277         add_item_to_tree(q_tree, offset, 2, "Type: %s", type_name);
278         offset += 2;
279
280         add_item_to_tree(q_tree, offset, 2, "Class: %s", class_name);
281         offset += 2;
282         
283         return dptr - data_start;
284 }
285
286
287 static int
288 dissect_nbns_answer(const u_char *nbns_data_ptr, const u_char *pd, int offset,
289     GtkWidget *nbns_tree, int opcode)
290 {
291         int len;
292         char name[MAXDNAME];
293         int name_len;
294         int type;
295         int class;
296         char *class_name;
297         char *type_name;
298         const u_char *dptr;
299         const u_char *data_start;
300         u_int ttl;
301         u_short data_len;
302         u_short flags;
303         GtkWidget *rr_tree, *trr;
304
305         data_start = dptr = pd + offset;
306
307         len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name,
308             &name_len, &type, &class);
309         dptr += len;
310
311         type_name = nbns_type_name(type);
312         class_name = dns_class_name(class);
313
314         ttl = pntohl(dptr);
315         dptr += 4;
316
317         data_len = pntohs(dptr);
318         dptr += 2;
319
320         switch (type) {
321         case T_NB:              /* "NB" record */
322                 trr = add_item_to_tree(nbns_tree, offset,
323                     (dptr - data_start) + data_len,
324                     "%s: type %s, class %s",
325                     name, type_name, class_name);
326                 rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
327                     name_len, type_name, class_name, ttl, data_len);
328                 offset += (dptr - data_start);
329                 while (data_len > 0) {
330                         if (opcode == 0x7) {
331                                 /* WACK response.  This doesn't contain the
332                                  * same type of RR data as other T_NB
333                                  * responses.  */
334                                 if (data_len < 2) {
335                                         add_item_to_tree(rr_tree, offset,
336                                             data_len, "(incomplete entry)");
337                                         break;
338                                 }
339                                 flags = pntohs(dptr);
340                                 dptr += 2;
341                                 add_item_to_tree(rr_tree, offset, 2,
342                                     "Flags: 0x%x", flags);
343                                 offset += 2;
344                                 data_len -= 2;
345                         } else {
346                                 if (data_len < 2) {
347                                         add_item_to_tree(rr_tree, offset,
348                                             data_len, "(incomplete entry)");
349                                         break;
350                                 }
351                                 flags = pntohs(dptr);
352                                 dptr += 2;
353                                 add_item_to_tree(rr_tree, offset, 2,
354                                     "Flags: 0x%x", flags);
355                                 offset += 2;
356                                 data_len -= 2;
357
358                                 if (data_len < 4) {
359                                         add_item_to_tree(rr_tree, offset,
360                                             data_len, "(incomplete entry)");
361                                         break;
362                                 }
363                                 add_item_to_tree(rr_tree, offset, 4,
364                                     "Addr: %s",
365                                     ip_to_str((guint8 *)dptr));
366                                 dptr += 4;
367                                 offset += 4;
368                                 data_len -= 4;
369                         }
370                 }
371                 break;
372
373         case T_NBSTAT:  /* "NBSTAT" record */
374                 {
375                         u_int num_names;
376                         char nbname[16+4+1];    /* 4 for [<last char>] */
377                         u_short name_flags;
378                         
379                         trr = add_item_to_tree(nbns_tree, offset,
380                             (dptr - data_start) + data_len,
381                             "%s: type %s, class %s",
382                             name, type_name, class_name);
383                         rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
384                             name_len, type_name, class_name, ttl, data_len);
385                         offset += (dptr - data_start);
386                         if (data_len < 1) {
387                                 add_item_to_tree(rr_tree, offset,
388                                     data_len, "(incomplete entry)");
389                                 break;
390                         }
391                         num_names = *dptr;
392                         dptr += 1;
393                         add_item_to_tree(rr_tree, offset, 2,
394                             "Number of names: %u", num_names);
395                         offset += 1;
396
397                         while (num_names != 0) {
398                                 if (data_len < 16) {
399                                         add_item_to_tree(rr_tree, offset,
400                                             data_len, "(incomplete entry)");
401                                         goto out;
402                                 }
403                                 memcpy(nbname, dptr, 16);
404                                 dptr += 16;
405                                 canonicalize_netbios_name(nbname);
406                                 add_item_to_tree(rr_tree, offset, 16,
407                                     "Name: %s", nbname);
408                                 offset += 16;
409                                 data_len -= 16;
410
411                                 if (data_len < 2) {
412                                         add_item_to_tree(rr_tree, offset,
413                                             data_len, "(incomplete entry)");
414                                         goto out;
415                                 }
416                                 name_flags = pntohs(dptr);
417                                 dptr += 2;
418                                 add_item_to_tree(rr_tree, offset, 2,
419                                     "Name flags: 0x%x", name_flags);
420                                 offset += 2;
421                                 data_len -= 2;
422
423                                 num_names--;
424                         }
425
426                         if (data_len < 6) {
427                                 add_item_to_tree(rr_tree, offset,
428                                     data_len, "(incomplete entry)");
429                                 break;
430                         }
431                         add_item_to_tree(rr_tree, offset, 6,
432                             "Unit ID: %s",
433                             ether_to_str((guint8 *)dptr));
434                         dptr += 6;
435                         offset += 6;
436                         data_len -= 6;
437
438                         if (data_len < 1) {
439                                 add_item_to_tree(rr_tree, offset,
440                                     data_len, "(incomplete entry)");
441                                 break;
442                         }
443                         add_item_to_tree(rr_tree, offset, 1,
444                             "Jumpers: 0x%x", *dptr);
445                         dptr += 1;
446                         offset += 1;
447                         data_len -= 1;
448
449                         if (data_len < 1) {
450                                 add_item_to_tree(rr_tree, offset,
451                                     data_len, "(incomplete entry)");
452                                 break;
453                         }
454                         add_item_to_tree(rr_tree, offset, 1,
455                             "Test result: 0x%x", *dptr);
456                         dptr += 1;
457                         offset += 1;
458                         data_len -= 1;
459
460                         if (data_len < 2) {
461                                 add_item_to_tree(rr_tree, offset,
462                                     data_len, "(incomplete entry)");
463                                 break;
464                         }
465                         add_item_to_tree(rr_tree, offset, 2,
466                             "Version number: 0x%x", pntohs(dptr));
467                         dptr += 2;
468                         offset += 2;
469                         data_len -= 2;
470
471                         if (data_len < 2) {
472                                 add_item_to_tree(rr_tree, offset,
473                                     data_len, "(incomplete entry)");
474                                 break;
475                         }
476                         add_item_to_tree(rr_tree, offset, 2,
477                             "Period of statistics: 0x%x", pntohs(dptr));
478                         dptr += 2;
479                         offset += 2;
480                         data_len -= 2;
481
482                         if (data_len < 2) {
483                                 add_item_to_tree(rr_tree, offset,
484                                     data_len, "(incomplete entry)");
485                                 break;
486                         }
487                         add_item_to_tree(rr_tree, offset, 2,
488                             "Number of CRCs: %u", pntohs(dptr));
489                         dptr += 2;
490                         offset += 2;
491                         data_len -= 2;
492
493                         if (data_len < 2) {
494                                 add_item_to_tree(rr_tree, offset,
495                                     data_len, "(incomplete entry)");
496                                 break;
497                         }
498                         add_item_to_tree(rr_tree, offset, 2,
499                             "Number of alignment errors: %u", pntohs(dptr));
500                         dptr += 2;
501                         offset += 2;
502                         data_len -= 2;
503
504                         if (data_len < 2) {
505                                 add_item_to_tree(rr_tree, offset,
506                                     data_len, "(incomplete entry)");
507                                 break;
508                         }
509                         add_item_to_tree(rr_tree, offset, 2,
510                             "Number of collisions: %u", pntohs(dptr));
511                         dptr += 2;
512                         offset += 2;
513                         data_len -= 2;
514
515                         if (data_len < 2) {
516                                 add_item_to_tree(rr_tree, offset,
517                                     data_len, "(incomplete entry)");
518                                 break;
519                         }
520                         add_item_to_tree(rr_tree, offset, 2,
521                             "Number of send aborts: %u", pntohs(dptr));
522                         dptr += 2;
523                         offset += 2;
524                         data_len -= 2;
525
526                         if (data_len < 4) {
527                                 add_item_to_tree(rr_tree, offset,
528                                     data_len, "(incomplete entry)");
529                                 break;
530                         }
531                         add_item_to_tree(rr_tree, offset, 4,
532                             "Number of good sends: %u", pntohl(dptr));
533                         dptr += 4;
534                         offset += 4;
535                         data_len -= 4;
536
537                         if (data_len < 4) {
538                                 add_item_to_tree(rr_tree, offset,
539                                     data_len, "(incomplete entry)");
540                                 break;
541                         }
542                         add_item_to_tree(rr_tree, offset, 4,
543                             "Number of good receives: %u", pntohl(dptr));
544                         dptr += 4;
545                         offset += 4;
546                         data_len -= 4;
547
548                         if (data_len < 2) {
549                                 add_item_to_tree(rr_tree, offset,
550                                     data_len, "(incomplete entry)");
551                                 break;
552                         }
553                         add_item_to_tree(rr_tree, offset, 2,
554                             "Number of retransmits: %u", pntohs(dptr));
555                         dptr += 2;
556                         offset += 2;
557                         data_len -= 2;
558
559                         if (data_len < 2) {
560                                 add_item_to_tree(rr_tree, offset,
561                                     data_len, "(incomplete entry)");
562                                 break;
563                         }
564                         add_item_to_tree(rr_tree, offset, 2,
565                             "Number of no resource conditions: %u", pntohs(dptr));
566                         dptr += 2;
567                         offset += 2;
568                         data_len -= 2;
569
570                         if (data_len < 2) {
571                                 add_item_to_tree(rr_tree, offset,
572                                     data_len, "(incomplete entry)");
573                                 break;
574                         }
575                         add_item_to_tree(rr_tree, offset, 2,
576                             "Number of command blocks: %u", pntohs(dptr));
577                         dptr += 2;
578                         offset += 2;
579                         data_len -= 2;
580
581                         if (data_len < 2) {
582                                 add_item_to_tree(rr_tree, offset,
583                                     data_len, "(incomplete entry)");
584                                 break;
585                         }
586                         add_item_to_tree(rr_tree, offset, 2,
587                             "Number of pending sessions: %u", pntohs(dptr));
588                         dptr += 2;
589                         offset += 2;
590                         data_len -= 2;
591
592                         if (data_len < 2) {
593                                 add_item_to_tree(rr_tree, offset,
594                                     data_len, "(incomplete entry)");
595                                 break;
596                         }
597                         add_item_to_tree(rr_tree, offset, 2,
598                             "Max number of pending sessions: %u", pntohs(dptr));
599                         dptr += 2;
600                         offset += 2;
601
602                         add_item_to_tree(rr_tree, offset, 2,
603                             "Max total sessions possible: %u", pntohs(dptr));
604                         dptr += 2;
605                         offset += 2;
606                         data_len -= 2;
607
608                         if (data_len < 2) {
609                                 add_item_to_tree(rr_tree, offset,
610                                     data_len, "(incomplete entry)");
611                                 break;
612                         }
613                         add_item_to_tree(rr_tree, offset, 2,
614                             "Session data packet size: %u", pntohs(dptr));
615                         dptr += 2;
616                         offset += 2;
617                         data_len -= 2;
618                 }
619         out:
620                 break;
621
622         default:
623                 trr = add_item_to_tree(nbns_tree, offset,
624                     (dptr - data_start) + data_len,
625                     "%s: type %s, class %s",
626                     name, type_name, class_name);
627                 rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
628                     name_len, type_name, class_name, ttl, data_len);
629                 offset += (dptr - data_start);
630                 add_item_to_tree(rr_tree, offset, data_len, "Data");
631                 break;
632         }
633         dptr += data_len;
634         
635         return dptr - data_start;
636 }
637
638 static int
639 dissect_query_records(const u_char *nbns_data_ptr, int count, const u_char *pd, 
640     int cur_off, GtkWidget *nbns_tree)
641 {
642         int start_off;
643         GtkWidget *qatree, *ti;
644         
645         qatree = gtk_tree_new();
646         start_off = cur_off;
647         
648         while (count-- > 0)
649                 cur_off += dissect_nbns_query(nbns_data_ptr, pd, cur_off, qatree);
650         ti = add_item_to_tree(GTK_WIDGET(nbns_tree), 
651                         start_off, cur_off - start_off, "Queries");
652         add_subtree(ti, qatree, ETT_NBNS_QRY);
653
654         return cur_off - start_off;
655 }
656
657
658
659 static int
660 dissect_answer_records(const u_char *nbns_data_ptr, int count,
661     const u_char *pd, int cur_off, GtkWidget *nbns_tree, int opcode, char *name)
662 {
663         int start_off;
664         GtkWidget *qatree, *ti;
665         
666         qatree = gtk_tree_new();
667         start_off = cur_off;
668
669         while (count-- > 0)
670                 cur_off += dissect_nbns_answer(nbns_data_ptr, pd, cur_off,
671                                         qatree, opcode);
672         ti = add_item_to_tree(GTK_WIDGET(nbns_tree), start_off, cur_off - start_off, name);
673         add_subtree(ti, qatree, ETT_NBNS_ANS);
674
675         return cur_off - start_off;
676 }
677
678 void
679 dissect_nbns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree)
680 {
681         GtkWidget               *nbns_tree, *ti;
682         struct nbns_header      header;
683         int                     nm_flags;
684         const u_char            *nbns_data_ptr;
685         int                     cur_off;
686
687         char *opcode[] = {
688                 "Query",
689                 "Unknown operation (1)",
690                 "Unknown operation (2)",
691                 "Unknown operation (3)",
692                 "Unknown operation (4)",
693                 "Registration",
694                 "Release",
695                 "Wait and Acknowledge",
696                 "Refresh",
697                 "Refresh(altcode)",
698                 "Unknown operation (10)",
699                 "Unknown operation (11)",
700                 "Unknown operation (12)",
701                 "Unknown operation (13)",
702                 "Unknown operation (14)",
703                 "Multi-Homed Registration",
704         };
705
706         nbns_data_ptr = &pd[offset];
707
708         /* This is taken from samba/source/nmlib.c, parse_nmb() */
709         header.name_tran_id = pntohs(&pd[offset]);
710         header.opcode = (pd[offset+2] >> 3) & 0xf;
711         header.r = (pd[offset+2] >> 7) & 1;
712
713         nm_flags = ((pd[offset+2] & 0x7) << 4) + (pd[offset+3] >> 4);
714         header.nm_flags.bcast = (nm_flags & 1) ? 1 : 0;
715         header.nm_flags.recursion_available = (nm_flags & 8) ? 1 : 0;
716         header.nm_flags.recursion_desired = (nm_flags & 0x10) ? 1 : 0;
717         header.nm_flags.trunc = (nm_flags & 0x20) ? 1 : 0;
718         header.nm_flags.authoritative = (nm_flags & 0x40) ? 1 : 0;
719
720         header.rcode = pd[offset+3] & 0xf;
721         header.qdcount = pntohs(&pd[offset+4]);
722         header.ancount = pntohs(&pd[offset+6]);
723         header.nscount = pntohs(&pd[offset+8]);
724         header.arcount = pntohs(&pd[offset+10]);
725
726         if (check_col(fd, COL_PROTOCOL))
727                 col_add_str(fd, COL_PROTOCOL, "NBNS (UDP)");
728         if (check_col(fd, COL_INFO)) {
729                 if (header.opcode <= 15) {
730                         col_add_fstr(fd, COL_INFO, "%s %s",
731                             opcode[header.opcode], header.r ? "reply" : "request");
732                 } else {
733                         col_add_fstr(fd, COL_INFO, "Unknown operation (%d) %s",
734                             header.opcode, header.r ? "reply" : "request");
735                 }
736         }
737
738         if (tree) {
739                 ti = add_item_to_tree(GTK_WIDGET(tree), offset, END_OF_FRAME,
740                                 "NetBIOS Name Service");
741                 nbns_tree = gtk_tree_new();
742                 add_subtree(ti, nbns_tree, ETT_NBNS);
743
744                 add_item_to_tree(nbns_tree, offset,      2, "Transaction ID: 0x%04X",
745                                 header.name_tran_id);
746                 add_item_to_tree(nbns_tree, offset +  2, 1, "Type: %s",
747                                 header.r == 0 ? "Request" : "Response" );
748                 
749                 if (header.opcode <= 15) {
750                         add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: %s (%d)",
751                                         opcode[header.opcode], header.opcode);
752                 }
753                 else {
754                         add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: Unknown (%d)",
755                                         header.opcode);
756                 }
757                 add_item_to_tree(nbns_tree, offset +  4, 2, "Questions: %d",
758                                         header.qdcount);
759                 add_item_to_tree(nbns_tree, offset +  6, 2, "Answer RRs: %d",
760                                         header.ancount);
761                 add_item_to_tree(nbns_tree, offset +  8, 2, "Authority RRs: %d",
762                                         header.nscount);
763                 add_item_to_tree(nbns_tree, offset + 10, 2, "Additional RRs: %d",
764                                         header.arcount);
765
766                 cur_off = offset + 12;
767     
768                 if (header.qdcount > 0)
769                         cur_off += dissect_query_records(nbns_data_ptr,
770                                         header.qdcount, pd, cur_off, nbns_tree);
771
772                 if (header.ancount > 0)
773                         cur_off += dissect_answer_records(nbns_data_ptr,
774                                         header.ancount, pd, cur_off, nbns_tree,
775                                         header.opcode, "Answers");
776
777                 if (header.nscount > 0)
778                         cur_off += dissect_answer_records(nbns_data_ptr,
779                                         header.nscount, pd, cur_off, nbns_tree, 
780                                         header.opcode,
781                                         "Authoritative nameservers");
782
783                 if (header.arcount > 0)
784                         cur_off += dissect_answer_records(nbns_data_ptr,
785                                         header.arcount, pd, cur_off, nbns_tree, 
786                                         header.opcode, "Additional records");
787         }
788 }
789
790
791 void
792 dissect_nbdgm(const u_char *pd, int offset, frame_data *fd, GtkTree *tree)
793 {
794         GtkWidget               *nbdgm_tree, *ti;
795         struct nbdgm_header     header;
796         int                     flags;
797         int                     message_index;
798
799         char *message[] = {
800                 "Unknown",
801                 "Direct_unique datagram",
802                 "Direct_group datagram",
803                 "Broadcast datagram",
804                 "Datagram error",
805                 "Datagram query request",
806                 "Datagram positive query response",
807                 "Datagram negative query response"
808         };
809
810         char *node[] = {
811                 "B node",
812                 "P node",
813                 "M node",
814                 "NBDD"
815         };
816
817         static value_string error_codes[] = {
818                 { 0x82, "Destination name not present" },
819                 { 0x83, "Invalid source name format" },
820                 { 0x84, "Invalid destination name format" },
821                 { 0x00, NULL }
822         };
823
824         char *yesno[] = { "No", "Yes" };
825
826         char name[32];
827         int len;
828
829         header.msg_type = pd[offset];
830         
831         flags = pd[offset+1];
832         header.flags.more = flags & 1;
833         header.flags.first = (flags & 2) >> 1;
834         header.flags.node_type = (flags & 12) >> 2;
835
836         header.dgm_id = pntohs(&pd[offset+2]);
837         memcpy(&header.src_ip, &pd[offset+4], 4);
838         header.src_port = pntohs(&pd[offset+8]);
839
840         if (header.msg_type == 0x10 ||
841                         header.msg_type == 0x11 || header.msg_type == 0x12) {
842                 header.dgm_length = pntohs(&pd[offset+10]);
843                 header.pkt_offset = pntohs(&pd[offset+12]);
844         }
845         else if (header.msg_type == 0x13) {
846                 header.error_code = pntohs(&pd[offset+10]);
847         }
848
849         message_index = header.msg_type - 0x0f;
850         if (message_index < 1 || message_index > 8) {
851                 message_index = 0;
852         }
853
854         if (check_col(fd, COL_PROTOCOL))
855                 col_add_str(fd, COL_PROTOCOL, "NBDS (UDP)");
856         if (check_col(fd, COL_INFO)) {
857                 col_add_fstr(fd, COL_INFO, "%s", message[message_index]);
858         }
859
860         if (tree) {
861                 ti = add_item_to_tree(GTK_WIDGET(tree), offset, header.dgm_length,
862                                 "NetBIOS Datagram Service");
863                 nbdgm_tree = gtk_tree_new();
864                 add_subtree(ti, nbdgm_tree, ETT_NBDGM);
865
866                 add_item_to_tree(nbdgm_tree, offset, 1, "Message Type: %s",
867                                 message[message_index]);
868                 add_item_to_tree(nbdgm_tree, offset+1, 1, "More fragments follow: %s",
869                                 yesno[header.flags.more]);
870                 add_item_to_tree(nbdgm_tree, offset+1, 1, "This is first fragment: %s",
871                                 yesno[header.flags.first]);
872                 add_item_to_tree(nbdgm_tree, offset+1, 1, "Node Type: %s",
873                                 node[header.flags.node_type]);
874
875                 add_item_to_tree(nbdgm_tree, offset+2, 2, "Datagram ID: 0x%04X",
876                                 header.dgm_id);
877                 add_item_to_tree(nbdgm_tree, offset+4, 4, "Source IP: %s",
878                                 ip_to_str((guint8 *)&header.src_ip));
879                 add_item_to_tree(nbdgm_tree, offset+8, 2, "Source Port: %d",
880                                 header.src_port);
881
882                 offset += 10;
883
884                 if (header.msg_type == 0x10 ||
885                                 header.msg_type == 0x11 || header.msg_type == 0x12) {
886
887                         add_item_to_tree(nbdgm_tree, offset, 2,
888                                         "Datagram length: %d bytes", header.dgm_length);
889                         add_item_to_tree(nbdgm_tree, offset+2, 2,
890                                         "Packet offset: %d bytes", header.pkt_offset);
891
892                         offset += 4;
893
894                         /* Source name */
895                         len = get_nbns_name(&pd[offset], pd, offset, name);
896
897                         add_item_to_tree(nbdgm_tree, offset, len, "Source name: %s",
898                                         name);
899                         offset += len;
900
901                         /* Destination name */
902                         len = get_nbns_name(&pd[offset], pd, offset, name);
903
904                         add_item_to_tree(nbdgm_tree, offset, len, "Destination name: %s",
905                                         name);
906                         offset += len;
907
908                         /* here we can pass the packet off to the next protocol */
909                         dissect_data(pd, offset, fd, GTK_TREE(nbdgm_tree));
910                 }
911                 else if (header.msg_type == 0x13) {
912                         add_item_to_tree(nbdgm_tree, offset, 1, "Error code: %s",
913                                 val_to_str(header.error_code, error_codes, "Unknown (0x%x)"));
914                 }
915                 else if (header.msg_type == 0x14 ||
916                                 header.msg_type == 0x15 || header.msg_type == 0x16) {
917                         /* Destination name */
918                         len = get_nbns_name(&pd[offset], pd, offset, name);
919
920                         add_item_to_tree(nbdgm_tree, offset, len, "Destination name: %s",
921                                         name);
922                 }
923         }
924 }