Include <time.h> to declare "gmtime()".
[obnox/wireshark/wip.git] / packet-dns.c
index fc16190c45fe5dc496918e1609c5c7edf1454e00..dec4c7a1235d803d2f0a331ca7fb92955867d91d 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-dns.c
  * Routines for DNS packet disassembly
  *
- * $Id: packet-dns.c,v 1.27 1999/11/10 06:01:21 guy Exp $
+ * $Id: packet-dns.c,v 1.33 1999/12/29 10:36:13 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
@@ -50,6 +50,14 @@ static int hf_dns_count_answers = -1;
 static int hf_dns_count_auth_rr = -1;
 static int hf_dns_count_add_rr = -1;
 
+static gint ett_dns = -1;
+static gint ett_dns_qd = -1;
+static gint ett_dns_rr = -1;
+static gint ett_dns_qry = -1;
+static gint ett_dns_ans = -1;
+static gint ett_dns_flags = -1;
+static gint ett_t_key_flags = -1;
+
 /* DNS structs and definitions */
 
 /* Offsets of fields in the DNS header. */
@@ -124,7 +132,7 @@ static int hf_dns_count_add_rr = -1;
 
 /* See RFC 1035 for all RR types for which no RFC is listed. */
 static char *
-dns_type_name (int type)
+dns_type_name (u_int type)
 {
   char *type_names[36] = {
     "unused",
@@ -193,13 +201,13 @@ dns_type_name (int type)
     case 255:
       return "ANY";
     }
-  
+
   return "unknown";
 }
 
 
 static char *
-dns_long_type_name (int type)
+dns_long_type_name (u_int type)
 {
   char *type_names[36] = {
     "unused",
@@ -500,7 +508,7 @@ dissect_dns_query(const u_char *pd, int offset, int dns_data_offset,
   if (dns_tree != NULL) {
     tq = proto_tree_add_text(dns_tree, offset, len, "%s: type %s, class %s", 
                   name, type_name, class_name);
-    q_tree = proto_item_add_subtree(tq, ETT_DNS_QD);
+    q_tree = proto_item_add_subtree(tq, ett_dns_qd);
 
     proto_tree_add_text(q_tree, offset, name_len, "Name: %s", name);
     offset += name_len;
@@ -537,6 +545,20 @@ add_rr_to_tree(proto_item *trr, int rr_type, int offset, const char *name,
   return rr_tree;
 }
 
+/*
+ * SIG and KEY RR algorithms.
+ */
+#define        DNS_ALGO_MD5            1       /* MD5/RSA */
+#define        DNS_ALGO_EDATE          253     /* Expiration date */
+#define        DNS_ALGO_PRIVATE        254     /* Private use */       
+
+static const value_string algo_vals[] = {
+         { DNS_ALGO_MD5,     "MD5/RSA" },
+         { DNS_ALGO_EDATE,   "Expiration date" },
+         { DNS_ALGO_PRIVATE, "Private use" },
+         { 0,                NULL }
+};
+
 static int
 dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
   frame_data *fd, proto_tree *dns_tree)
@@ -600,7 +622,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
                     "%s: type %s, class %s, addr %s",
                     name, type_name, class_name,
                     ip_to_str((guint8 *)dptr));
-      rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+      rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                     long_type_name, class_name, ttl, data_len);
       if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
        /* We ran past the end of the captured data in the packet. */
@@ -623,7 +645,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
        trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                       "%s: type %s, class %s, ns %s",
                       name, type_name, class_name, ns_name);
-       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        if (ns_name_len < 0) {
          /* We ran past the end of the captured data in the packet. */
@@ -647,7 +669,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
        trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                     "%s: type %s, class %s, cname %s",
                     name, type_name, class_name, cname);
-       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        if (cname_len < 0) {
          /* We ran past the end of the captured data in the packet. */
@@ -684,7 +706,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
        trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                     "%s: type %s, class %s, mname %s",
                     name, type_name, class_name, mname);
-       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        if (mname_len < 0) {
          /* We ran past the end of the captured data in the packet. */
@@ -761,7 +783,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
        trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                     "%s: type %s, class %s, ptr %s",
                     name, type_name, class_name, pname);
-       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        if (pname_len < 0) {
          /* We ran past the end of the captured data in the packet. */
@@ -774,6 +796,80 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
     }
     break;
 
+  case T_HINFO:
+    {
+      int cpu_offset;
+      int cpu_len;
+      int os_offset;
+      int os_len;
+
+      cpu_offset = cur_offset;
+      if (!BYTES_ARE_IN_FRAME(cpu_offset, 1)) {
+       /* We ran past the end of the captured data in the packet. */
+       if (dns_tree != NULL) {
+         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                      "%s: type %s, class %s, <CPU goes past end of captured data in packet>",
+                      name, type_name, class_name);
+         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+       }
+       return 0;
+      }
+      cpu_len = pd[cpu_offset];
+      if (!BYTES_ARE_IN_FRAME(cpu_offset + 1, cpu_len)) {
+       /* We ran past the end of the captured data in the packet. */
+       if (dns_tree != NULL) {
+         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                      "%s: type %s, class %s, <CPU goes past end of captured data in packet>",
+                      name, type_name, class_name);
+         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+       }
+       return 0;
+      }
+      os_offset = cpu_offset + 1 + cpu_len;
+      if (!BYTES_ARE_IN_FRAME(os_offset, 1)) {
+       /* We ran past the end of the captured data in the packet. */
+       if (dns_tree != NULL) {
+         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                      "%s: type %s, class %s, CPU %.*s, <OS goes past end of captured data in packet>",
+                      name, type_name, class_name, cpu_len, &pd[cpu_offset + 1]);
+         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+       }
+       return 0;
+      }
+      os_len = pd[os_offset];
+      if (!BYTES_ARE_IN_FRAME(os_offset + 1, os_len)) {
+       /* We ran past the end of the captured data in the packet. */
+       if (dns_tree != NULL) {
+         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                      "%s: type %s, class %s, CPU %*.s, <OS goes past end of captured data in packet>",
+                      name, type_name, class_name, cpu_len, &pd[cpu_offset + 1]);
+         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+       }
+       return 0;
+      }
+      if (fd != NULL)
+       col_append_fstr(fd, COL_INFO, " %s %.*s %.*s", type_name, cpu_len,
+           &pd[cpu_offset + 1], os_len, &pd[os_offset + 1]);
+      if (dns_tree != NULL) {
+       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                    "%s: type %s, class %s, CPU %.*s, OS %.*s",
+                    name, type_name, class_name,
+                    cpu_len, &pd[cpu_offset + 1], os_len, &pd[os_offset + 1]);
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+       proto_tree_add_text(rr_tree, cpu_offset, 1 + cpu_len, "CPU: %.*s",
+                       cpu_len, &pd[cpu_offset + 1]);
+       proto_tree_add_text(rr_tree, os_offset, 1 + os_len, "OS: %.*s",
+                       os_len, &pd[os_offset + 1]);
+      }
+      break;
+    }
+    break;
+
   case T_MX:
     {
       guint16 preference = 0;
@@ -786,7 +882,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
          trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                       "%s: type %s, class %s, <preference goes past end of captured data in packet>",
                       name, type_name, class_name);
-         rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        }
        return 0;
@@ -799,7 +895,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
          trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                       "%s: type %s, class %s, preference %u, <mx goes past end of captured data in packet>",
                       name, type_name, class_name, preference);
-         rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        }
        return 0;
@@ -810,7 +906,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
        trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                       "%s: type %s, class %s, preference %u, mx %s",
                       name, type_name, class_name, preference, mx_name);
-       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        proto_tree_add_text(rr_tree, cur_offset, 2, "Preference: %u", preference);
        proto_tree_add_text(rr_tree, cur_offset + 2, mx_name_len, "Mail exchange: %s",
@@ -818,7 +914,216 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
       }
     }
     break;
-      
+
+  case T_SIG:
+    {
+      int rr_len = data_len;
+      struct timeval unixtime;
+      char signer_name[MAXDNAME];
+      int signer_name_len;
+
+      if (fd != NULL)
+       col_append_fstr(fd, COL_INFO, " %s", type_name);
+      if (dns_tree != NULL) {
+       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                    "%s: type %s, class %s",
+                    name, type_name, class_name);
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+
+        if (!BYTES_ARE_IN_FRAME(cur_offset, 2)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+        }
+       proto_tree_add_text(rr_tree, cur_offset, 2, "Type covered: %s (%s)",
+               dns_type_name(pntohs(&pd[cur_offset])),
+               dns_long_type_name(pntohs(&pd[cur_offset])));
+       cur_offset += 2;
+       rr_len -= 2;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, 1, "Algorithm: %s",
+               val_to_str(pd[cur_offset], algo_vals,
+                   "Unknown (0x%02X)"));
+       cur_offset += 1;
+       rr_len -= 1;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, 1, "Labels: %u",
+               pd[cur_offset]);
+       cur_offset += 1;
+       rr_len -= 1;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, 4, "Original TTL: %s",
+               time_secs_to_str(pntohl(&pd[cur_offset])));
+       cur_offset += 4;
+       rr_len -= 4;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       unixtime.tv_sec = pntohl(&pd[cur_offset]);
+       unixtime.tv_usec = 0;
+       proto_tree_add_text(rr_tree, cur_offset, 4, "Signature expiration: %s",
+               abs_time_to_str(&unixtime));
+       cur_offset += 4;
+       rr_len -= 4;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       unixtime.tv_sec = pntohl(&pd[cur_offset]);
+       unixtime.tv_usec = 0;
+       proto_tree_add_text(rr_tree, cur_offset, 4, "Time signed: %s",
+               abs_time_to_str(&unixtime));
+       cur_offset += 4;
+       rr_len -= 4;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 2)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, 2, "Key footprint: 0x%04x",
+               pntohs(&pd[cur_offset]));
+       cur_offset += 2;
+       rr_len -= 2;
+
+       signer_name_len = get_dns_name(pd, cur_offset, dns_data_offset, signer_name, sizeof(signer_name));
+       if (signer_name_len < 0) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, signer_name_len,
+               "Signer's name: %s", signer_name);
+       cur_offset += signer_name_len;
+       rr_len -= signer_name_len;
+
+       proto_tree_add_text(rr_tree, cur_offset, rr_len, "Signature");
+      }
+    }
+    break;
+
+  case T_KEY:
+    {
+      int rr_len = data_len;
+      guint16 flags;
+      proto_item *tf;
+      proto_tree *flags_tree;
+
+      if (fd != NULL)
+       col_append_fstr(fd, COL_INFO, " %s", type_name);
+      if (dns_tree != NULL) {
+       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                    "%s: type %s, class %s",
+                    name, type_name, class_name);
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+
+        if (!BYTES_ARE_IN_FRAME(cur_offset, 2)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+        }
+        flags = pntohs(&pd[cur_offset]);
+       tf = proto_tree_add_text(rr_tree, cur_offset, 2, "Flags: 0x%04X", flags);
+       flags_tree = proto_item_add_subtree(tf, ett_t_key_flags);
+       proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x8000,
+                 2*8, "Key prohibited for authentication",
+                      "Key allowed for authentication"));
+       proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x4000,
+                 2*8, "Key prohibited for confidentiality",
+                      "Key allowed for confidentiality"));
+       if ((flags & 0xC000) != 0xC000) {
+         /* We have a key */
+         proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x2000,
+                 2*8, "Key is experimental or optional",
+                      "Key is required"));
+         proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0400,
+                 2*8, "Key is associated with a user",
+                      "Key is not associated with a user"));
+         proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0200,
+                 2*8, "Key is associated with the named entity",
+                      "Key is not associated with the named entity"));
+         proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0100,
+                 2*8, "This is the zone key for the specified zone",
+                      "This is not a zone key"));
+         proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0080,
+                 2*8, "Key is valid for use with IPSEC",
+                      "Key is not valid for use with IPSEC"));
+         proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0040,
+                 2*8, "Key is valid for use with MIME security multiparts",
+                      "Key is not valid for use with MIME security multiparts"));
+         proto_tree_add_text(flags_tree, cur_offset, 2, "%s",
+               decode_numeric_bitfield(flags, 0x000F,
+                 2*8, "Signatory = %u"));
+       }
+       cur_offset += 2;
+       rr_len -= 2;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, 1, "Protocol: %u",
+               pd[cur_offset]);
+       cur_offset += 1;
+       rr_len -= 1;
+
+       if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, 1, "Algorithm: %s",
+               val_to_str(pd[cur_offset], algo_vals,
+                   "Unknown (0x%02X)"));
+       cur_offset += 1;
+               rr_len -= 1;
+
+       proto_tree_add_text(rr_tree, cur_offset, rr_len, "Public key");
+      }
+    }
+    break;
+
+  case T_AAAA:
+    if (fd != NULL) {
+      col_append_fstr(fd, COL_INFO, " %s %s", type_name,
+                       ip6_to_str((struct e_in6_addr *)dptr));
+    }
+    if (dns_tree != NULL) {
+      trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                    "%s: type %s, class %s, addr %s",
+                    name, type_name, class_name,
+                    ip6_to_str((struct e_in6_addr *)dptr));
+      rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                    long_type_name, class_name, ttl, data_len);
+      if (!BYTES_ARE_IN_FRAME(cur_offset, 16)) {
+       /* We ran past the end of the captured data in the packet. */
+       return 0;
+      }
+      proto_tree_add_text(rr_tree, cur_offset, 16, "Addr: %s",
+                    ip6_to_str((struct e_in6_addr *)dptr));
+    }
+    break;
+
   case T_LOC:
     {
       if (fd != NULL)
@@ -827,7 +1132,7 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
        trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                     "%s: type %s, class %s",
                     name, type_name, class_name);
-       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
        if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
          /* We ran past the end of the captured data in the packet. */
@@ -891,6 +1196,55 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
     }
     break;
       
+  case T_NXT:
+    {
+      int rr_len = data_len;
+      char next_domain_name[MAXDNAME];
+      int next_domain_name_len;
+      int rr_type;
+      guint8 bits;
+      int mask;
+      int i;
+
+      next_domain_name_len = get_dns_name(pd, cur_offset, dns_data_offset,
+                       next_domain_name, sizeof(next_domain_name));
+      if (fd != NULL)
+       col_append_fstr(fd, COL_INFO, " %s %s", type_name, next_domain_name);
+      if (dns_tree != NULL) {
+       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
+                    "%s: type %s, class %s, next domain name %s",
+                    name, type_name, class_name, next_domain_name);
+       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
+                      long_type_name, class_name, ttl, data_len);
+       if (next_domain_name_len < 0) {
+         /* We ran past the end of the captured data in the packet. */
+         return 0;
+       }
+       proto_tree_add_text(rr_tree, cur_offset, next_domain_name_len,
+                       "Next domain name: %s", next_domain_name);
+       cur_offset += next_domain_name_len;
+       rr_len -= next_domain_name_len;
+       rr_type = 0;
+       while (rr_len != 0) {
+         bits = pd[cur_offset];
+         mask = 1<<8;
+         for (i = 0; i < 8; i++) {
+           if (bits & mask) {
+             proto_tree_add_text(rr_tree, cur_offset, 1,
+                       "RR type in bit map: %s (%s)",
+                       dns_type_name(rr_type),
+                       dns_long_type_name(rr_type));
+           }
+           mask >>= 1;
+           rr_type++;
+         }
+         cur_offset += 1;
+         rr_len -= 1;
+       }
+      }
+    }
+    break;
+
     /* TODO: parse more record types */
 
   default:
@@ -900,10 +1254,11 @@ dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
                     "%s: type %s, class %s",
                     name, type_name, class_name);
-      rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
+      rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
                       long_type_name, class_name, ttl, data_len);
       proto_tree_add_text(rr_tree, cur_offset, data_len, "Data");
     }
+    break;
   }
   
   dptr += data_len;
@@ -922,7 +1277,7 @@ dissect_query_records(const u_char *pd, int cur_off, int dns_data_offset,
   start_off = cur_off;
   if (dns_tree) {
     ti = proto_tree_add_text(dns_tree, start_off, 0, "Queries");
-    qatree = proto_item_add_subtree(ti, ETT_DNS_QRY);
+    qatree = proto_item_add_subtree(ti, ett_dns_qry);
   }
   while (count-- > 0) {
     add_off = dissect_dns_query(pd, cur_off, dns_data_offset, fd, qatree);
@@ -949,7 +1304,7 @@ dissect_answer_records(const u_char *pd, int cur_off, int dns_data_offset,
   start_off = cur_off;
   if (dns_tree) {
     ti = proto_tree_add_text(dns_tree, start_off, 0, name);
-    qatree = proto_item_add_subtree(ti, ETT_DNS_ANS);
+    qatree = proto_item_add_subtree(ti, ett_dns_ans);
   }
   while (count-- > 0) {
     add_off = dissect_dns_answer(pd, cur_off, dns_data_offset, fd, qatree);
@@ -991,7 +1346,7 @@ dissect_dns(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
   dns_data_offset = offset;
 
   if (check_col(fd, COL_PROTOCOL))
-    col_add_str(fd, COL_PROTOCOL, "DNS (UDP)");
+    col_add_str(fd, COL_PROTOCOL, "DNS");
 
   if (pi.captured_len < DNS_HDRLEN) {
     col_add_str(fd, COL_INFO, "Short DNS packet");
@@ -1008,10 +1363,16 @@ dissect_dns(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
   add   = pntohs(&pd[offset + DNS_ADD]);
 
   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" : "");
+    strcpy(buf, val_to_str(flags & F_OPCODE, opcode_vals, "Unknown operation (%x)"));
+    if (flags & F_RESPONSE) {
+      strcat(buf, " response");
+      if ((flags & F_RCODE) != RCODE_NOERROR) {
+        strcat(buf, ", ");
+        strcat(buf, val_to_str(flags & F_RCODE, rcode_vals,
+            "Unknown error (%x)"));
+      }
+    }
+    col_add_str(fd, COL_INFO, buf);
   } else {
     /* Set "fd" to NULL; we pass a NULL "fd" to the query and answer
        dissectors, as a way of saying that they shouldn't add stuff
@@ -1022,9 +1383,9 @@ dissect_dns(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
   
   if (tree) {
     ti = proto_tree_add_item_format(tree, proto_dns, offset, 4, NULL,
-                         (flags & F_RESPONSE) ? "DNS response" : "DNS query");
+      "Domain Name System (%s)", (flags & F_RESPONSE) ? "response" : "query");
     
-    dns_tree = proto_item_add_subtree(ti, ETT_DNS);
+    dns_tree = proto_item_add_subtree(ti, ett_dns);
 
     if (flags & F_RESPONSE)
       proto_tree_add_item_hidden(dns_tree, hf_dns_response, offset, 4, 1);
@@ -1046,7 +1407,7 @@ dissect_dns(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                                    flags,
                                    "Flags: 0x%04x (%s)",
                                    flags, buf);
-    field_tree = proto_item_add_subtree(tf, ETT_DNS_FLAGS);
+    field_tree = proto_item_add_subtree(tf, ett_dns_flags);
     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
        decode_boolean_bitfield(flags, F_RESPONSE,
             2*8, "Response", "Query"));
@@ -1158,8 +1519,17 @@ proto_register_dns(void)
        FT_UINT16, BASE_DEC, NULL, 0x0,
        "Number of additional records in packet" }}
   };
+  static gint *ett[] = {
+    &ett_dns,
+    &ett_dns_qd,
+    &ett_dns_rr,
+    &ett_dns_qry,
+    &ett_dns_ans,
+    &ett_dns_flags,
+    &ett_t_key_flags,
+  };
 
   proto_dns = proto_register_protocol("Domain Name Service", "dns");
   proto_register_field_array(proto_dns, hf, array_length(hf));
-
+  proto_register_subtree_array(ett, array_length(ett));
 }