* 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>
#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)
}
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];
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;
}
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. */
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;
}
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) {
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);
+ }
}
}