/* packet.c
* Routines for packet disassembly
*
- * $Id: packet.c,v 1.19 1999/01/28 21:29:36 gram Exp $
+ * $Id: packet.c,v 1.45 1999/09/29 22:19:12 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
# include <sys/types.h>
#endif
-#include <gtk/gtk.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_WINSOCK_H
+#include <winsock.h>
+#endif
+
+#include <glib.h>
#include <stdio.h>
#include <stdarg.h>
# include <netinet/in.h>
#endif
-#include "ethereal.h"
#include "packet.h"
-#include "etypes.h"
+#include "print.h"
#include "file.h"
-extern GtkWidget *byte_view;
-extern GdkFont *m_r_font, *m_b_font;
extern capture_file cf;
+gboolean tree_is_expanded[NUM_TREE_TYPES];
+
+int proto_frame = -1;
+int hf_frame_arrival_time = -1;
+int hf_frame_time_delta = -1;
+int hf_frame_number = -1;
+int hf_frame_packet_len = -1;
+int hf_frame_capture_len = -1;
+
gchar *
ether_to_str(const guint8 *ad) {
static gchar str[3][18];
static gchar *cur;
+ gchar *p;
+ int i;
+ guint32 octet;
+ static const gchar hex_digits[16] = "0123456789abcdef";
if (cur == &str[0][0]) {
cur = &str[1][0];
} else {
cur = &str[0][0];
}
- sprintf(cur, "%02x:%02x:%02x:%02x:%02x:%02x", ad[0], ad[1], ad[2],
- ad[3], ad[4], ad[5]);
- return cur;
+ p = &cur[18];
+ *--p = '\0';
+ i = 5;
+ for (;;) {
+ octet = ad[i];
+ *--p = hex_digits[octet&0xF];
+ octet >>= 4;
+ *--p = hex_digits[octet&0xF];
+ if (i == 0)
+ break;
+ *--p = ':';
+ i--;
+ }
+ return p;
}
gchar *
ip_to_str(const guint8 *ad) {
static gchar str[3][16];
static gchar *cur;
+ gchar *p;
+ int i;
+ guint32 octet;
+ guint32 digit;
if (cur == &str[0][0]) {
cur = &str[1][0];
} else {
cur = &str[0][0];
}
- sprintf(cur, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
- return cur;
+ p = &cur[16];
+ *--p = '\0';
+ i = 3;
+ for (;;) {
+ octet = ad[i];
+ *--p = (octet%10) + '0';
+ octet /= 10;
+ digit = octet%10;
+ octet /= 10;
+ if (digit != 0 || octet != 0)
+ *--p = digit + '0';
+ if (octet != 0)
+ *--p = octet + '0';
+ if (i == 0)
+ break;
+ *--p = '.';
+ i--;
+ }
+ return p;
}
#define PLURALIZE(n) (((n) > 1) ? "s" : "")
return cur;
}
-void
-packet_hex_print(GtkText *bv, guchar *pd, gint len, gint bstart, gint blen) {
- gint i = 0, j, k, cur;
- gchar line[128], hexchars[] = "0123456789abcdef";
- GdkFont *cur_font, *new_font;
-
- while (i < len) {
- /* Print the line number */
- sprintf(line, "%04x ", i);
- gtk_text_insert(bv, m_r_font, NULL, NULL, line, -1);
- /* Do we start in bold? */
- cur_font = (i >= bstart && i < (bstart + blen)) ? m_b_font : m_r_font;
- j = i;
- k = i + BYTE_VIEW_WIDTH;
- cur = 0;
- /* Print the hex bit */
- while (i < k) {
- if (i < len) {
- line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
- line[cur++] = hexchars[pd[i] & 0x0f];
- } else {
- line[cur++] = ' '; line[cur++] = ' ';
- }
- line[cur++] = ' ';
- i++;
- /* Did we cross a bold/plain boundary? */
- new_font = (i >= bstart && i < (bstart + blen)) ? m_b_font : m_r_font;
- if (cur_font != new_font) {
- gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
- cur_font = new_font;
- cur = 0;
- }
- }
- line[cur++] = ' ';
- gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
- cur = 0;
- i = j;
- /* Print the ASCII bit */
- cur_font = (i >= bstart && i < (bstart + blen)) ? m_b_font : m_r_font;
- while (i < k) {
- if (i < len) {
- line[cur++] = (isgraph(pd[i])) ? pd[i] : '.';
- } else {
- line[cur++] = ' ';
- }
- i++;
- /* Did we cross a bold/plain boundary? */
- new_font = (i >= bstart && i < (bstart + blen)) ? m_b_font : m_r_font;
- if (cur_font != new_font) {
- gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
- cur_font = new_font;
- cur = 0;
- }
- }
- line[cur++] = '\n';
- line[cur] = '\0';
- gtk_text_insert(bv, cur_font, NULL, NULL, line, -1);
+/* Max string length for displaying byte string. */
+#define MAX_BYTE_STR_LEN 20
+
+/* Turn an array of bytes into a string showing the bytes in hex. */
+gchar *
+bytes_to_str(const guint8 *bd, int bd_len) {
+ static gchar str[3][MAX_BYTE_STR_LEN+3+1];
+ static gchar *cur;
+ gchar *p;
+ int len;
+ static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ if (cur == &str[0][0]) {
+ cur = &str[1][0];
+ } else if (cur == &str[1][0]) {
+ cur = &str[2][0];
+ } else {
+ cur = &str[0][0];
+ }
+ p = cur;
+ len = MAX_BYTE_STR_LEN;
+ while (bd_len > 0 && len > 0) {
+ *p++ = hex[(*bd) >> 4];
+ *p++ = hex[(*bd) & 0xF];
+ len -= 2;
+ bd++;
+ bd_len--;
}
+ if (bd_len != 0) {
+ /* Note that we're not showing the full string. */
+ *p++ = '.';
+ *p++ = '.';
+ *p++ = '.';
+ }
+ *p = '\0';
+ return cur;
}
-static void
-set_item_style(GtkWidget *widget, gpointer dummy)
+static const char *mon_names[12] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+};
+
+gchar *
+abs_time_to_str(struct timeval *abs_time)
{
- gtk_widget_set_style(widget, item_style);
+ struct tm *tmp;
+ static gchar *cur;
+ static char str[3][3+1+2+2+4+1+2+1+2+1+2+1+4+1 + 5 /* extra */];
+
+ if (cur == &str[0][0]) {
+ cur = &str[1][0];
+ } else if (cur == &str[1][0]) {
+ cur = &str[2][0];
+ } else {
+ cur = &str[0][0];
+ }
+
+ tmp = localtime(&abs_time->tv_sec);
+ sprintf(cur, "%s %2d, %d %02d:%02d:%02d.%04ld",
+ mon_names[tmp->tm_mon],
+ tmp->tm_mday,
+ tmp->tm_year + 1900,
+ tmp->tm_hour,
+ tmp->tm_min,
+ tmp->tm_sec,
+ (long)abs_time->tv_usec/100);
+
+ return cur;
}
-GtkWidget *
-add_item_to_tree(GtkWidget *tree, gint start, gint len,
- gchar *format, ...) {
- GtkWidget *ti;
- va_list ap;
- gchar label_str[256];
-
- if (!tree)
- return(NULL);
-
- va_start(ap, format);
- vsnprintf(label_str, 256, format, ap);
- ti = gtk_tree_item_new_with_label(label_str);
- gtk_container_foreach(GTK_CONTAINER(ti), set_item_style, NULL);
- gtk_object_set_data(GTK_OBJECT(ti), E_TREEINFO_START_KEY, (gpointer) start);
- gtk_object_set_data(GTK_OBJECT(ti), E_TREEINFO_LEN_KEY, (gpointer) len);
- gtk_tree_append(GTK_TREE(tree), ti);
- gtk_widget_show(ti);
-
- return ti;
+gchar *
+rel_time_to_str(struct timeval *rel_time)
+{
+ static gchar *cur;
+ static char str[3][10+1+6+1];
+
+ if (cur == &str[0][0]) {
+ cur = &str[1][0];
+ } else if (cur == &str[1][0]) {
+ cur = &str[2][0];
+ } else {
+ cur = &str[0][0];
+ }
+
+ sprintf(cur, "%ld.%06ld", (long)rel_time->tv_sec,
+ (long)rel_time->tv_usec);
+
+ return cur;
}
-void
-set_item_len(GtkWidget *ti, gint len)
+/*
+ * Given a pointer into a data buffer, and to the end of the buffer,
+ * find the end of the (putative) line at that position in the data
+ * buffer.
+ * Return a pointer to the EOL character(s) in "*eol".
+ */
+const u_char *
+find_line_end(const u_char *data, const u_char *dataend, const u_char **eol)
{
- gtk_object_set_data(GTK_OBJECT(ti), E_TREEINFO_LEN_KEY, (gpointer) len);
-}
+ const u_char *lineend;
+
+ lineend = memchr(data, '\n', dataend - data);
+ if (lineend == NULL) {
+ /*
+ * No LF - line is probably continued in next TCP segment.
+ */
+ lineend = dataend;
+ *eol = dataend;
+ } else {
+ /*
+ * Is the LF at the beginning of the line?
+ */
+ if (lineend > data) {
+ /*
+ * No - is it preceded by a carriage return?
+ * (Perhaps it's supposed to be, but that's not guaranteed....)
+ */
+ if (*(lineend - 1) == '\r') {
+ /*
+ * Yes. The EOL starts with the CR.
+ */
+ *eol = lineend - 1;
+ } else {
+ /*
+ * No. The EOL starts with the LF.
+ */
+ *eol = lineend;
+
+ /*
+ * I seem to remember that we once saw lines ending with LF-CR
+ * in an HTTP request or response, so check if it's *followed*
+ * by a carriage return.
+ */
+ if (lineend < (dataend - 1) && *(lineend + 1) == '\r') {
+ /*
+ * It's <non-LF><LF><CR>; say it ends with the CR.
+ */
+ lineend++;
+ }
+ }
+ }
-void
-add_subtree(GtkWidget *ti, GtkWidget *subtree, gint idx) {
- static gint tree_type[NUM_TREE_TYPES];
-
- gtk_tree_item_set_subtree(GTK_TREE_ITEM(ti), subtree);
- if (tree_type[idx])
- gtk_tree_item_expand(GTK_TREE_ITEM(ti));
- gtk_signal_connect(GTK_OBJECT(ti), "expand", (GtkSignalFunc) expand_tree,
- (gpointer) &tree_type[idx]);
- gtk_signal_connect(GTK_OBJECT(ti), "collapse", (GtkSignalFunc) collapse_tree,
- (gpointer) &tree_type[idx]);
+ /*
+ * Point to the character after the last character.
+ */
+ lineend++;
+ }
+ return lineend;
}
-void
-expand_tree(GtkWidget *w, gpointer data) {
- gint *val = (gint *) data;
- *val = 1;
+#define MAX_COLUMNS_LINE_DETAIL 62
+
+/*
+ * Get the length of the next token in a line, and the beginning of the
+ * next token after that (if any).
+ * Return 0 if there is no next token.
+ */
+int
+get_token_len(const u_char *linep, const u_char *lineend,
+ const u_char **next_token)
+{
+ const u_char *tokenp;
+ int token_len;
+
+ tokenp = linep;
+
+ /*
+ * Search for a blank, a CR or an LF, or the end of the buffer.
+ */
+ while (linep < lineend && *linep != ' ' && *linep != '\r' && *linep != '\n')
+ linep++;
+ token_len = linep - tokenp;
+
+ /*
+ * Skip trailing blanks.
+ */
+ while (linep < lineend && *linep == ' ')
+ linep++;
+
+ *next_token = linep;
+
+ return token_len;
}
-void
-collapse_tree(GtkWidget *w, gpointer data) {
- gint *val = (gint *) data;
- *val = 0;
+/*
+ * Given a string, generate a string from it that shows non-printable
+ * characters as C-style escapes, and return a pointer to it.
+ */
+gchar *
+format_text(const u_char *string, int len)
+{
+ static gchar fmtbuf[MAX_COLUMNS_LINE_DETAIL + 3 + 4 + 1];
+ gchar *fmtbufp;
+ int column;
+ const u_char *stringend = string + len;
+ u_char c;
+ int i;
+
+ column = 0;
+ fmtbufp = &fmtbuf[0];
+ while (string < stringend) {
+ if (column >= MAX_COLUMNS_LINE_DETAIL) {
+ /*
+ * Put "..." and quit.
+ */
+ strcpy(fmtbufp, " ...");
+ break;
+ }
+ c = *string++;
+ if (isprint(c)) {
+ *fmtbufp++ = c;
+ column++;
+ } else {
+ *fmtbufp++ = '\\';
+ column++;
+ switch (c) {
+
+ case '\\':
+ *fmtbufp++ = '\\';
+ column++;
+ break;
+
+ case '\a':
+ *fmtbufp++ = 'a';
+ column++;
+ break;
+
+ case '\b':
+ *fmtbufp++ = 'b';
+ column++;
+ break;
+
+ case '\f':
+ *fmtbufp++ = 'f';
+ column++;
+ break;
+
+ case '\n':
+ *fmtbufp++ = 'n';
+ column++;
+ break;
+
+ case '\r':
+ *fmtbufp++ = 'r';
+ column++;
+ break;
+
+ case '\t':
+ *fmtbufp++ = 't';
+ column++;
+ break;
+
+ case '\v':
+ *fmtbufp++ = 'v';
+ column++;
+ break;
+
+ default:
+ i = (c>>6)&03;
+ *fmtbufp++ = i + '0';
+ column++;
+ i = (c>>3)&07;
+ *fmtbufp++ = i + '0';
+ column++;
+ i = (c>>0)&07;
+ *fmtbufp++ = i + '0';
+ column++;
+ break;
+ }
+ }
+ }
+ *fmtbufp = '\0';
+ return fmtbuf;
}
+
/* Tries to match val against each element in the value_string array vs.
Returns the associated string ptr on a match.
Formats val with fmt, and returns the resulting string, on failure. */
return(vs[i].strptr);
i++;
}
-
+
return(NULL);
}
+/* Generate, into "buf", a string showing the bits of a bitfield.
+ Return a pointer to the character after that string. */
+static char *
+decode_bitfield_value(char *buf, guint32 val, guint32 mask, int width)
+{
+ int i;
+ guint32 bit;
+ char *p;
+
+ i = 0;
+ p = buf;
+ bit = 1 << (width - 1);
+ for (;;) {
+ if (mask & bit) {
+ /* This bit is part of the field. Show its value. */
+ if (val & bit)
+ *p++ = '1';
+ else
+ *p++ = '0';
+ } else {
+ /* This bit is not part of the field. */
+ *p++ = '.';
+ }
+ bit >>= 1;
+ i++;
+ if (i >= width)
+ break;
+ if (i % 4 == 0)
+ *p++ = ' ';
+ }
+ strcpy(p, " = ");
+ p += 3;
+ return p;
+}
+
+/* Generate a string describing a Boolean bitfield (a one-bit field that
+ says something is either true of false). */
+const char *
+decode_boolean_bitfield(guint32 val, guint32 mask, int width,
+ const char *truedesc, const char *falsedesc)
+{
+ static char buf[1025];
+ char *p;
+
+ p = decode_bitfield_value(buf, val, mask, width);
+ if (val & mask)
+ strcpy(p, truedesc);
+ else
+ strcpy(p, falsedesc);
+ return buf;
+}
+
+/* Generate a string describing an enumerated bitfield (an N-bit field
+ with various specific values having particular names). */
+const char *
+decode_enumerated_bitfield(guint32 val, guint32 mask, int width,
+ const value_string *tab, const char *fmt)
+{
+ static char buf[1025];
+ char *p;
+
+ p = decode_bitfield_value(buf, val, mask, width);
+ sprintf(p, fmt, val_to_str(val & mask, tab, "Unknown"));
+ return buf;
+}
+
+/* Generate a string describing a numeric bitfield (an N-bit field whose
+ value is just a number). */
+const char *
+decode_numeric_bitfield(guint32 val, guint32 mask, int width,
+ const char *fmt)
+{
+ static char buf[1025];
+ char *p;
+ int shift = 0;
+
+ /* Compute the number of bits we have to shift the bitfield right
+ to extract its value. */
+ while ((mask & (1<<shift)) == 0)
+ shift++;
+
+ p = decode_bitfield_value(buf, val, mask, width);
+ sprintf(p, fmt, (val & mask) >> shift);
+ return buf;
+}
+
/* Checks to see if a particular packet information element is needed for
the packet list */
gint
check_col(frame_data *fd, gint el) {
int i;
-
+
if (fd->cinfo) {
for (i = 0; i < fd->cinfo->num_cols; i++) {
if (fd->cinfo->fmt_matx[i][el])
return FALSE;
}
-/* To do: Add check_col checks to the pinfo_add* routines */
-
/* Adds a vararg list to a packet info string. */
void
col_add_fstr(frame_data *fd, gint el, gchar *format, ...) {
}
void
-col_add_str(frame_data *fd, gint el, gchar* str) {
+col_add_str(frame_data *fd, gint el, const gchar* str) {
int i;
for (i = 0; i < fd->cinfo->num_cols; i++) {
}
}
-static const char *mon_names[12] = {
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec"
-};
-
/* this routine checks the frame type from the cf structure */
void
-dissect_packet(const u_char *pd, frame_data *fd, GtkTree *tree)
+dissect_packet(const u_char *pd, frame_data *fd, proto_tree *tree)
{
- GtkWidget *fh_tree, *ti;
- struct tm *tmp;
- time_t then;
+ proto_tree *fh_tree;
+ proto_item *ti;
+ struct timeval tv;
/* Put in frame header information. */
- if (check_col(fd, COL_ABS_TIME)) {
- then = fd->abs_secs;
- tmp = localtime(&then);
- col_add_fstr(fd, COL_ABS_TIME, "%02d:%02d:%02d.%04ld",
- tmp->tm_hour,
- tmp->tm_min,
- tmp->tm_sec,
- (long)fd->abs_usecs/100);
- }
- if (check_col(fd, COL_REL_TIME)) {
- col_add_fstr(fd, COL_REL_TIME, "%d.%06d", fd->rel_secs, fd->rel_usecs);
- }
- if (check_col(fd, COL_DELTA_TIME)) {
- col_add_fstr(fd, COL_DELTA_TIME, "%d.%06d", fd->del_secs, fd->del_usecs);
- }
-
if (tree) {
- ti = add_item_to_tree(GTK_WIDGET(tree), 0, fd->cap_len,
- "Frame (%d on wire, %d captured)",
- fd->pkt_len, fd->cap_len);
-
- fh_tree = gtk_tree_new();
- add_subtree(ti, fh_tree, ETT_FRAME);
- then = fd->abs_secs;
- tmp = localtime(&then);
- add_item_to_tree(fh_tree, 0, 0,
- "Frame arrived on %s %2d, %d %02d:%02d:%02d.%04ld",
- mon_names[tmp->tm_mon],
- tmp->tm_mday,
- tmp->tm_year + 1900,
- tmp->tm_hour,
- tmp->tm_min,
- tmp->tm_sec,
- (long)fd->abs_usecs/100);
-
- add_item_to_tree(fh_tree, 0, 0, "Total frame length: %d bytes",
- fd->pkt_len);
- add_item_to_tree(fh_tree, 0, 0, "Capture frame length: %d bytes",
- fd->cap_len);
+ ti = proto_tree_add_item_format(tree, proto_frame, 0, fd->cap_len,
+ NULL, "Frame (%d on wire, %d captured)", fd->pkt_len, fd->cap_len);
+
+ fh_tree = proto_item_add_subtree(ti, ETT_FRAME);
+
+ tv.tv_sec = fd->abs_secs;
+ tv.tv_usec = fd->abs_usecs;
+
+ proto_tree_add_item(fh_tree, hf_frame_arrival_time,
+ 0, 0, &tv);
+
+ tv.tv_sec = fd->del_secs;
+ tv.tv_usec = fd->del_usecs;
+
+ proto_tree_add_item(fh_tree, hf_frame_time_delta,
+ 0, 0, &tv);
+
+ proto_tree_add_item(fh_tree, hf_frame_number,
+ 0, 0, fd->num);
+
+ proto_tree_add_item_format(fh_tree, hf_frame_packet_len,
+ 0, 0, fd->pkt_len, "Packet Length: %d byte%s", fd->pkt_len,
+ plurality(fd->pkt_len, "", "s"));
+
+ proto_tree_add_item_format(fh_tree, hf_frame_capture_len,
+ 0, 0, fd->cap_len, "Capture Length: %d byte%s", fd->cap_len,
+ plurality(fd->cap_len, "", "s"));
}
-#ifdef WITH_WIRETAP
+ /* Set the initial payload to the packet length, and the initial
+ captured payload to the capture length (other protocols may
+ reduce them if their headers say they're less). */
+ pi.len = fd->pkt_len;
+ pi.captured_len = fd->cap_len;
+
switch (fd->lnk_t) {
case WTAP_ENCAP_ETHERNET :
- dissect_eth(pd, fd, tree);
+ dissect_eth(pd, 0, fd, tree);
break;
case WTAP_ENCAP_FDDI :
- dissect_fddi(pd, fd, tree);
+ dissect_fddi(pd, fd, tree, FALSE);
+ break;
+ case WTAP_ENCAP_FDDI_BITSWAPPED :
+ dissect_fddi(pd, fd, tree, TRUE);
break;
case WTAP_ENCAP_TR :
- dissect_tr(pd, fd, tree);
+ dissect_tr(pd, 0, fd, tree);
break;
- case WTAP_ENCAP_NONE :
+ case WTAP_ENCAP_NULL :
dissect_null(pd, fd, tree);
break;
case WTAP_ENCAP_PPP :
dissect_ppp(pd, fd, tree);
break;
+ case WTAP_ENCAP_LAPB :
+ dissect_lapb(pd, fd, tree);
+ break;
case WTAP_ENCAP_RAW_IP :
dissect_raw(pd, fd, tree);
break;
- }
-#else
- switch (cf.lnk_t) {
- case DLT_EN10MB :
- dissect_eth(pd, fd, tree);
- break;
- case DLT_FDDI :
- dissect_fddi(pd, fd, tree);
- break;
- case DLT_IEEE802 :
- dissect_tr(pd, fd, tree);
+ case WTAP_ENCAP_LINUX_ATM_CLIP :
+ dissect_clip(pd, fd, tree);
break;
- case DLT_NULL :
- dissect_null(pd, fd, tree);
+ case WTAP_ENCAP_ATM_SNIFFER :
+ dissect_atm(pd, fd, tree);
break;
- case DLT_PPP :
- dissect_ppp(pd, fd, tree);
- break;
- case DLT_RAW :
- dissect_raw(pd, fd, tree);
+ case WTAP_ENCAP_ASCEND :
+ dissect_ascend(pd, fd, tree);
break;
}
-#endif
+}
+
+void
+proto_register_frame(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_frame_arrival_time,
+ { "Arrival Time", "frame.time", FT_ABSOLUTE_TIME, NULL }},
+
+ { &hf_frame_time_delta,
+ { "Time delta from previous packet", "frame.time_delta", FT_RELATIVE_TIME, NULL }},
+
+ { &hf_frame_number,
+ { "Frame Number", "frame.number", FT_UINT32, NULL }},
+
+ { &hf_frame_packet_len,
+ { "Total Frame Length", "frame.pkt_len", FT_UINT32, NULL }},
+
+ { &hf_frame_capture_len,
+ { "Capture Frame Length", "frame.cap_len", FT_UINT32, NULL }}
+ };
+
+ proto_frame = proto_register_protocol("Frame", "frame");
+ proto_register_field_array(proto_frame, hf, array_length(hf));
}