Ethereal-dev: [ethereal-dev] DHCP patch

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: guy@xxxxxxxxxx (Guy Harris)
Date: Wed, 27 Jan 1999 23:56:46 -0800 (PST)
I sent most of this out in the past couple of days, but it may have
gotten lost due to some problems "pow.zing.org" seemed to have had.

These are patches to:

	display "time interval, in seconds" values in DHCP, DNS, and
	NBNS as "N days, N hours, N minutes, N seconds";

	decode the NetBIOS node type option in DHCP;

	treat all DHCP options as having the length specified by the
	byte after the option number byte;

	fix the item length for IP address/netmask pairs to be 8 rather
	than 4;

	decode hardware types by using the same routine the ARP
	dissector uses;

	decode hardware addresses by using the same routine the ARP
	dissector uses;

	fix that routine to recognize 6-octet IEEE 802.x and
	Experimental Ethernet (3Mb) addresses as being Ethernet-like
	(although I have vague memories that 3Mb Ethernet may not have
	used 6-octet addresses);

	fix the value against which the "magic cookie" is checked to be
	in the right byte order and, if it matches, just show it as
	"(OK)";

	fix "snprintf()" to print "%o" and "%x" as unsigned rather than
	signed (the ANSI C standard says that they're unsigned, and some
	DHCP fields in a packet trace I saw looked rather odd as
	"0x-756fe3" or whatever).

diff -c /tmp/xxx/packet-arp.c ./packet-arp.c
*** /tmp/xxx/packet-arp.c	Wed Jan 27 23:31:32 1999
--- ./packet-arp.c	Mon Jan 25 22:00:32 1999
***************
*** 117,126 ****
    return cur;
  }
  
! static gchar *
  arphrdaddr_to_str(guint8 *ad, int ad_len, guint16 type) {
!   if (type == ARPHRD_ETHER && ad_len == 6) {
!     /* Ethernet address.  */
      return ether_to_str(ad);
    }
    return arpaddr_to_str(ad, ad_len);
--- 117,128 ----
    return cur;
  }
  
! gchar *
  arphrdaddr_to_str(guint8 *ad, int ad_len, guint16 type) {
!   if ((type == ARPHRD_ETHER || type == ARPHRD_EETHER || type == ARPHRD_IEEE802)
!   				&& ad_len == 6) {
!     /* Ethernet address (or Experimental 3Mb Ethernet, or IEEE 802.x
!        address, which are the same type of address). */
      return ether_to_str(ad);
    }
    return arpaddr_to_str(ad, ad_len);
***************
*** 135,164 ****
    return arpaddr_to_str(ad, ad_len);
  }
  
! /* Offsets of fields within an ARP packet. */
! #define	AR_HRD		0
! #define	AR_PRO		2
! #define	AR_HLN		4
! #define	AR_PLN		5
! #define	AR_OP		6
! 
! void
! dissect_arp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
!   guint16     ar_hrd;
!   guint16     ar_pro;
!   guint8      ar_hln;
!   guint8      ar_pln;
!   guint16     ar_op;
!   GtkWidget   *arp_tree, *ti;
!   gchar       *op_str;
!   int         sha_offset, spa_offset, tha_offset, tpa_offset;
!   gchar       *sha_str, *spa_str, *tha_str, *tpa_str;
!   static const value_string op_vals[] = {
!     {ARPOP_REQUEST,  "ARP request" },
!     {ARPOP_REPLY,    "ARP reply"   },
!     {ARPOP_RREQUEST, "RARP request"},
!     {ARPOP_RREPLY,   "RARP reply"  },
!     {0,              NULL          } };
    static const value_string hrd_vals[] = {
      {ARPHRD_NETROM,   "NET/ROM pseudo"       },
      {ARPHRD_ETHER,    "Ethernet"             },
--- 137,144 ----
    return arpaddr_to_str(ad, ad_len);
  }
  
! gchar *
! arphrdtype_to_str(guint16 hwtype, const char *fmt) {
    static const value_string hrd_vals[] = {
      {ARPHRD_NETROM,   "NET/ROM pseudo"       },
      {ARPHRD_ETHER,    "Ethernet"             },
***************
*** 190,195 ****
--- 170,203 ----
      {ARPHRD_EUI_64,   "EUI-64"               },
      {0,                NULL                  } };
  
+     return val_to_str(hwtype, hrd_vals, fmt);
+ }
+ 
+ /* Offsets of fields within an ARP packet. */
+ #define	AR_HRD		0
+ #define	AR_PRO		2
+ #define	AR_HLN		4
+ #define	AR_PLN		5
+ #define	AR_OP		6
+ 
+ void
+ dissect_arp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
+   guint16     ar_hrd;
+   guint16     ar_pro;
+   guint8      ar_hln;
+   guint8      ar_pln;
+   guint16     ar_op;
+   GtkWidget   *arp_tree, *ti;
+   gchar       *op_str;
+   int         sha_offset, spa_offset, tha_offset, tpa_offset;
+   gchar       *sha_str, *spa_str, *tha_str, *tpa_str;
+   static const value_string op_vals[] = {
+     {ARPOP_REQUEST,  "ARP request" },
+     {ARPOP_REPLY,    "ARP reply"   },
+     {ARPOP_RREQUEST, "RARP request"},
+     {ARPOP_RREPLY,   "RARP reply"  },
+     {0,              NULL          } };
+ 
    /* To do: Check for {cap len,pkt len} < struct len */
    ar_hrd = pntohs(&pd[offset + AR_HRD]);
    ar_pro = pntohs(&pd[offset + AR_PRO]);
***************
*** 246,252 ****
      arp_tree = gtk_tree_new();
      add_subtree(ti, arp_tree, ETT_ARP);
      add_item_to_tree(arp_tree, offset + AR_HRD, 2,
!       "Hardware type: %s", val_to_str(ar_hrd, hrd_vals, "Unknown (0x%04x)"));
      add_item_to_tree(arp_tree, offset + AR_PRO, 2,
        "Protocol type: %s", ethertype_to_str(ar_pro, "Unknown (0x%04x)"));
      add_item_to_tree(arp_tree, offset + AR_HLN, 1,
--- 254,260 ----
      arp_tree = gtk_tree_new();
      add_subtree(ti, arp_tree, ETT_ARP);
      add_item_to_tree(arp_tree, offset + AR_HRD, 2,
!       "Hardware type: %s", arphrdtype_to_str(ar_hrd, "Unknown (0x%04x)"));
      add_item_to_tree(arp_tree, offset + AR_PRO, 2,
        "Protocol type: %s", ethertype_to_str(ar_pro, "Unknown (0x%04x)"));
      add_item_to_tree(arp_tree, offset + AR_HLN, 1,

diff -c /tmp/xxx/packet-bootp.c ./packet-bootp.c
*** /tmp/xxx/packet-bootp.c	Wed Jan 27 23:31:33 1999
--- ./packet-bootp.c	Wed Jan 27 23:28:24 1999
***************
*** 51,56 ****
--- 51,57 ----
  #include "etypes.h"
  
  enum field_type { none, ipv4, string, toggle, yes_no, special, opaque,
+ 	time_in_secs,
  	val_u_byte, val_u_short, val_u_long,
  	val_s_long };
  
***************
*** 62,68 ****
  #define NUM_OPT_INFOS 77
  
  /* returns the number of bytes consumed by this option */
! int
  bootp_option(const u_char *pd, GtkWidget *bp_tree, int voff, int eoff)
  {
  	char			*text;
--- 63,69 ----
  #define NUM_OPT_INFOS 77
  
  /* returns the number of bytes consumed by this option */
! static int
  bootp_option(const u_char *pd, GtkWidget *bp_tree, int voff, int eoff)
  {
  	char			*text;
***************
*** 70,79 ****
  	u_char			code = pd[voff];
  	int				vlen = pd[voff+1];
  	u_char			byte;
! 	int				i, consumed = 1; /* if code is unknown, consume 1 byte */
  	GtkWidget		*vti, *v_tree;
  
! 	char	*opt53_text[] = {
  		"Unknown Message Type",
  		"Discover",
  		"Offer",
--- 71,81 ----
  	u_char			code = pd[voff];
  	int				vlen = pd[voff+1];
  	u_char			byte;
! 	int				i, consumed = vlen + 2;
! 	u_long			time_secs;
  	GtkWidget		*vti, *v_tree;
  
! 	static const char	*opt53_text[] = {
  		"Unknown Message Type",
  		"Discover",
  		"Offer",
***************
*** 84,89 ****
--- 86,97 ----
  		"Release",
  		"Inform"
  	};
+ 	static const value_string nbnt_vals[] = {
+ 	    {0x1,   "B-node" },
+ 	    {0x2,   "P-node" },
+ 	    {0x4,   "M-node" },
+ 	    {0x8,   "H-node" },
+ 	    {0,     NULL     } };
  
  	static struct opt_info opt[] = {
  		/*   0 */ { "Padding",								none },
***************
*** 110,116 ****
  		/*  21 */ { "Policy Filter",						special },
  		/*  22 */ { "Maximum Datagram Reassembly Size",		val_u_short },
  		/*  23 */ { "Default IP Time-to-Live",				val_u_byte },
! 		/*  24 */ { "Path MTU Aging Timeout",				val_u_long },
  		/*  25 */ { "Path MTU Plateau Table",				val_u_short },
  		/*  26 */ { "Interface MTU",						val_u_short },
  		/*  27 */ { "All Subnets are Local",				yes_no },
--- 118,124 ----
  		/*  21 */ { "Policy Filter",						special },
  		/*  22 */ { "Maximum Datagram Reassembly Size",		val_u_short },
  		/*  23 */ { "Default IP Time-to-Live",				val_u_byte },
! 		/*  24 */ { "Path MTU Aging Timeout",				time_in_secs },
  		/*  25 */ { "Path MTU Plateau Table",				val_u_short },
  		/*  26 */ { "Interface MTU",						val_u_short },
  		/*  27 */ { "All Subnets are Local",				yes_no },
***************
*** 121,130 ****
  		/*  32 */ { "Router Solicitation Address",			ipv4 },
  		/*  33 */ { "Static Route",							special },
  		/*  34 */ { "Trailer Encapsulation",				toggle },
! 		/*  35 */ { "ARP Cache Timeout",					val_u_long },
  		/*  36 */ { "Ethernet Encapsulation",				toggle },
  		/*  37 */ { "TCP Default TTL", 						val_u_byte },
! 		/*  38 */ { "TCP Keepalive Interval",				val_u_long },
  		/*  39 */ { "TCP Keepalive Garbage",				toggle },
  		/*  40 */ { "Network Information Service Domain",	string },
  		/*  41 */ { "Network Information Service Servers",	ipv4 },
--- 129,138 ----
  		/*  32 */ { "Router Solicitation Address",			ipv4 },
  		/*  33 */ { "Static Route",							special },
  		/*  34 */ { "Trailer Encapsulation",				toggle },
! 		/*  35 */ { "ARP Cache Timeout",					time_in_secs },
  		/*  36 */ { "Ethernet Encapsulation",				toggle },
  		/*  37 */ { "TCP Default TTL", 						val_u_byte },
! 		/*  38 */ { "TCP Keepalive Interval",				time_in_secs },
  		/*  39 */ { "TCP Keepalive Garbage",				toggle },
  		/*  40 */ { "Network Information Service Domain",	string },
  		/*  41 */ { "Network Information Service Servers",	ipv4 },
***************
*** 137,151 ****
  		/*  48 */ { "X Window System Font Server",			ipv4 },
  		/*  49 */ { "X Window System Display Manager",		ipv4 },
  		/*  50 */ { "Requested IP Address",					ipv4 },
! 		/*  51 */ { "IP Address Lease Time",				val_u_long },
  		/*  52 */ { "Option Overload",						special },
  		/*  53 */ { "DHCP Message Type",					special },
  		/*  54 */ { "Server Identifier",					ipv4 },
  		/*  55 */ { "Parameter Request List",				special },
  		/*  56 */ { "Message",								string },
  		/*  57 */ { "Maximum DHCP Message Size",			val_u_short },
! 		/*  58 */ { "Renewal Time Value",					val_u_long },
! 		/*  59 */ { "Rebinding Time Value",					val_u_long },
  		/*  60 */ { "Vendor class identifier",				opaque },
  		/*  61 */ { "Client identifier",					special },
  		/*  64 */ { "Network Information Service+ Domain",	string },
--- 145,159 ----
  		/*  48 */ { "X Window System Font Server",			ipv4 },
  		/*  49 */ { "X Window System Display Manager",		ipv4 },
  		/*  50 */ { "Requested IP Address",					ipv4 },
! 		/*  51 */ { "IP Address Lease Time",				time_in_secs },
  		/*  52 */ { "Option Overload",						special },
  		/*  53 */ { "DHCP Message Type",					special },
  		/*  54 */ { "Server Identifier",					ipv4 },
  		/*  55 */ { "Parameter Request List",				special },
  		/*  56 */ { "Message",								string },
  		/*  57 */ { "Maximum DHCP Message Size",			val_u_short },
! 		/*  58 */ { "Renewal Time Value",					time_in_secs },
! 		/*  59 */ { "Rebinding Time Value",					time_in_secs },
  		/*  60 */ { "Vendor class identifier",				opaque },
  		/*  61 */ { "Client identifier",					special },
  		/*  64 */ { "Network Information Service+ Domain",	string },
***************
*** 196,202 ****
  				v_tree = gtk_tree_new();
  				add_subtree(vti, v_tree, ETT_BOOTP_OPTION);
  				for (i = voff + 2; i < voff + consumed; i += 8) {
! 					add_item_to_tree(v_tree, i, 4, "IP Address/Mask: %s/%s",
  						ip_to_str((guint8*)&pd[i]),
  						ip_to_str((guint8*)&pd[i+4]));
  				}
--- 204,210 ----
  				v_tree = gtk_tree_new();
  				add_subtree(vti, v_tree, ETT_BOOTP_OPTION);
  				for (i = voff + 2; i < voff + consumed; i += 8) {
! 					add_item_to_tree(v_tree, i, 8, "IP Address/Mask: %s/%s",
  						ip_to_str((guint8*)&pd[i]),
  						ip_to_str((guint8*)&pd[i+4]));
  				}
***************
*** 220,226 ****
  				v_tree = gtk_tree_new();
  				add_subtree(vti, v_tree, ETT_BOOTP_OPTION);
  				for (i = voff + 2; i < voff + consumed; i += 8) {
! 					add_item_to_tree(v_tree, i, 4,
  						"Destination IP Address/Router: %s/%s",
  						ip_to_str((guint8*)&pd[i]),
  						ip_to_str((guint8*)&pd[i+4]));
--- 228,234 ----
  				v_tree = gtk_tree_new();
  				add_subtree(vti, v_tree, ETT_BOOTP_OPTION);
  				for (i = voff + 2; i < voff + consumed; i += 8) {
! 					add_item_to_tree(v_tree, i, 8,
  						"Destination IP Address/Router: %s/%s",
  						ip_to_str((guint8*)&pd[i]),
  						ip_to_str((guint8*)&pd[i+4]));
***************
*** 228,233 ****
--- 236,250 ----
  			}
  			break;
  
+ 		/* NetBIOS-over-TCP/IP Node Type */
+ 		case 46:
+ 			byte = pd[voff+2];
+ 			add_item_to_tree(bp_tree, voff, consumed,
+ 					"Option %d: %s = %s", code, text,
+ 					val_to_str(byte, nbnt_vals,
+ 					    "Unknown (0x%02x)"));
+ 			break;
+ 				
  		/* DHCP Message Type */
  		case 53:
  			byte = pd[voff+2];
***************
*** 265,280 ****
  			/* We *MAY* use hwtype/hwaddr. If we have 7 bytes, I'll
  				guess that the first is the hwtype, and the last 6 are
  				the hw addr */
! 			if (pd[voff+1] == 7) {
  				vti = add_item_to_tree(GTK_WIDGET(bp_tree), voff,
  					consumed, "Option %d: %s", code, text);
  				v_tree = gtk_tree_new();
  				add_subtree(vti, v_tree, ETT_BOOTP_OPTION);
  				add_item_to_tree(v_tree, voff+2, 1,
! 					"Hardware type: 0x%02x", pd[voff+2]);
  				add_item_to_tree(v_tree, voff+3, 6,
  					"Client hardware address: %s",
! 					ether_to_str((guint8*)&pd[voff+3]));
  			}
  			/* otherwise, it's opaque data */
  			else {
--- 282,300 ----
  			/* We *MAY* use hwtype/hwaddr. If we have 7 bytes, I'll
  				guess that the first is the hwtype, and the last 6 are
  				the hw addr */
! 			if (vlen == 7) {
  				vti = add_item_to_tree(GTK_WIDGET(bp_tree), voff,
  					consumed, "Option %d: %s", code, text);
  				v_tree = gtk_tree_new();
  				add_subtree(vti, v_tree, ETT_BOOTP_OPTION);
  				add_item_to_tree(v_tree, voff+2, 1,
! 					"Hardware type: %s",
! 					arphrdtype_to_str(pd[voff+2],
! 						"Unknown (0x%02x)"));
  				add_item_to_tree(v_tree, voff+3, 6,
  					"Client hardware address: %s",
! 					arphrdaddr_to_str((guint8*)&pd[voff+3],
! 						6, pd[voff+2]));
  			}
  			/* otherwise, it's opaque data */
  			else {
***************
*** 295,301 ****
  	}
  
  	/* Normal cases */
- 	consumed = vlen + 2;
  	if (code < NUM_OPT_INFOS) {
  		text = opt[code].text;
  		ftype = opt[code].ftype;
--- 315,320 ----
***************
*** 398,403 ****
--- 417,431 ----
  				}
  				break;
  
+ 			case time_in_secs:
+ 				time_secs = pntohl(&pd[voff+2]);
+ 				add_item_to_tree(bp_tree, voff, consumed,
+ 					"Option %d: %s = %s", code, text,
+ 					((time_secs == 0xffffffff) ?
+ 					    "infinity" :
+ 					    time_secs_to_str(time_secs)));
+ 				break;
+ 
  			default:
  				add_item_to_tree(bp_tree, voff, consumed,
  						"Option %d: %s (%d bytes)", code, text, vlen);
***************
*** 421,434 ****
  		col_add_str(fd, COL_PROTOCOL, "BOOTP");
  
  	if (check_col(fd, COL_INFO)) {
! 		/* if hwaddr is 6 bytes, assume MAC */
! 		if (pd[offset] == 1 && pd[offset+2] == 6) {
  			col_add_fstr(fd, COL_INFO, "Boot Request from %s",
! 				ether_to_str((guint8*)&pd[offset+28]));
  		}
  		else {
! 			col_add_str(fd, COL_INFO, pd[offset] == 1 ? "Boot Request" :
! 				"Boot Reply");
  		}
  	}
  
--- 449,461 ----
  		col_add_str(fd, COL_PROTOCOL, "BOOTP");
  
  	if (check_col(fd, COL_INFO)) {
! 		if (pd[offset] == 1) {
  			col_add_fstr(fd, COL_INFO, "Boot Request from %s",
! 				arphrdaddr_to_str((guint8*)&pd[offset+28],
! 					pd[offset+2], pd[offset+1]));
  		}
  		else {
! 			col_add_str(fd, COL_INFO, "Boot Reply");
  		}
  	}
  
***************
*** 441,447 ****
  		add_item_to_tree(bp_tree, offset, 1, pd[offset] == 1 ?
  			"Boot Request" : "Boot Reply");
  		add_item_to_tree(bp_tree, offset + 1, 1,
! 			"Hardware type: 0x%02x", pd[offset+1]);
  		add_item_to_tree(bp_tree, offset + 2, 1,
  			"Hardware address length: %d", pd[offset+2]);
  		add_item_to_tree(bp_tree, offset + 3, 1,
--- 468,475 ----
  		add_item_to_tree(bp_tree, offset, 1, pd[offset] == 1 ?
  			"Boot Request" : "Boot Reply");
  		add_item_to_tree(bp_tree, offset + 1, 1,
! 			"Hardware type: %s",
! 			arphrdtype_to_str(pd[offset+1], "Unknown (0x%02x)"));
  		add_item_to_tree(bp_tree, offset + 2, 1,
  			"Hardware address length: %d", pd[offset+2]);
  		add_item_to_tree(bp_tree, offset + 3, 1,
***************
*** 461,480 ****
  		add_item_to_tree(bp_tree, offset + 24, 4,
  			"Relay agent IP address: %s", ip_to_str((guint8*)&pd[offset+24]));
  
! 		/* If HW address is 6 bytes, assume MAC. */
! 		if (pd[offset+2] == 6) {
! 			add_item_to_tree(bp_tree, offset + 28, 6,
! 				"Client hardware address: %s",
! 				ether_to_str((guint8*)&pd[offset+28]));
! 		}
! 		else {
! 			add_item_to_tree(bp_tree, offset + 28, 16,
! 				"Client hardware address: %02x:%02x%02x:%02x:%02x:%02x:%02x:%02x%02x:%02x%02x:%02x:%02x:%02x:%02x:%02x",
! 				pd[offset+28], pd[offset+29], pd[offset+30], pd[offset+31],
! 				pd[offset+32], pd[offset+33], pd[offset+34], pd[offset+35],
! 				pd[offset+36], pd[offset+37], pd[offset+38], pd[offset+39],
! 				pd[offset+40], pd[offset+41], pd[offset+42], pd[offset+43]);
! 		}
  
  		/* The server host name is optional */
  		if (pd[offset+44]) {
--- 489,498 ----
  		add_item_to_tree(bp_tree, offset + 24, 4,
  			"Relay agent IP address: %s", ip_to_str((guint8*)&pd[offset+24]));
  
! 		add_item_to_tree(bp_tree, offset + 28, pd[offset+2],
! 			"Client hardware address: %s",
! 			arphrdaddr_to_str((guint8*)&pd[offset+28],
! 				pd[offset+2], pd[offset+1]));
  
  		/* The server host name is optional */
  		if (pd[offset+44]) {
***************
*** 496,505 ****
  				"Boot file name not given");
  		}
  
! 		if (pntohl(&pd[offset+236]) ==  0x63538263) {
  			add_item_to_tree(bp_tree, offset + 236, 4,
! 				"Magic cookie: %s (generic)",
! 					ip_to_str((guint8*)&pd[offset+236]));
  		}
  		else {
  			add_item_to_tree(bp_tree, offset + 236, 4,
--- 514,522 ----
  				"Boot file name not given");
  		}
  
! 		if (pntohl(&pd[offset+236]) == 0x63825363) {
  			add_item_to_tree(bp_tree, offset + 236, 4,
! 				"Magic cookie: (OK)");
  		}
  		else {
  			add_item_to_tree(bp_tree, offset + 236, 4,

diff -c /tmp/xxx/packet-dns.c ./packet-dns.c
*** /tmp/xxx/packet-dns.c	Wed Jan 27 23:31:33 1999
--- ./packet-dns.c	Mon Jan 25 23:12:27 1999
***************
*** 341,347 ****
    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;
--- 341,348 ----
    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: %s",
! 						time_secs_to_str(ttl));
    offset += 4;
    add_item_to_tree(rr_tree, offset, 2, "Data length: %u", data_len);
    return rr_tree;

diff -c /tmp/xxx/packet.c ./packet.c
*** /tmp/xxx/packet.c	Wed Jan 27 23:31:32 1999
--- ./packet.c	Mon Jan 25 22:37:06 1999
***************
*** 89,94 ****
--- 89,144 ----
    return cur;
  }
  
+ #define	PLURALIZE(n)	(((n) > 1) ? "s" : "")
+ #define	COMMA(do_it)	((do_it) ? ", " : "")
+ 
+ gchar *
+ time_secs_to_str(guint32 time)
+ {
+   static gchar  str[3][8+1+4+2+2+5+2+2+7+2+2+7+1];
+   static gchar *cur, *p;
+   int hours, mins, secs;
+   int do_comma;
+ 
+   if (cur == &str[0][0]) {
+     cur = &str[1][0];
+   } else if (cur == &str[1][0]) {  
+     cur = &str[2][0];
+   } else {  
+     cur = &str[0][0];
+   }
+ 
+   secs = time % 60;
+   time /= 60;
+   mins = time % 60;
+   time /= 60;
+   hours = time % 24;
+   time /= 24;
+ 
+   p = cur;
+   if (time != 0) {
+     sprintf(p, "%u day%s", time, PLURALIZE(time));
+     p += strlen(p);
+     do_comma = 1;
+   } else
+     do_comma = 0;
+   if (hours != 0) {
+     sprintf(p, "%s%u hour%s", COMMA(do_comma), hours, PLURALIZE(hours));
+     p += strlen(p);
+     do_comma = 1;
+   } else
+     do_comma = 0;
+   if (mins != 0) {
+     sprintf(p, "%s%u minute%s", COMMA(do_comma), mins, PLURALIZE(mins));
+     p += strlen(p);
+     do_comma = 1;
+   } else
+     do_comma = 0;
+   if (secs != 0)
+     sprintf(p, "%s%u second%s", COMMA(do_comma), secs, PLURALIZE(secs));
+   return cur;
+ }
+ 
  void
  packet_hex_print(GtkText *bv, guchar *pd, gint len, gint bstart, gint blen) {
    gint     i = 0, j, k, cur;


diff -c /tmp/xxx/packet.h ./packet.h
*** /tmp/xxx/packet.h	Wed Jan 27 23:31:32 1999
--- ./packet.h	Mon Jan 25 22:37:01 1999
***************
*** 498,503 ****
--- 498,504 ----
  /* Utility routines used by packet*.c */
  gchar*     ether_to_str(const guint8 *);
  gchar*     ip_to_str(const guint8 *);
+ gchar*     time_secs_to_str(guint32);
  void       packet_hex_print(GtkText *, guint8 *, gint, gint, gint);
  #define E_TREEINFO_START_KEY "tree_info_start"
  #define E_TREEINFO_LEN_KEY   "tree_info_len"
***************
*** 584,588 ****
--- 585,593 ----
  void ethertype(guint16 etype, int offset,
  		const u_char *pd, frame_data *fd, GtkTree *tree,
  		GtkWidget *fh_tree);
+ 
+ /* These functions are in packet-arp.c */
+ gchar *arphrdaddr_to_str(guint8 *ad, int ad_len, guint16 type);
+ gchar *arphrdtype_to_str(guint16 hwtype, const char *fmt);
  
  #endif /* packet.h */

diff -c /tmp/xxx/snprintf.c ./snprintf.c
*** /tmp/xxx/snprintf.c	Wed Jan 27 23:31:32 1999
--- ./snprintf.c	Mon Jan 25 21:32:10 1999
***************
*** 574,582 ****
            case 'o':  /* octal */
              STAR_ARGS(&data);
              if (data.a_long == FOUND)
!               d = va_arg(args, long);
              else
!               d = va_arg(args, int);
              octal(&data, d);
              state = 0;
              break;
--- 574,582 ----
            case 'o':  /* octal */
              STAR_ARGS(&data);
              if (data.a_long == FOUND)
!               d = va_arg(args, unsigned long);
              else
!               d = va_arg(args, unsigned int);
              octal(&data, d);
              state = 0;
              break;
***************
*** 584,592 ****
            case 'X':  /* hexadecimal */
              STAR_ARGS(&data);
              if (data.a_long == FOUND)
!               d = va_arg(args, long);
              else
!               d = va_arg(args, int);
              hexa(&data, d);
              state = 0;
              break;
--- 584,592 ----
            case 'X':  /* hexadecimal */
              STAR_ARGS(&data);
              if (data.a_long == FOUND)
!               d = va_arg(args, unsigned long);
              else
!               d = va_arg(args, unsigned int);
              hexa(&data, d);
              state = 0;
              break;