/*
* txtproto_print() derived from original code by Hannes Gredler
- * (hannes@juniper.net):
+ * (hannes@gredler.at):
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code
#include "config.h"
#endif
-#include <tcpdump-stdinc.h>
+#include <netdissect-stdinc.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
+#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
-#include "interface.h"
+#include "netdissect.h"
+#include "ascii_strcasecmp.h"
+#include "timeval-operations.h"
int32_t thiszone; /* seconds offset from gmt to local time */
+/* invalid string to print '(invalid)' for malformed or corrupted packets */
+const char istr[] = " (invalid)";
+
+/*
+ * timestamp display buffer size, the biggest size of both formats is needed
+ * sizeof("0000000000.000000000") > sizeof("00:00:00.000000000")
+ */
+#define TS_BUF_SIZE sizeof("0000000000.000000000")
+
+#define TOKBUFSIZE 128
+
+/*
+ * Print out a character, filtering out the non-printable ones
+ */
+void
+fn_print_char(netdissect_options *ndo, u_char c)
+{
+ if (!ND_ISASCII(c)) {
+ c = ND_TOASCII(c);
+ ND_PRINT((ndo, "M-"));
+ }
+ if (!ND_ISPRINT(c)) {
+ c ^= 0x40; /* DEL to ?, others to alpha */
+ ND_PRINT((ndo, "^"));
+ }
+ ND_PRINT((ndo, "%c", c));
+}
/*
* Print out a null-terminated filename (or other ascii string).
* If ep is NULL, assume no truncation check is needed.
* Return true if truncated.
+ * Stop at ep (if given) or before the null char, whichever is first.
*/
int
fn_print(netdissect_options *ndo,
return(ret);
}
+/*
+ * Print out a null-terminated filename (or other ascii string) from
+ * a fixed-length field in the packet buffer, or from what remains of
+ * the packet.
+ *
+ * n is the length of the fixed-length field, or the number of bytes
+ * remaining in the packet based on its on-the-network length.
+ *
+ * If ep is non-null, it should point just past the last captured byte
+ * of the packet, e.g. ndo->ndo_snapend. If ep is NULL, we assume no
+ * truncation check, other than the checks of the field length/remaining
+ * packet data length, is needed.
+ *
+ * Return the number of bytes of string processed, including the
+ * terminating null, if not truncated; as the terminating null is
+ * included in the count, and as there must be a terminating null,
+ * this will always be non-zero. Return 0 if truncated.
+ */
+u_int
+fn_printztn(netdissect_options *ndo,
+ register const u_char *s, register u_int n, register const u_char *ep)
+{
+ register u_int bytes;
+ register u_char c;
+
+ bytes = 0;
+ for (;;) {
+ if (n == 0 || (ep != NULL && s >= ep)) {
+ /*
+ * Truncated. This includes "no null before we
+ * got to the end of the fixed-length buffer or
+ * the end of the packet".
+ *
+ * XXX - BOOTP says "null-terminated", which
+ * means the maximum length of the string, in
+ * bytes, is 1 less than the size of the buffer,
+ * as there must always be a terminating null.
+ */
+ bytes = 0;
+ break;
+ }
+
+ c = *s++;
+ bytes++;
+ n--;
+ if (c == '\0') {
+ /* End of string */
+ break;
+ }
+ if (!ND_ISASCII(c)) {
+ c = ND_TOASCII(c);
+ ND_PRINT((ndo, "M-"));
+ }
+ if (!ND_ISPRINT(c)) {
+ c ^= 0x40; /* DEL to ?, others to alpha */
+ ND_PRINT((ndo, "^"));
+ }
+ ND_PRINT((ndo, "%c", c));
+ }
+ return(bytes);
+}
+
/*
* Print out a counted filename (or other ascii string).
* If ep is NULL, assume no truncation check is needed.
* Return true if truncated.
+ * Stop at ep (if given) or after n bytes, whichever is first.
*/
int
fn_printn(netdissect_options *ndo,
* Print out a null-padded filename (or other ascii string).
* If ep is NULL, assume no truncation check is needed.
* Return true if truncated.
+ * Stop at ep (if given) or after n bytes or before the null char,
+ * whichever is first.
*/
int
fn_printzp(netdissect_options *ndo,
#ifndef HAVE_PCAP_SET_TSTAMP_PRECISION
_U_
#endif
-, int sec, int usec)
+, int sec, int usec, char *buf)
{
- static char buf[sizeof("00:00:00.000000000")];
const char *format;
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
break;
default:
- format = "%02d:%02d:%02d.{unknown precision}";
+ format = "%02d:%02d:%02d.{unknown}";
break;
}
#else
format = "%02d:%02d:%02d.%06u";
#endif
- snprintf(buf, sizeof(buf), format,
+ snprintf(buf, TS_BUF_SIZE, format,
sec / 3600, (sec % 3600) / 60, sec % 60, usec);
return buf;
}
+/*
+ * Format the timestamp - Unix timeval style
+ */
+static char *
+ts_unix_format(netdissect_options *ndo
+#ifndef HAVE_PCAP_SET_TSTAMP_PRECISION
+_U_
+#endif
+, int sec, int usec, char *buf)
+{
+ const char *format;
+
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ switch (ndo->ndo_tstamp_precision) {
+
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ format = "%u.%06u";
+ break;
+
+ case PCAP_TSTAMP_PRECISION_NANO:
+ format = "%u.%09u";
+ break;
+
+ default:
+ format = "%u.{unknown}";
+ break;
+ }
+#else
+ format = "%u.%06u";
+#endif
+
+ snprintf(buf, TS_BUF_SIZE, format,
+ (unsigned)sec, (unsigned)usec);
+
+ return buf;
+}
+
/*
* Print the timestamp
*/
register int s;
struct tm *tm;
time_t Time;
- static unsigned b_sec;
- static unsigned b_usec;
- int d_usec;
- int d_sec;
+ char buf[TS_BUF_SIZE];
+ static struct timeval tv_ref;
+ struct timeval tv_result;
+ int negative_offset;
+ int nano_prec;
switch (ndo->ndo_tflag) {
case 0: /* Default */
s = (tvp->tv_sec + thiszone) % 86400;
- ND_PRINT((ndo, "%s ", ts_format(ndo, s, tvp->tv_usec)));
+ ND_PRINT((ndo, "%s ", ts_format(ndo, s, tvp->tv_usec, buf)));
break;
case 1: /* No time stamp */
break;
case 2: /* Unix timeval style */
- ND_PRINT((ndo, "%u.%06u ",
- (unsigned)tvp->tv_sec,
- (unsigned)tvp->tv_usec));
+ ND_PRINT((ndo, "%s ", ts_unix_format(ndo,
+ tvp->tv_sec, tvp->tv_usec, buf)));
break;
- case 3: /* Microseconds since previous packet */
- case 5: /* Microseconds since first packet */
- if (b_sec == 0) {
- /* init timestamp for first packet */
- b_usec = tvp->tv_usec;
- b_sec = tvp->tv_sec;
- }
+ case 3: /* Microseconds/nanoseconds since previous packet */
+ case 5: /* Microseconds/nanoseconds since first packet */
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ switch (ndo->ndo_tstamp_precision) {
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ nano_prec = 0;
+ break;
+ case PCAP_TSTAMP_PRECISION_NANO:
+ nano_prec = 1;
+ break;
+ default:
+ nano_prec = 0;
+ break;
+ }
+#else
+ nano_prec = 0;
+#endif
+ if (!(netdissect_timevalisset(&tv_ref)))
+ tv_ref = *tvp; /* set timestamp for first packet */
- d_usec = tvp->tv_usec - b_usec;
- d_sec = tvp->tv_sec - b_sec;
+ negative_offset = netdissect_timevalcmp(tvp, &tv_ref, <);
+ if (negative_offset)
+ netdissect_timevalsub(&tv_ref, tvp, &tv_result, nano_prec);
+ else
+ netdissect_timevalsub(tvp, &tv_ref, &tv_result, nano_prec);
- while (d_usec < 0) {
- d_usec += 1000000;
- d_sec--;
- }
+ ND_PRINT((ndo, (negative_offset ? "-" : " ")));
- ND_PRINT((ndo, "%s ", ts_format(ndo, d_sec, d_usec)));
+ ND_PRINT((ndo, "%s ", ts_format(ndo,
+ tv_result.tv_sec, tv_result.tv_usec, buf)));
- if (ndo->ndo_tflag == 3) { /* set timestamp for last packet */
- b_sec = tvp->tv_sec;
- b_usec = tvp->tv_usec;
- }
+ if (ndo->ndo_tflag == 3)
+ tv_ref = *tvp; /* set timestamp for previous packet */
break;
- case 4: /* Default + Date*/
+ case 4: /* Default + Date */
s = (tvp->tv_sec + thiszone) % 86400;
Time = (tvp->tv_sec + thiszone) - s;
tm = gmtime (&Time);
else
ND_PRINT((ndo, "%04d-%02d-%02d %s ",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
- ts_format(ndo, s, tvp->tv_usec)));
+ ts_format(ndo, s, tvp->tv_usec, buf)));
break;
}
}
/*
- * Print a relative number of seconds (e.g. hold time, prune timer)
+ * Print an unsigned relative number of seconds (e.g. hold time, prune timer)
* in the form 5m1s. This does no truncation, so 32230861 seconds
* is represented as 1y1w1d1h1m1s.
*/
void
-relts_print(netdissect_options *ndo,
- int secs)
+unsigned_relts_print(netdissect_options *ndo,
+ uint32_t secs)
{
static const char *lengths[] = {"y", "w", "d", "h", "m", "s"};
- static const int seconds[] = {31536000, 604800, 86400, 3600, 60, 1};
+ static const u_int seconds[] = {31536000, 604800, 86400, 3600, 60, 1};
const char **l = lengths;
- const int *s = seconds;
+ const u_int *s = seconds;
if (secs == 0) {
ND_PRINT((ndo, "0s"));
return;
}
- if (secs < 0) {
- ND_PRINT((ndo, "-"));
- secs = -secs;
- }
while (secs > 0) {
if (secs >= *s) {
ND_PRINT((ndo, "%d%s", secs / *s, *l));
}
}
+/*
+ * Print a signed relative number of seconds (e.g. hold time, prune timer)
+ * in the form 5m1s. This does no truncation, so 32230861 seconds
+ * is represented as 1y1w1d1h1m1s.
+ */
+void
+signed_relts_print(netdissect_options *ndo,
+ int32_t secs)
+{
+ if (secs < 0) {
+ ND_PRINT((ndo, "-"));
+ if (secs == INT32_MIN) {
+ /*
+ * -2^31; you can't fit its absolute value into
+ * a 32-bit signed integer.
+ *
+ * Just directly pass said absolute value to
+ * unsigned_relts_print() directly.
+ *
+ * (XXX - does ISO C guarantee that -(-2^n),
+ * when calculated and cast to an n-bit unsigned
+ * integer type, will have the value 2^n?)
+ */
+ unsigned_relts_print(ndo, 2147483648U);
+ } else {
+ /*
+ * We now know -secs will fit into an int32_t;
+ * negate it and pass that to unsigned_relts_print().
+ */
+ unsigned_relts_print(ndo, -secs);
+ }
+ return;
+ }
+ unsigned_relts_print(ndo, secs);
+}
+
/*
* this is a generic routine for printing unknown data;
* we pass on the linefeed plus indentation string to
tok2str(register const struct tok *lp, register const char *fmt,
register u_int v)
{
- static char buf[4][128];
+ static char buf[4][TOKBUFSIZE];
static int idx = 0;
char *ret;
bittok2str_internal(register const struct tok *lp, register const char *fmt,
register u_int v, const char *sep)
{
- static char buf[256]; /* our stringbuffer */
- int buflen=0;
+ static char buf[1024+1]; /* our string buffer */
+ char *bufp = buf;
+ size_t space_left = sizeof(buf), string_size;
register u_int rotbit; /* this is the bit we rotate through all bitpositions */
register u_int tokval;
const char * sepstr = "";
*/
if (tokval == (v&rotbit)) {
/* ok we have found something */
- buflen+=snprintf(buf+buflen, sizeof(buf)-buflen, "%s%s",
- sepstr, lp->s);
+ if (space_left <= 1)
+ return (buf); /* only enough room left for NUL, if that */
+ string_size = strlcpy(bufp, sepstr, space_left);
+ if (string_size >= space_left)
+ return (buf); /* we ran out of room */
+ bufp += string_size;
+ space_left -= string_size;
+ if (space_left <= 1)
+ return (buf); /* only enough room left for NUL, if that */
+ string_size = strlcpy(bufp, lp->s, space_left);
+ if (string_size >= space_left)
+ return (buf); /* we ran out of room */
+ bufp += string_size;
+ space_left -= string_size;
sepstr = sep;
break;
}
lp++;
}
- if (buflen == 0)
+ if (bufp == buf)
/* bummer - lets print the "unknown" message as advised in the fmt string if we got one */
(void)snprintf(buf, sizeof(buf), fmt == NULL ? "#%08x" : fmt, v);
return (buf);
/*
* Convert a value to a string using an array; the macro
- * tok2strary() in <interface.h> is the public interface to
+ * tok2strary() in <netdissect.h> is the public interface to
* this function and ensures that the second argument is
* correct for bounds-checking.
*/
tok2strary_internal(register const char **lp, int n, register const char *fmt,
register int v)
{
- static char buf[128];
+ static char buf[TOKBUFSIZE];
if (v >= 0 && v < n && lp[v] != NULL)
return lp[v];
return (prefix_len);
}
-#ifdef INET6
int
mask62plen(const u_char *mask)
{
}
return (cidr_len);
}
-#endif /* INET6 */
/*
* Routine to print out information for text-based protocols such as FTP,
if (idx != 0) {
/* Is this a valid request name? */
while ((cmd = *cmds++) != NULL) {
- if (strcasecmp((const char *)token, cmd) == 0) {
+ if (ascii_strcasecmp((const char *)token, cmd) == 0) {
/* Yes. */
is_reqresp = 1;
break;
/* Capitalize the protocol name */
for (pnp = protoname; *pnp != '\0'; pnp++)
- ND_PRINT((ndo, "%c", toupper(*pnp)));
+ ND_PRINT((ndo, "%c", toupper((u_char)*pnp)));
if (is_reqresp) {
/*
{
u_int idx = 0;
- while (*s && idx < maxlen) {
+ while (idx < maxlen && *s) {
safeputchar(ndo, *s);
idx++;
s++;