From 022967513b13cd4b2d3037ff918978b2967af8c6 Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Wed, 14 Oct 1998 19:35:00 +0000 Subject: [PATCH] Tag NetBIOS Name Service-over-UDP packets as "NBNS (UDP)". Give a detailed display of the innards of NBNS-over-UDP packets. Export some stuff from the DNS decoder for the use of the NBNS decoder (NBNS is DNS-like). Give a more detailed display of the innards of DNS packets as well. Fix a couple of minor NBNS bugs. svn path=/trunk/; revision=55 --- ethereal.c | 4 +- packet-dns.c | 238 ++++++++++------- packet-nbns.c | 705 ++++++++++++++++++++++++++++++++++++++++++++++---- packet.h | 6 +- 4 files changed, 804 insertions(+), 149 deletions(-) diff --git a/ethereal.c b/ethereal.c index d1707e0f55..9ad8af75ab 100644 --- a/ethereal.c +++ b/ethereal.c @@ -1,6 +1,6 @@ /* ethereal.c * - * $Id: ethereal.c,v 1.5 1998/10/12 01:40:47 gerald Exp $ + * $Id: ethereal.c,v 1.6 1998/10/14 19:34:57 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -520,7 +520,7 @@ main(int argc, char *argv[]) col_width = gdk_string_width(pl_style->font, "00:00:00:00:00:00") + 2; gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_SOURCE, col_width); gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_DESTINATION, col_width); - col_width = gdk_string_width(pl_style->font, "AppleTalk") + 2; + col_width = gdk_string_width(pl_style->font, "NBNS (UDP)") + 2; gtk_clist_set_column_width(GTK_CLIST(packet_list), COL_PROTOCOL, col_width); gtk_widget_set_usize(packet_list, -1, pl_size); gtk_paned_add1(GTK_PANED(u_pane), packet_list); diff --git a/packet-dns.c b/packet-dns.c index 59f96a1bee..ad8e475617 100644 --- a/packet-dns.c +++ b/packet-dns.c @@ -1,7 +1,7 @@ /* packet-dns.c * Routines for DNS packet disassembly * - * $Id: packet-dns.c,v 1.4 1998/09/27 22:12:28 gerald Exp $ + * $Id: packet-dns.c,v 1.5 1998/10/14 19:34:58 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -43,6 +43,7 @@ #include "ethereal.h" #include "packet.h" +#include "packet-dns.h" /* DNS structs and definitions */ @@ -56,8 +57,6 @@ typedef struct _e_dns { guint16 dns_add; } e_dns; -#define MAXDNAME 1025 /* maximum domain name */ - /* type values */ #define T_A 1 /* host address */ #define T_NS 2 /* authoritative server */ @@ -71,8 +70,6 @@ typedef struct _e_dns { #define T_AAAA 28 /* IP6 Address */ -static const u_char *dns_data_ptr; - static char * dns_type_name (int type) { @@ -116,7 +113,7 @@ dns_type_name (int type) } -static char * +char * dns_class_name(int class) { char *class_name; @@ -171,7 +168,8 @@ copy_one_name_component(const u_char *dataptr, char *nameptr) static int -copy_name_component_rec(const u_char *dataptr, char *nameptr, int *real_string_len) +copy_name_component_rec(const u_char *dns_data_ptr, const u_char *dataptr, + char *nameptr, int *real_string_len) { int len = 0; int str_len; @@ -182,7 +180,7 @@ copy_name_component_rec(const u_char *dataptr, char *nameptr, int *real_string_l compress = 1; offset = get_compressed_name_offset(dataptr); dataptr = dns_data_ptr + offset; - copy_name_component_rec(dataptr, nameptr, &str_len); + copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len); *real_string_len += str_len; nameptr += str_len; len = 2; @@ -202,7 +200,7 @@ copy_name_component_rec(const u_char *dataptr, char *nameptr, int *real_string_l if (*dataptr > 0) { *nameptr++ = '.'; - len += copy_name_component_rec(dataptr, nameptr, &str_len); + len += copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len); *real_string_len += str_len; return len; } @@ -211,24 +209,27 @@ copy_name_component_rec(const u_char *dataptr, char *nameptr, int *real_string_l } -static int -get_dns_name(const u_char *pd, int offset, char *nameptr, int maxname) +int +get_dns_name(const u_char *dns_data_ptr, const u_char *pd, int offset, + char *nameptr, int maxname) { int len; const u_char *dataptr = pd + offset; int str_len = 0; memset (nameptr, 0, maxname); - len = copy_name_component_rec(dataptr, nameptr, &str_len); + len = copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len); return len; } static int -get_dns_name_type_class (const u_char *pd, +get_dns_name_type_class (const u_char *dns_data_ptr, + const u_char *pd, int offset, - char *name_ret, + char *name_ret, + int *name_len_ret, int *type_ret, int *class_ret) { @@ -239,19 +240,20 @@ get_dns_name_type_class (const u_char *pd, char name[MAXDNAME]; const u_char *pd_save; - name_len = get_dns_name(pd, offset, name, sizeof(name)); + name_len = get_dns_name(dns_data_ptr, pd, offset, name, sizeof(name)); pd += offset; pd_save = pd; pd += name_len; - type = (*pd << 8) | *(pd + 1); + type = pntohs(pd); pd += 2; - class = (*pd << 8) | *(pd + 1); + class = pntohs(pd); pd += 2; strcpy (name_ret, name); *type_ret = type; *class_ret = class; + *name_len_ret = name_len; len = pd - pd_save; return len; @@ -259,114 +261,141 @@ get_dns_name_type_class (const u_char *pd, static int -dissect_dns_query(const u_char *pd, int offset, GtkWidget *dns_tree) -{ - int len; - char name[MAXDNAME]; - int type; - int class; - char *class_name; - char *type_name; - - len = get_dns_name_type_class (pd, offset, name, &type, &class); - - type_name = dns_type_name(type); - class_name = dns_class_name(class); - - add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s", - name, type_name, class_name ); - - return len; -} - - -static int -dissect_dns_answer(const u_char *pd, int offset, GtkWidget *dns_tree) +dissect_dns_query(const u_char *dns_data_ptr, const u_char *pd, int offset, + GtkWidget *dns_tree) { int len; char name[MAXDNAME]; + int name_len; int type; int class; char *class_name; char *type_name; const u_char *dptr; const u_char *data_start; - const u_char *res_ptr; - u_int ttl; - u_short data_len; data_start = dptr = pd + offset; - len = get_dns_name_type_class (pd, offset, name, &type, &class); + len = get_dns_name_type_class(dns_data_ptr, pd, offset, name, &name_len, + &type, &class); dptr += len; - /* this works regardless of the alignment */ - ttl = (*dptr << 24) | *(dptr + 1) << 16 | *(dptr + 2) << 8 | *(dptr + 3); - dptr += 4; - data_len = (*dptr << 8) | *(dptr + 1); - dptr += 2; + add_item_to_tree(dns_tree, offset, name_len, "Name: %s", name); + offset += name_len; + + type_name = dns_type_name(type); + add_item_to_tree(dns_tree, offset, 2, "Type: %s", type_name); + offset += 2; + + class_name = dns_class_name(class); + add_item_to_tree(dns_tree, offset, 2, "Class: %s", class_name); + offset += 2; + + return dptr - data_start; +} + + +GtkWidget * +add_rr_to_tree(GtkWidget *trr, int rr_type, int offset, const char *name, + int namelen, const char *type_name, const char *class_name, u_int ttl, + u_short data_len) +{ + GtkWidget *rr_tree; + + rr_tree = gtk_tree_new(); + add_subtree(trr, rr_tree, rr_type); + add_item_to_tree(rr_tree, offset, namelen, "Name: %s", name); + offset += namelen; + add_item_to_tree(rr_tree, offset, 2, "Type: %s", type_name); + offset += 2; + add_item_to_tree(rr_tree, offset, 2, "Class: %s", class_name); + offset += 2; + add_item_to_tree(rr_tree, offset, 4, "Time to live: %u", ttl); + offset += 4; + add_item_to_tree(rr_tree, offset, 2, "Data length: %u", data_len); + return rr_tree; +} + +static int +dissect_dns_answer(const u_char *dns_data_ptr, const u_char *pd, int offset, + GtkWidget *dns_tree) +{ + int len; + char name[MAXDNAME]; + int name_len; + int type; + int class; + char *class_name; + char *type_name; + const u_char *dptr; + const u_char *data_start; + u_int ttl; + u_short data_len; + GtkWidget *rr_tree, *trr; + + data_start = dptr = pd + offset; + + len = get_dns_name_type_class(dns_data_ptr, pd, offset, name, &name_len, + &type, &class); + dptr += len; type_name = dns_type_name(type); class_name = dns_class_name(class); - res_ptr = dptr; - /* skip the resource data */ - dptr += data_len; - - len = dptr - data_start; - + ttl = pntohl(dptr); + dptr += 4; + + data_len = pntohs(dptr); + dptr += 2; + switch (type) { case T_A: /* "A" record */ - add_item_to_tree(dns_tree, offset, len, + trr = add_item_to_tree(dns_tree, offset, (dptr - data_start) + data_len, "%s: type %s, class %s, addr %d.%d.%d.%d", name, type_name, class_name, - *res_ptr, *(res_ptr+1), *(res_ptr+2), *(res_ptr+3)); + *dptr, *(dptr+1), *(dptr+2), *(dptr+3)); + rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len, type_name, + class_name, ttl, data_len); + offset += (dptr - data_start); + add_item_to_tree(rr_tree, offset, 4, "Addr: %d.%d.%d.%d", + *dptr, *(dptr+1), *(dptr+2), *(dptr+3)); break; case T_NS: /* "NS" record */ { char ns_name[MAXDNAME]; + int ns_name_len; - get_dns_name(res_ptr, 0, ns_name, sizeof(ns_name)); - add_item_to_tree(dns_tree, offset, len, - "%s: %s, type %s, class %s", - name, ns_name, type_name, class_name); - + ns_name_len = get_dns_name(dns_data_ptr, dptr, 0, ns_name, sizeof(ns_name)); + trr = add_item_to_tree(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, + type_name, class_name, ttl, data_len); + offset += (dptr - data_start); + add_item_to_tree(rr_tree, offset, ns_name_len, "Name server: %s", ns_name); } break; /* TODO: parse more record types */ - default: - add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s", + default: + trr = add_item_to_tree(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, type_name, + class_name, ttl, data_len); + offset += (dptr - data_start); + add_item_to_tree(rr_tree, offset, data_len, "Data"); } - return len; + dptr += data_len; + + return dptr - data_start; } - static int -dissect_answer_records(int count, const u_char *pd, int cur_off, - GtkWidget *dns_tree, char *name) -{ - int start_off; - GtkWidget *qatree, *ti; - - qatree = gtk_tree_new(); - start_off = cur_off; - - while (count-- > 0) - cur_off += dissect_dns_answer(pd, cur_off, qatree); - ti = add_item_to_tree(GTK_WIDGET(dns_tree), start_off, cur_off - start_off, name); - add_subtree(ti, qatree, ETT_DNS_ANS); - - return cur_off - start_off; -} - - -static int -dissect_query_records(int count, const u_char *pd, +dissect_query_records(const u_char *dns_data_ptr, int count, const u_char *pd, int cur_off, GtkWidget *dns_tree) { int start_off; @@ -376,7 +405,7 @@ dissect_query_records(int count, const u_char *pd, start_off = cur_off; while (count-- > 0) - cur_off += dissect_dns_query(pd, cur_off, qatree); + cur_off += dissect_dns_query(dns_data_ptr, pd, cur_off, qatree); ti = add_item_to_tree(GTK_WIDGET(dns_tree), start_off, cur_off - start_off, "Queries"); add_subtree(ti, qatree, ETT_DNS_QRY); @@ -386,8 +415,29 @@ dissect_query_records(int count, const u_char *pd, +static int +dissect_answer_records(const u_char *dns_data_ptr, int count, + const u_char *pd, int cur_off, GtkWidget *dns_tree, + char *name) +{ + int start_off; + GtkWidget *qatree, *ti; + + qatree = gtk_tree_new(); + start_off = cur_off; + + while (count-- > 0) + cur_off += dissect_dns_answer(dns_data_ptr, pd, cur_off, qatree); + ti = add_item_to_tree(GTK_WIDGET(dns_tree), start_off, cur_off - start_off, name); + add_subtree(ti, qatree, ETT_DNS_ANS); + + return cur_off - start_off; +} + + void dissect_dns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) { + const u_char *dns_data_ptr; e_dns *dh; GtkWidget *dns_tree, *ti; guint16 id, flags, quest, ans, auth, add; @@ -430,17 +480,19 @@ dissect_dns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) { cur_off = offset + 12; if (quest > 0) - cur_off += dissect_query_records(quest, pd, cur_off, dns_tree); + cur_off += dissect_query_records(dns_data_ptr, quest, pd, cur_off, + dns_tree); if (ans > 0) - cur_off += dissect_answer_records(ans, pd, cur_off, dns_tree, "Answers"); + cur_off += dissect_answer_records(dns_data_ptr, ans, pd, cur_off, + dns_tree, "Answers"); if (auth > 0) - cur_off += dissect_answer_records(auth, pd, cur_off, dns_tree, - "Authoritative nameservers"); + cur_off += dissect_answer_records(dns_data_ptr, auth, pd, cur_off, + dns_tree, "Authoritative nameservers"); if (add > 0) - cur_off += dissect_answer_records(add, pd, cur_off, dns_tree, - "Additional records"); + cur_off += dissect_answer_records(dns_data_ptr, add, pd, cur_off, + dns_tree, "Additional records"); } } diff --git a/packet-nbns.c b/packet-nbns.c index 3a69973f08..90dbda7ee3 100644 --- a/packet-nbns.c +++ b/packet-nbns.c @@ -1,8 +1,9 @@ /* packet-nbns.c * Routines for NetBIOS Name Service packet disassembly * Gilbert Ramirez + * Much stuff added by Guy Harris * - * $Id: packet-nbns.c,v 1.1 1998/10/14 04:09:11 gram Exp $ + * $Id: packet-nbns.c,v 1.2 1998/10/14 19:34:59 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -44,6 +45,7 @@ #include "ethereal.h" #include "packet.h" +#include "packet-dns.h" /* Packet structure taken from RFC 1002. See also RFC 1001. * The Samba source code, specifically nmblib.c, also helps a lot. */ @@ -67,36 +69,638 @@ struct nbns_header { guint16 arcount; }; +/* type values */ +#define T_NB 32 /* NetBIOS name service RR */ +#define T_NBSTAT 33 /* NetBIOS node status RR */ + + +static char * +nbns_type_name (int type) +{ + switch (type) { + case T_NB: + return "NB"; + case T_NBSTAT: + return "NBSTAT"; + } + + return "unknown"; +} + +/* "Canonicalize" a 16-character NetBIOS name by: + * + * removing and saving the last byte; + * + * stripping trailing blanks; + * + * appending the trailing byte, as a hex number, in square brackets. */ +static char * +canonicalize_netbios_name(char *nbname) +{ + char *pnbname; + u_char lastchar; + + /* Get the last character of the name, as it's a special number + * indicating the type of the name, rather than part of the name + * *per se*. */ + pnbname = nbname + 15; /* point to the 16th character */ + lastchar = *(unsigned char *)pnbname; + + /* Now strip off any trailing blanks used to pad it to + * 16 bytes. */ + while (pnbname > &nbname[0]) { + if (*(pnbname - 1) != ' ') + break; /* found non-blank character */ + pnbname--; /* blank - skip over it */ + } + + /* Replace the last character with its hex value, in square + * brackets, to make it easier to tell what it is. */ + sprintf(pnbname, "[%02X]", lastchar); + pnbname += 4; + return pnbname; +} + +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 len; + int name_len; + int type; + int class; + char name[MAXDNAME]; + char nbname[MAXDNAME+4]; /* 4 for [] */ + 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]; + for (;;) { + /* Every two characters of the first level-encoded name + * turn into one character in the decoded name. */ + cname = *pname; + if (cname == '\0') + break; /* no more characters */ + if (cname == '.') + break; /* scope ID follows */ + if (cname < 'A' || cname > 'Z') { + /* Not legal. */ + strcpy(nbname, + "Illegal NetBIOS name (character not between A and Z in first-level encoding)"); + goto bad; + } + cname -= 'A'; + cnbname = cname << 4; + pname++; + + cname = *pname; + if (cname == '\0' || cname == '.') { + /* No more characters in the name - but we're in + * the middle of a pair. Not legal. */ + strcpy(nbname, + "Illegal NetBIOS name (odd number of bytes)"); + goto bad; + } + if (cname < 'A' || cname > 'Z') { + /* Not legal. */ + strcpy(nbname, + "Illegal NetBIOS name (character not between A and Z in first-level encoding)"); + goto bad; + } + cname -= 'A'; + cnbname |= cname; + pname++; + + /* Store the character. */ + *pnbname++ = cnbname; + } + + /* NetBIOS names are supposed to be exactly 16 bytes long. */ + if (pnbname - nbname == 16) { + /* This one is; canonicalize its name. */ + pnbname = canonicalize_netbios_name(nbname); + } else { + sprintf(nbname, "Illegal NetBIOS name (%d bytes long)", + pnbname - nbname); + goto bad; + } + if (cname == '.') { + /* We have a scope ID, starting at "pname"; append that to + * the decoded host name. */ + strcpy(pnbname, pname); + } else { + /* Terminate the decoded host name. */ + *pnbname = '\0'; + } + +bad: + strcpy (name_ret, nbname); + *type_ret = type; + *class_ret = class; + *name_len_ret = name_len; + + len = pd - pd_save; + return len; +} + + +static int +dissect_nbns_query(const u_char *nbns_data_ptr, const u_char *pd, int offset, + GtkWidget *nbns_tree) +{ + int len; + char name[MAXDNAME]; + int name_len; + int type; + int class; + char *class_name; + char *type_name; + const u_char *dptr; + const u_char *data_start; + + data_start = dptr = pd + offset; + + len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name, + &name_len, &type, &class); + dptr += len; + + add_item_to_tree(nbns_tree, offset, name_len, "Name: %s", name); + offset += name_len; + + type_name = nbns_type_name(type); + add_item_to_tree(nbns_tree, offset, 2, "Type: %s", type_name); + offset += 2; + + class_name = dns_class_name(class); + add_item_to_tree(nbns_tree, offset, 2, "Class: %s", class_name); + offset += 2; + + return dptr - data_start; +} + + +static int +dissect_nbns_answer(const u_char *nbns_data_ptr, const u_char *pd, int offset, + GtkWidget *nbns_tree, int opcode) +{ + int len; + char name[MAXDNAME]; + int name_len; + int type; + int class; + char *class_name; + char *type_name; + const u_char *dptr; + const u_char *data_start; + u_int ttl; + u_short data_len; + u_short flags; + GtkWidget *rr_tree, *trr; + + data_start = dptr = pd + offset; + + len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name, + &name_len, &type, &class); + dptr += len; + + type_name = nbns_type_name(type); + class_name = dns_class_name(class); + + ttl = pntohl(dptr); + dptr += 4; + + data_len = pntohs(dptr); + dptr += 2; + + switch (type) { + case T_NB: /* "NB" record */ + trr = add_item_to_tree(nbns_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_NBNS_RR, offset, name, + name_len, type_name, class_name, ttl, data_len); + offset += (dptr - data_start); + while (data_len > 0) { + if (opcode == 0x7) { + /* WACK response. This doesn't contain the + * same type of RR data as other T_NB + * responses. */ + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + flags = pntohs(dptr); + dptr += 2; + add_item_to_tree(rr_tree, offset, 2, + "Flags: 0x%x", flags); + offset += 2; + data_len -= 2; + } else { + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + flags = pntohs(dptr); + dptr += 2; + add_item_to_tree(rr_tree, offset, 2, + "Flags: 0x%x", flags); + offset += 2; + data_len -= 2; + + if (data_len < 4) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 4, + "Addr: %d.%d.%d.%d", + *dptr, *(dptr+1), *(dptr+2), *(dptr+3)); + dptr += 4; + offset += 4; + data_len -= 4; + } + } + break; + + case T_NBSTAT: /* "NBSTAT" record */ + { + u_int num_names; + char nbname[16+4+1]; /* 4 for [] */ + u_short name_flags; + + trr = add_item_to_tree(nbns_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_NBNS_RR, offset, name, + name_len, type_name, class_name, ttl, data_len); + offset += (dptr - data_start); + if (data_len < 1) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + num_names = *dptr; + dptr += 1; + add_item_to_tree(rr_tree, offset, 2, + "Number of names: %u", num_names); + offset += 1; + + while (num_names != 0) { + if (data_len < 16) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + goto out; + } + memcpy(nbname, dptr, 16); + dptr += 16; + canonicalize_netbios_name(nbname); + add_item_to_tree(rr_tree, offset, 16, + "Name: %s", nbname); + offset += 16; + data_len -= 16; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + goto out; + } + name_flags = pntohs(dptr); + dptr += 2; + add_item_to_tree(rr_tree, offset, 2, + "Name flags: 0x%x", name_flags); + offset += 2; + data_len -= 2; + + num_names--; + } + + if (data_len < 6) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 6, + "Unit ID: %02x:%02x:%02x:%02x:%02x:%02x", + *dptr, *(dptr + 1), *(dptr + 2), + *(dptr + 3), *(dptr + 4), *(dptr + 5)); + dptr += 6; + offset += 6; + data_len -= 6; + + if (data_len < 1) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 1, + "Jumpers: 0x%x", *dptr); + dptr += 1; + offset += 1; + data_len -= 1; + + if (data_len < 1) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 1, + "Test result: 0x%x", *dptr); + dptr += 1; + offset += 1; + data_len -= 1; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Version number: 0x%x", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Period of statistics: 0x%x", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of CRCs: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of alignment errors: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of collisions: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of send aborts: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 4) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 4, + "Number of good sends: %u", pntohl(dptr)); + dptr += 4; + offset += 4; + data_len -= 4; + + if (data_len < 4) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 4, + "Number of good receives: %u", pntohl(dptr)); + dptr += 4; + offset += 4; + data_len -= 4; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of retransmits: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of no resource conditions: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of command blocks: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Number of pending sessions: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Max number of pending sessions: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + + add_item_to_tree(rr_tree, offset, 2, + "Max total sessions possible: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + + if (data_len < 2) { + add_item_to_tree(rr_tree, offset, + data_len, "(incomplete entry)"); + break; + } + add_item_to_tree(rr_tree, offset, 2, + "Session data packet size: %u", pntohs(dptr)); + dptr += 2; + offset += 2; + data_len -= 2; + } + out: + break; + + default: + trr = add_item_to_tree(nbns_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_NBNS_RR, offset, name, + name_len, type_name, class_name, ttl, data_len); + offset += (dptr - data_start); + add_item_to_tree(rr_tree, offset, data_len, "Data"); + break; + } + dptr += data_len; + + return dptr - data_start; +} + +static int +dissect_query_records(const u_char *nbns_data_ptr, int count, const u_char *pd, + int cur_off, GtkWidget *nbns_tree) +{ + 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"); + add_subtree(ti, qatree, ETT_NBNS_QRY); + + return cur_off - start_off; +} + + + +static int +dissect_answer_records(const u_char *nbns_data_ptr, int count, + const u_char *pd, int cur_off, GtkWidget *nbns_tree, int opcode, char *name) +{ + int start_off; + GtkWidget *qatree, *ti; + + qatree = gtk_tree_new(); + start_off = cur_off; + + 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); + + return cur_off - start_off; +} + void dissect_nbns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) { - GtkWidget *nbns_tree, *ti; + GtkWidget *nbns_tree, *ti; struct nbns_header header; - int nm_flags; + int nm_flags; + const u_char *nbns_data_ptr; + int cur_off; char *opcode[] = { "Query", - "Unknown", - "Unknown", - "Unknown", - "Unknown", + "Unknown operation (1)", + "Unknown operation (2)", + "Unknown operation (3)", + "Unknown operation (4)", "Registration", "Release", "Wait and Acknowledge", - "Refresh" - "Refresh(altcode)" - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", + "Refresh", + "Refresh(altcode)", + "Unknown operation (10)", + "Unknown operation (11)", + "Unknown operation (12)", + "Unknown operation (13)", + "Unknown operation (14)", "Multi-Homed Registration", }; + 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)");*/ - strcpy(fd->win_info[COL_PROTOCOL], "NBNS"); - strcpy(fd->win_info[COL_INFO], "NetBIOS Name Service"); + 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"); + } } if (tree) { @@ -105,27 +709,9 @@ 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); - /* 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 = pletohs(&pd[offset+4]); - header.ancount = pletohs(&pd[offset+6]); - header.nscount = pletohs(&pd[offset+8]); - header.arcount = pletohs(&pd[offset+10]); - - add_item_to_tree(nbns_tree, offset, 2, "Transaction ID: 0x%04X", + 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", + add_item_to_tree(nbns_tree, offset + 2, 1, "Type: %s", header.r == 0 ? "Request" : "Response" ); if (header.opcode <= 15) { @@ -136,22 +722,35 @@ dissect_nbns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) add_item_to_tree(nbns_tree, offset + 2, 1, "Operation: Unknown (%d)", header.opcode); } + 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); -/* add_item_to_tree(nbns_tree, offset+2, 2, */ + cur_off = offset + 12; + + if (header.qdcount > 0) + cur_off += dissect_query_records(nbns_data_ptr, + header.qdcount, pd, cur_off, nbns_tree); + + if (header.ancount > 0) + cur_off += dissect_answer_records(nbns_data_ptr, + header.ancount, pd, cur_off, nbns_tree, + header.opcode, "Answers"); + + if (header.nscount > 0) + cur_off += dissect_answer_records(nbns_data_ptr, + header.nscount, pd, cur_off, nbns_tree, + header.opcode, + "Authoritative nameservers"); + + if (header.arcount > 0) + cur_off += dissect_answer_records(nbns_data_ptr, + header.arcount, pd, cur_off, nbns_tree, + header.opcode, "Additional records"); } } - - - - - - - - - - - - - - - diff --git a/packet.h b/packet.h index 90f12013a5..2d02f374de 100644 --- a/packet.h +++ b/packet.h @@ -1,7 +1,7 @@ /* packet.h * Definitions for packet disassembly structures and routines * - * $Id: packet.h,v 1.16 1998/10/14 05:18:32 gram Exp $ + * $Id: packet.h,v 1.17 1998/10/14 19:35:00 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs @@ -405,6 +405,7 @@ enum { ETT_DNS, ETT_DNS_ANS, ETT_DNS_QRY, + ETT_DNS_RR, ETT_RIP, ETT_RIP_VEC, ETT_OSPF, @@ -427,6 +428,9 @@ enum { ETT_IPXSAP, ETT_IPXSAP_SERVER, ETT_NBNS, + ETT_NBNS_ANS, + ETT_NBNS_QRY, + ETT_NBNS_RR, ETT_NBIPX, NUM_TREE_TYPES /* last item number plus one */ };