Decode the word containing the opcode, flags, reply code, etc. in DNS
[obnox/wireshark/wip.git] / packet-nbns.c
index 86f24f412deb3e49df39a23647bcc6e1dddc910a..e6ca278528cbf002cf832d87b7aadc2447a82c59 100644 (file)
@@ -3,7 +3,7 @@
  * Gilbert Ramirez <gram@verdict.uthscsa.edu>
  * Much stuff added by Guy Harris <guy@netapp.com>
  *
- * $Id: packet-nbns.c,v 1.6 1998/11/12 00:06:32 gram Exp $
+ * $Id: packet-nbns.c,v 1.11 1999/01/04 09:13:46 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
@@ -32,6 +32,7 @@
 #include <gtk/gtk.h>
 
 #include <stdio.h>
+#include <string.h>
 #include <memory.h>
 
 #ifdef HAVE_SYS_TYPES_H
 #include "ethereal.h"
 #include "packet.h"
 #include "packet-dns.h"
+#include "util.h"
 
 /* Packet structure taken from RFC 1002. See also RFC 1001.
- * The Samba source code, specifically nmblib.c, also helps a lot. */
+ * Opcode, flags, and rcode treated as "flags", similarly to DNS,
+ * to make it easier to lift the dissection code from "packet-dns.c". */
 
-struct nbns_header {
+/* Offsets of fields in the NBNS header. */
+#define        NBNS_ID         0
+#define        NBNS_FLAGS      2
+#define        NBNS_QUEST      4
+#define        NBNS_ANS        6
+#define        NBNS_AUTH       8
+#define        NBNS_ADD        10
 
-       guint16         name_tran_id;
-       guint8          r;
-       guint8          opcode;
-       struct {
-               guint8  bcast;
-               guint8  recursion_available;
-               guint8  recursion_desired;
-               guint8  trunc;
-               guint8  authoritative;
-       } nm_flags;
-       guint8          rcode;
-       guint16         qdcount;
-       guint16         ancount;
-       guint16         nscount;
-       guint16         arcount;
-};
+/* Length of NBNS header. */
+#define        NBNS_HDRLEN     12
 
 /* type values  */
 #define T_NB            32              /* NetBIOS name service RR */
 #define T_NBSTAT        33              /* NetBIOS node status RR */
 
+/* NetBIOS datagram packet, from RFC 1002, page 32 */
+struct nbdgm_header {
+       guint8          msg_type;
+       struct {
+               guint8  more;
+               guint8  first;
+               guint8  node_type;
+       } flags;
+       guint16         dgm_id;
+       guint32         src_ip;
+       guint16         src_port;
+
+       /* For packets with data */
+       guint16         dgm_length;
+       guint16         pkt_offset;
+
+       /* For error packets */
+       guint8          error_code;
+};
+
+/* Bit fields in the flags */
+#define F_RESPONSE      (1<<15)         /* packet is response */
+#define F_OPCODE        (0xF<<11)       /* query opcode */
+#define F_AUTHORITATIVE (1<<10)         /* response is authoritative */
+#define F_TRUNCATED     (1<<9)          /* response is truncated */
+#define F_RECDESIRED    (1<<8)          /* recursion desired */
+#define F_RECAVAIL      (1<<7)          /* recursion available */
+#define F_BROADCAST     (1<<4)          /* broadcast/multicast packet */
+#define F_RCODE         (0xF<<0)        /* reply code */
+
+/* Opcodes */
+#define OPCODE_QUERY          (0<<11)    /* standard query */
+#define OPCODE_REGISTRATION   (5<<11)    /* registration */
+#define OPCODE_RELEASE        (6<<11)    /* release name */
+#define OPCODE_WACK           (7<<11)    /* wait for acknowledgement */
+#define OPCODE_REFRESH        (8<<11)    /* refresh registration */
+#define OPCODE_REFRESHALT     (9<<11)    /* refresh registration (alternate opcode) */
+#define OPCODE_MHREGISTRATION (15<<11)   /* multi-homed registration */
+
+/* Reply codes */
+#define RCODE_NOERROR   (0<<0)
+#define RCODE_FMTERROR  (1<<0)
+#define RCODE_SERVFAIL  (2<<0)
+#define RCODE_NAMEERROR (3<<0)
+#define RCODE_NOTIMPL   (4<<0)
+#define RCODE_REFUSED   (5<<0)
+#define RCODE_ACTIVE    (6<<0)
+#define RCODE_CONFLICT  (7<<0)
+
 
 static char *
 nbns_type_name (int type)
@@ -121,29 +165,16 @@ canonicalize_netbios_name(char *nbname)
 }
 
 static int
-get_nbns_name_type_class(const u_char *nbns_data_ptr, const u_char *pd,
-    int offset, char *name_ret, int *name_len_ret, int *type_ret,
-    int *class_ret)
+get_nbns_name(const u_char *nbns_data_ptr, const u_char *pd,
+    int offset, char *name_ret)
 {
-       int len;
        int name_len;
-       int type;
-       int class;
        char name[MAXDNAME];
        char nbname[MAXDNAME+4];        /* 4 for [<last char>] */
        char *pname, *pnbname, cname, cnbname;
-       const u_char *pd_save;
 
        name_len = get_dns_name(nbns_data_ptr, pd, offset, name, sizeof(name));
-       pd += offset;
-       pd_save = pd;
-       pd += name_len;
        
-       type = pntohs(pd);
-       pd += 2;
-       class = pntohs(pd);
-       pd += 2;
-
        /* OK, now undo the first-level encoding. */
        pname = &name[0];
        pnbname = &nbname[0];
@@ -207,12 +238,31 @@ get_nbns_name_type_class(const u_char *nbns_data_ptr, const u_char *pd,
 
 bad:
        strcpy (name_ret, nbname);
+       return name_len;
+}
+
+
+static int
+get_nbns_name_type_class(const u_char *nbns_data_ptr, const u_char *pd,
+    int offset, char *name_ret, int *name_len_ret, int *type_ret,
+    int *class_ret)
+{
+       int name_len;
+       int type;
+       int class;
+
+       name_len = get_nbns_name(nbns_data_ptr, pd, offset, name_ret);
+       offset += name_len;
+       
+       type = pntohs(&pd[offset]);
+       offset += 2;
+       class = pntohs(&pd[offset]);
+
        *type_ret = type;
        *class_ret = class;
        *name_len_ret = name_len;
 
-       len = pd - pd_save;
-       return len;
+       return name_len + 4;
 }
 
 
@@ -301,7 +351,7 @@ dissect_nbns_answer(const u_char *nbns_data_ptr, const u_char *pd, int offset,
                    name_len, type_name, class_name, ttl, data_len);
                offset += (dptr - data_start);
                while (data_len > 0) {
-                       if (opcode == 0x7) {
+                       if (opcode == OPCODE_WACK) {
                                /* WACK response.  This doesn't contain the
                                 * same type of RR data as other T_NB
                                 * responses.  */
@@ -616,14 +666,14 @@ dissect_query_records(const u_char *nbns_data_ptr, int count, const u_char *pd,
        int start_off;
        GtkWidget *qatree, *ti;
        
-       qatree = gtk_tree_new();
        start_off = cur_off;
-       
-       while (count-- > 0)
-               cur_off += dissect_nbns_query(nbns_data_ptr, pd, cur_off, qatree);
        ti = add_item_to_tree(GTK_WIDGET(nbns_tree), 
-                       start_off, cur_off - start_off, "Queries");
+                       start_off, 0, "Queries");
+       qatree = gtk_tree_new();
        add_subtree(ti, qatree, ETT_NBNS_QRY);
+       while (count-- > 0)
+               cur_off += dissect_nbns_query(nbns_data_ptr, pd, cur_off, qatree);
+       set_item_len(ti, cur_off - start_off);
 
        return cur_off - start_off;
 }
@@ -637,75 +687,65 @@ dissect_answer_records(const u_char *nbns_data_ptr, int count,
        int start_off;
        GtkWidget *qatree, *ti;
        
-       qatree = gtk_tree_new();
        start_off = cur_off;
-
+       ti = add_item_to_tree(GTK_WIDGET(nbns_tree),
+                       start_off, 0, name);
+       qatree = gtk_tree_new();
+       add_subtree(ti, qatree, ETT_NBNS_ANS);
        while (count-- > 0)
                cur_off += dissect_nbns_answer(nbns_data_ptr, pd, cur_off,
                                        qatree, opcode);
-       ti = add_item_to_tree(GTK_WIDGET(nbns_tree), start_off, cur_off - start_off, name);
-       add_subtree(ti, qatree, ETT_NBNS_ANS);
-
+       set_item_len(ti, cur_off - start_off);
        return cur_off - start_off;
 }
 
 void
 dissect_nbns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree)
 {
-       GtkWidget               *nbns_tree, *ti;
-       struct nbns_header      header;
-       int                     nm_flags;
        const u_char            *nbns_data_ptr;
+       GtkWidget               *nbns_tree, *ti, *field_tree, *tf;
+       guint16                 id, flags, quest, ans, auth, add;
+       char                    buf[128+1];
        int                     cur_off;
-
-       char *opcode[] = {
-               "Query",
-               "Unknown operation (1)",
-               "Unknown operation (2)",
-               "Unknown operation (3)",
-               "Unknown operation (4)",
-               "Registration",
-               "Release",
-               "Wait and Acknowledge",
-               "Refresh",
-               "Refresh(altcode)",
-               "Unknown operation (10)",
-               "Unknown operation (11)",
-               "Unknown operation (12)",
-               "Unknown operation (13)",
-               "Unknown operation (14)",
-               "Multi-Homed Registration",
+       static const value_string opcode_vals[] = {
+                 { OPCODE_QUERY,          "Name query"                 },
+                 { OPCODE_REGISTRATION,   "Registration"               },
+                 { OPCODE_RELEASE,        "Release"                    },
+                 { OPCODE_WACK,           "Wait for acknowledgment"    },
+                 { OPCODE_REFRESH,        "Refresh"                    },
+                 { OPCODE_REFRESHALT,     "Refresh (alternate opcode)" },
+                 { OPCODE_MHREGISTRATION, "Multi-homed registration"   },
+                 { 0,                     NULL                         }
+       };
+       static const value_string rcode_vals[] = {
+                 { RCODE_NOERROR,   "No error"              },
+                 { RCODE_FMTERROR,  "Format error"          },
+                 { RCODE_SERVFAIL,  "Server failure"        },
+                 { RCODE_NAMEERROR, "Name error"            },
+                 { RCODE_NOTIMPL,   "Not implemented"       },
+                 { RCODE_REFUSED,   "Refused"               },
+                 { RCODE_ACTIVE,    "Name is active"        },
+                 { RCODE_CONFLICT,  "Name is in conflict"   },
+                 { 0,               NULL                    }
        };
 
        nbns_data_ptr = &pd[offset];
 
-       /* This is taken from samba/source/nmlib.c, parse_nmb() */
-       header.name_tran_id = pntohs(&pd[offset]);
-       header.opcode = (pd[offset+2] >> 3) & 0xf;
-       header.r = (pd[offset+2] >> 7) & 1;
-
-       nm_flags = ((pd[offset+2] & 0x7) << 4) + (pd[offset+3] >> 4);
-       header.nm_flags.bcast = (nm_flags & 1) ? 1 : 0;
-       header.nm_flags.recursion_available = (nm_flags & 8) ? 1 : 0;
-       header.nm_flags.recursion_desired = (nm_flags & 0x10) ? 1 : 0;
-       header.nm_flags.trunc = (nm_flags & 0x20) ? 1 : 0;
-       header.nm_flags.authoritative = (nm_flags & 0x40) ? 1 : 0;
-
-       header.rcode = pd[offset+3] & 0xf;
-       header.qdcount = pntohs(&pd[offset+4]);
-       header.ancount = pntohs(&pd[offset+6]);
-       header.nscount = pntohs(&pd[offset+8]);
-       header.arcount = pntohs(&pd[offset+10]);
-
-       if (fd->win_info[COL_NUM]) {
-               strcpy(fd->win_info[COL_PROTOCOL], "NBNS (UDP)");
-               if (header.opcode <= 15) {
-                       sprintf(fd->win_info[COL_INFO], "%s %s",
-                           opcode[header.opcode], header.r ? "reply" : "request");
-               } else {
-                       sprintf(fd->win_info[COL_INFO], "Unknown operation (%d) %s",
-                           header.opcode, header.r ? "reply" : "request");
-               }
+       /* To do: check for runts, errs, etc. */
+       id    = pntohs(&pd[offset + NBNS_ID]);
+       flags = pntohs(&pd[offset + NBNS_FLAGS]);
+       quest = pntohs(&pd[offset + NBNS_QUEST]);
+       ans   = pntohs(&pd[offset + NBNS_ANS]);
+       auth  = pntohs(&pd[offset + NBNS_AUTH]);
+       add   = pntohs(&pd[offset + NBNS_ADD]);
+
+       if (check_col(fd, COL_PROTOCOL))
+               col_add_str(fd, COL_PROTOCOL, "NBNS (UDP)");
+       if (check_col(fd, COL_INFO)) {
+               col_add_fstr(fd, COL_INFO, "%s%s",
+                   val_to_str(flags & F_OPCODE, opcode_vals,
+                     "Unknown operation (%x)"),
+                   (flags & F_RESPONSE) ? " response" : "");
        }
 
        if (tree) {
@@ -714,48 +754,236 @@ dissect_nbns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree)
                nbns_tree = gtk_tree_new();
                add_subtree(ti, nbns_tree, ETT_NBNS);
 
-               add_item_to_tree(nbns_tree, offset,      2, "Transaction ID: 0x%04X",
-                               header.name_tran_id);
-               add_item_to_tree(nbns_tree, offset +  2, 1, "Type: %s",
-                               header.r == 0 ? "Request" : "Response" );
-               
-               if (header.opcode <= 15) {
-                       add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: %s (%d)",
-                                       opcode[header.opcode], header.opcode);
+               add_item_to_tree(nbns_tree, offset + NBNS_ID, 2,
+                               "Transaction ID: 0x%04X", id);
+
+               strcpy(buf, val_to_str(flags & F_OPCODE, opcode_vals,
+                               "Unknown (%x)"));
+               if (flags & F_RESPONSE) {
+                       strcat(buf, " response");
+                       strcat(buf, ", ");
+                       strcat(buf, val_to_str(flags & F_RCODE, rcode_vals,
+                           "Unknown error (%x)"));
+               }
+               tf = add_item_to_tree(nbns_tree, offset + NBNS_FLAGS, 2,
+                               "Flags: 0x%04x (%s)", flags, buf);
+               field_tree = gtk_tree_new();
+               add_subtree(tf, field_tree, ETT_NBNS_FLAGS);
+               add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2, "%s",
+                   decode_boolean_bitfield(flags, F_RESPONSE,
+                     2*8, "Response", "Query"));
+               add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2, "%s",
+                   decode_enumerated_bitfield(flags, F_OPCODE,
+                     2*8, opcode_vals, "%s"));
+               if (flags & F_RESPONSE) {
+                       add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2,
+                               "%s",
+                               decode_boolean_bitfield(flags, F_AUTHORITATIVE,
+                                       2*8,
+                                       "Server is an authority for domain",
+                                       "Server isn't an authority for domain"));
+               }
+               add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2, "%s",
+                               decode_boolean_bitfield(flags, F_TRUNCATED,
+                                       2*8,
+                                       "Message is truncated",
+                                       "Message is not truncated"));
+               add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2, "%s",
+                               decode_boolean_bitfield(flags, F_RECDESIRED,
+                                       2*8,
+                                       "Do query recursively",
+                                       "Don't do query recursively"));
+               if (flags & F_RESPONSE) {
+                       add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2,
+                               "%s",
+                               decode_boolean_bitfield(flags, F_RECAVAIL,
+                                       2*8,
+                                       "Server can do recursive queries",
+                                       "Server can't do recursive queries"));
                }
-               else {
-                       add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: Unknown (%d)",
-                                       header.opcode);
+               add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2, "%s",
+                               decode_boolean_bitfield(flags, F_BROADCAST,
+                                       2*8,
+                                       "Broadcast packet",
+                                       "Not a broadcast packet"));
+               if (flags & F_RESPONSE) {
+                       add_item_to_tree(field_tree, offset + NBNS_FLAGS, 2,
+                               "%s",
+                               decode_enumerated_bitfield(flags, F_RCODE,
+                                       2*8,
+                                       rcode_vals, "%s"));
                }
-               add_item_to_tree(nbns_tree, offset +  4, 2, "Questions: %d",
-                                       header.qdcount);
-               add_item_to_tree(nbns_tree, offset +  6, 2, "Answer RRs: %d",
-                                       header.ancount);
-               add_item_to_tree(nbns_tree, offset +  8, 2, "Authority RRs: %d",
-                                       header.nscount);
-               add_item_to_tree(nbns_tree, offset + 10, 2, "Additional RRs: %d",
-                                       header.arcount);
-
-               cur_off = offset + 12;
+               add_item_to_tree(nbns_tree, offset + NBNS_QUEST, 2,
+                                       "Questions: %d",
+                                       quest);
+               add_item_to_tree(nbns_tree, offset + NBNS_ANS, 2,
+                                       "Answer RRs: %d",
+                                       ans);
+               add_item_to_tree(nbns_tree, offset + NBNS_AUTH, 2,
+                                       "Authority RRs: %d",
+                                       auth);
+               add_item_to_tree(nbns_tree, offset + NBNS_ADD, 2,
+                                       "Additional RRs: %d",
+                                       add);
+
+               cur_off = offset + NBNS_HDRLEN;
     
-               if (header.qdcount > 0)
+               if (quest > 0)
                        cur_off += dissect_query_records(nbns_data_ptr,
-                                       header.qdcount, pd, cur_off, nbns_tree);
+                                       quest, pd, cur_off, nbns_tree);
 
-               if (header.ancount > 0)
+               if (ans > 0)
                        cur_off += dissect_answer_records(nbns_data_ptr,
-                                       header.ancount, pd, cur_off, nbns_tree,
-                                       header.opcode, "Answers");
+                                       ans, pd, cur_off, nbns_tree,
+                                       flags & F_OPCODE,
+                                       "Answers");
 
-               if (header.nscount > 0)
+               if (auth > 0)
                        cur_off += dissect_answer_records(nbns_data_ptr,
-                                       header.nscount, pd, cur_off, nbns_tree, 
-                                       header.opcode,
+                                       auth, pd, cur_off, nbns_tree, 
+                                       flags & F_OPCODE,
                                        "Authoritative nameservers");
 
-               if (header.arcount > 0)
+               if (add > 0)
                        cur_off += dissect_answer_records(nbns_data_ptr,
-                                       header.arcount, pd, cur_off, nbns_tree, 
-                                       header.opcode, "Additional records");
+                                       add, pd, cur_off, nbns_tree, 
+                                       flags & F_OPCODE,
+                                       "Additional records");
+       }
+}
+
+
+void
+dissect_nbdgm(const u_char *pd, int offset, frame_data *fd, GtkTree *tree)
+{
+       GtkWidget               *nbdgm_tree, *ti;
+       struct nbdgm_header     header;
+       int                     flags;
+       int                     message_index;
+
+       char *message[] = {
+               "Unknown",
+               "Direct_unique datagram",
+               "Direct_group datagram",
+               "Broadcast datagram",
+               "Datagram error",
+               "Datagram query request",
+               "Datagram positive query response",
+               "Datagram negative query response"
+       };
+
+       char *node[] = {
+               "B node",
+               "P node",
+               "M node",
+               "NBDD"
+       };
+
+       static value_string error_codes[] = {
+               { 0x82, "Destination name not present" },
+               { 0x83, "Invalid source name format" },
+               { 0x84, "Invalid destination name format" },
+               { 0x00, NULL }
+       };
+
+       char *yesno[] = { "No", "Yes" };
+
+       char name[32];
+       int len;
+
+       header.msg_type = pd[offset];
+       
+       flags = pd[offset+1];
+       header.flags.more = flags & 1;
+       header.flags.first = (flags & 2) >> 1;
+       header.flags.node_type = (flags & 12) >> 2;
+
+       header.dgm_id = pntohs(&pd[offset+2]);
+       memcpy(&header.src_ip, &pd[offset+4], 4);
+       header.src_port = pntohs(&pd[offset+8]);
+
+       if (header.msg_type == 0x10 ||
+                       header.msg_type == 0x11 || header.msg_type == 0x12) {
+               header.dgm_length = pntohs(&pd[offset+10]);
+               header.pkt_offset = pntohs(&pd[offset+12]);
+       }
+       else if (header.msg_type == 0x13) {
+               header.error_code = pntohs(&pd[offset+10]);
+       }
+
+       message_index = header.msg_type - 0x0f;
+       if (message_index < 1 || message_index > 8) {
+               message_index = 0;
+       }
+
+       if (check_col(fd, COL_PROTOCOL))
+               col_add_str(fd, COL_PROTOCOL, "NBDS (UDP)");
+       if (check_col(fd, COL_INFO)) {
+               col_add_fstr(fd, COL_INFO, "%s", message[message_index]);
+       }
+
+       if (tree) {
+               ti = add_item_to_tree(GTK_WIDGET(tree), offset, header.dgm_length,
+                               "NetBIOS Datagram Service");
+               nbdgm_tree = gtk_tree_new();
+               add_subtree(ti, nbdgm_tree, ETT_NBDGM);
+
+               add_item_to_tree(nbdgm_tree, offset, 1, "Message Type: %s",
+                               message[message_index]);
+               add_item_to_tree(nbdgm_tree, offset+1, 1, "More fragments follow: %s",
+                               yesno[header.flags.more]);
+               add_item_to_tree(nbdgm_tree, offset+1, 1, "This is first fragment: %s",
+                               yesno[header.flags.first]);
+               add_item_to_tree(nbdgm_tree, offset+1, 1, "Node Type: %s",
+                               node[header.flags.node_type]);
+
+               add_item_to_tree(nbdgm_tree, offset+2, 2, "Datagram ID: 0x%04X",
+                               header.dgm_id);
+               add_item_to_tree(nbdgm_tree, offset+4, 4, "Source IP: %s",
+                               ip_to_str((guint8 *)&header.src_ip));
+               add_item_to_tree(nbdgm_tree, offset+8, 2, "Source Port: %d",
+                               header.src_port);
+
+               offset += 10;
+
+               if (header.msg_type == 0x10 ||
+                               header.msg_type == 0x11 || header.msg_type == 0x12) {
+
+                       add_item_to_tree(nbdgm_tree, offset, 2,
+                                       "Datagram length: %d bytes", header.dgm_length);
+                       add_item_to_tree(nbdgm_tree, offset+2, 2,
+                                       "Packet offset: %d bytes", header.pkt_offset);
+
+                       offset += 4;
+
+                       /* Source name */
+                       len = get_nbns_name(&pd[offset], pd, offset, name);
+
+                       add_item_to_tree(nbdgm_tree, offset, len, "Source name: %s",
+                                       name);
+                       offset += len;
+
+                       /* Destination name */
+                       len = get_nbns_name(&pd[offset], pd, offset, name);
+
+                       add_item_to_tree(nbdgm_tree, offset, len, "Destination name: %s",
+                                       name);
+                       offset += len;
+
+                       /* here we can pass the packet off to the next protocol */
+                       dissect_data(pd, offset, fd, GTK_TREE(nbdgm_tree));
+               }
+               else if (header.msg_type == 0x13) {
+                       add_item_to_tree(nbdgm_tree, offset, 1, "Error code: %s",
+                               val_to_str(header.error_code, error_codes, "Unknown (0x%x)"));
+               }
+               else if (header.msg_type == 0x14 ||
+                               header.msg_type == 0x15 || header.msg_type == 0x16) {
+                       /* Destination name */
+                       len = get_nbns_name(&pd[offset], pd, offset, name);
+
+                       add_item_to_tree(nbdgm_tree, offset, len, "Destination name: %s",
+                                       name);
+               }
        }
 }