X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/ad7a38341c19e71e3595c17368ac18f08b71482d..87889ba67add1e8abd77fdaacbfd831ee9335bcc:/util.c diff --git a/util.c b/util.c index 4d25d6f0..5569021c 100644 --- a/util.c +++ b/util.c @@ -19,11 +19,23 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/tcpdump/util.c,v 1.109 2007-01-29 09:59:42 hannes Exp $ (LBL)"; -#endif +/* + * txtproto_print() derived from original code by Hannes Gredler + * (hannes@juniper.net): + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code + * distributions retain the above copyright notice and this paragraph + * in its entirety, and (2) distributions including binary code include + * the above copyright notice and this paragraph in its entirety in + * the documentation or other materials provided with the distribution. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND + * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT + * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + */ +#define NETDISSECT_REWORKED #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -32,11 +44,9 @@ static const char rcsid[] _U_ = #include -#include #ifdef HAVE_FCNTL_H #include #endif -#include #include #include #include @@ -44,15 +54,14 @@ static const char rcsid[] _U_ = #include "interface.h" -char * ts_format(register int, register int); - /* * Print out a null-terminated filename (or other ascii string). * If ep is NULL, assume no truncation check is needed. * Return true if truncated. */ int -fn_print(register const u_char *s, register const u_char *ep) +fn_print(netdissect_options *ndo, + register const u_char *s, register const u_char *ep) { register int ret; register u_char c; @@ -64,16 +73,15 @@ fn_print(register const u_char *s, register const u_char *ep) ret = 0; break; } - if (!isascii(c)) { - c = toascii(c); - putchar('M'); - putchar('-'); + if (!ND_ISASCII(c)) { + c = ND_TOASCII(c); + ND_PRINT((ndo, "M-")); } - if (!isprint(c)) { + if (!ND_ISPRINT(c)) { c ^= 0x40; /* DEL to ?, others to alpha */ - putchar('^'); + ND_PRINT((ndo, "^")); } - putchar(c); + ND_PRINT((ndo, "%c", c)); } return(ret); } @@ -84,24 +92,23 @@ fn_print(register const u_char *s, register const u_char *ep) * Return true if truncated. */ int -fn_printn(register const u_char *s, register u_int n, - register const u_char *ep) +fn_printn(netdissect_options *ndo, + register const u_char *s, register u_int n, register const u_char *ep) { register u_char c; while (n > 0 && (ep == NULL || s < ep)) { n--; c = *s++; - if (!isascii(c)) { - c = toascii(c); - putchar('M'); - putchar('-'); + if (!ND_ISASCII(c)) { + c = ND_TOASCII(c); + ND_PRINT((ndo, "M-")); } - if (!isprint(c)) { + if (!ND_ISPRINT(c)) { c ^= 0x40; /* DEL to ?, others to alpha */ - putchar('^'); + ND_PRINT((ndo, "^")); } - putchar(c); + ND_PRINT((ndo, "%c", c)); } return (n == 0) ? 0 : 1; } @@ -112,8 +119,9 @@ fn_printn(register const u_char *s, register u_int n, * Return true if truncated. */ int -fn_printzp(register const u_char *s, register u_int n, - register const u_char *ep) +fn_printzp(netdissect_options *ndo, + register const u_char *s, register u_int n, + register const u_char *ep) { register int ret; register u_char c; @@ -126,16 +134,15 @@ fn_printzp(register const u_char *s, register u_int n, ret = 0; break; } - if (!isascii(c)) { - c = toascii(c); - putchar('M'); - putchar('-'); + if (!ND_ISASCII(c)) { + c = ND_TOASCII(c); + ND_PRINT((ndo, "M-")); } - if (!isprint(c)) { + if (!ND_ISPRINT(c)) { c ^= 0x40; /* DEL to ?, others to alpha */ - putchar('^'); + ND_PRINT((ndo, "^")); } - putchar(c); + ND_PRINT((ndo, "%c", c)); } return (n == 0) ? 0 : ret; } @@ -143,12 +150,37 @@ fn_printzp(register const u_char *s, register u_int n, /* * Format the timestamp */ -char * -ts_format(register int sec, register int usec) +static char * +ts_format(netdissect_options *ndo +#ifndef HAVE_PCAP_SET_TSTAMP_PRECISION +_U_ +#endif +, int sec, int usec) { - static char buf[sizeof("00:00:00.000000")]; - (void)snprintf(buf, sizeof(buf), "%02d:%02d:%02d.%06u", - sec / 3600, (sec % 3600) / 60, sec % 60, usec); + static char buf[sizeof("00:00:00.000000000")]; + const char *format; + +#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION + switch (ndo->ndo_tstamp_precision) { + + case PCAP_TSTAMP_PRECISION_MICRO: + format = "%02d:%02d:%02d.%06u"; + break; + + case PCAP_TSTAMP_PRECISION_NANO: + format = "%02d:%02d:%02d.%09u"; + break; + + default: + format = "%02d:%02d:%02d.{unknown precision}"; + break; + } +#else + format = "%02d:%02d:%02d.%06u"; +#endif + + snprintf(buf, sizeof(buf), format, + sec / 3600, (sec % 3600) / 60, sec % 60, usec); return buf; } @@ -157,7 +189,8 @@ ts_format(register int sec, register int usec) * Print the timestamp */ void -ts_print(register const struct timeval *tvp) +ts_print(netdissect_options *ndo, + register const struct timeval *tvp) { register int s; struct tm *tm; @@ -167,20 +200,20 @@ ts_print(register const struct timeval *tvp) int d_usec; int d_sec; - switch (tflag) { + switch (ndo->ndo_tflag) { case 0: /* Default */ s = (tvp->tv_sec + thiszone) % 86400; - (void)printf("%s ", ts_format(s, tvp->tv_usec)); + ND_PRINT((ndo, "%s ", ts_format(ndo, s, tvp->tv_usec))); break; case 1: /* No time stamp */ break; case 2: /* Unix timeval style */ - (void)printf("%u.%06u ", + ND_PRINT((ndo, "%u.%06u ", (unsigned)tvp->tv_sec, - (unsigned)tvp->tv_usec); + (unsigned)tvp->tv_usec)); break; case 3: /* Microseconds since previous packet */ @@ -199,9 +232,9 @@ ts_print(register const struct timeval *tvp) d_sec--; } - (void)printf("%s ", ts_format(d_sec, d_usec)); + ND_PRINT((ndo, "%s ", ts_format(ndo, d_sec, d_usec))); - if (tflag == 3) { /* set timestamp for last packet */ + if (ndo->ndo_tflag == 3) { /* set timestamp for last packet */ b_sec = tvp->tv_sec; b_usec = tvp->tv_usec; } @@ -212,11 +245,11 @@ ts_print(register const struct timeval *tvp) Time = (tvp->tv_sec + thiszone) - s; tm = gmtime (&Time); if (!tm) - printf("Date fail "); + ND_PRINT((ndo, "Date fail ")); else - printf("%04d-%02d-%02d %s ", + ND_PRINT((ndo, "%04d-%02d-%02d %s ", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, - ts_format(s, tvp->tv_usec)); + ts_format(ndo, s, tvp->tv_usec))); break; } } @@ -227,7 +260,8 @@ ts_print(register const struct timeval *tvp) * is represented as 1y1w1d1h1m1s. */ void -relts_print(int secs) +relts_print(netdissect_options *ndo, + int secs) { static const char *lengths[] = {"y", "w", "d", "h", "m", "s"}; static const int seconds[] = {31536000, 604800, 86400, 3600, 60, 1}; @@ -235,16 +269,16 @@ relts_print(int secs) const int *s = seconds; if (secs == 0) { - (void)printf("0s"); + ND_PRINT((ndo, "0s")); return; } if (secs < 0) { - (void)printf("-"); + ND_PRINT((ndo, "-")); secs = -secs; } while (secs > 0) { if (secs >= *s) { - (void)printf("%d%s", secs / *s, *l); + ND_PRINT((ndo, "%d%s", secs / *s, *l)); secs -= (secs / *s) * *s; } s++; @@ -266,8 +300,8 @@ print_unknown_data(netdissect_options *ndo, const u_char *cp,const char *ident,i ident)); return(0); } - if (snapend - cp < len) - len = snapend - cp; + if (ndo->ndo_snapend - cp < len) + len = ndo->ndo_snapend - cp; if (len < 0) { ND_PRINT((ndo,"%sDissector error: print_unknown_data called with pointer past end of packet", ident)); @@ -282,7 +316,7 @@ print_unknown_data(netdissect_options *ndo, const u_char *cp,const char *ident,i */ const char * tok2strbuf(register const struct tok *lp, register const char *fmt, - register int v, char *buf, size_t bufsize) + register u_int v, char *buf, size_t bufsize) { if (lp != NULL) { while (lp->s != NULL) { @@ -327,6 +361,7 @@ bittok2str_internal(register const struct tok *lp, register const char *fmt, int buflen=0; register int rotbit; /* this is the bit we rotate through all bitpositions */ register int tokval; + const char * sepstr = ""; while (lp != NULL && lp->s != NULL) { tokval=lp->v; /* load our first value */ @@ -339,7 +374,8 @@ bittok2str_internal(register const struct tok *lp, register const char *fmt, if (tokval == (v&rotbit)) { /* ok we have found something */ buflen+=snprintf(buf+buflen, sizeof(buf)-buflen, "%s%s", - lp->s, sep ? ", " : ""); + sepstr, lp->s); + sepstr = sep ? ", " : ""; break; } rotbit=rotbit<<1; /* no match - lets shift and try again */ @@ -347,23 +383,10 @@ bittok2str_internal(register const struct tok *lp, register const char *fmt, lp++; } - /* user didn't want string seperation - no need to cut off trailing seperators */ - if (!sep) { - return (buf); - } - - if (buflen != 0) { /* did we find anything */ - /* yep, set the the trailing zero 2 bytes before to eliminate the last comma & whitespace */ - buf[buflen-2] = '\0'; - return (buf); - } - else { + if (buflen == 0) /* bummer - lets print the "unknown" message as advised in the fmt string if we got one */ - if (fmt == NULL) - fmt = "#%d"; - (void)snprintf(buf, sizeof(buf), fmt, v); - return (buf); - } + (void)snprintf(buf, sizeof(buf), fmt == NULL ? "#%d" : fmt, v); + return (buf); } /* @@ -415,9 +438,9 @@ tok2strary_internal(register const char **lp, int n, register const char *fmt, */ int -mask2plen(u_int32_t mask) +mask2plen(uint32_t mask) { - u_int32_t bitmasks[33] = { + uint32_t bitmasks[33] = { 0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000, 0xfc000000, 0xfe000000, 0xff000000, @@ -468,6 +491,249 @@ mask62plen(const u_char *mask) } #endif /* INET6 */ +/* + * Routine to print out information for text-based protocols such as FTP, + * HTTP, SMTP, RTSP, SIP, .... + */ +#define MAX_TOKEN 128 + +/* + * Fetch a token from a packet, starting at the specified index, + * and return the length of the token. + * + * Returns 0 on error; yes, this is indistinguishable from an empty + * token, but an "empty token" isn't a valid token - it just means + * either a space character at the beginning of the line (this + * includes a blank line) or no more tokens remaining on the line. + */ +static int +fetch_token(netdissect_options *ndo, const u_char *pptr, u_int idx, u_int len, + u_char *tbuf, size_t tbuflen) +{ + size_t toklen = 0; + + for (; idx < len; idx++) { + if (!ND_TTEST(*(pptr + idx))) { + /* ran past end of captured data */ + return (0); + } + if (!isascii(*(pptr + idx))) { + /* not an ASCII character */ + return (0); + } + if (isspace(*(pptr + idx))) { + /* end of token */ + break; + } + if (!isprint(*(pptr + idx))) { + /* not part of a command token or response code */ + return (0); + } + if (toklen + 2 > tbuflen) { + /* no room for this character and terminating '\0' */ + return (0); + } + tbuf[toklen] = *(pptr + idx); + toklen++; + } + if (toklen == 0) { + /* no token */ + return (0); + } + tbuf[toklen] = '\0'; + + /* + * Skip past any white space after the token, until we see + * an end-of-line (CR or LF). + */ + for (; idx < len; idx++) { + if (!ND_TTEST(*(pptr + idx))) { + /* ran past end of captured data */ + break; + } + if (*(pptr + idx) == '\r' || *(pptr + idx) == '\n') { + /* end of line */ + break; + } + if (!isascii(*(pptr + idx)) || !isprint(*(pptr + idx))) { + /* not a printable ASCII character */ + break; + } + if (!isspace(*(pptr + idx))) { + /* beginning of next token */ + break; + } + } + return (idx); +} + +/* + * Scan a buffer looking for a line ending - LF or CR-LF. + * Return the index of the character after the line ending or 0 if + * we encounter a non-ASCII or non-printable character or don't find + * the line ending. + */ +static u_int +print_txt_line(netdissect_options *ndo, const char *protoname, + const char *prefix, const u_char *pptr, u_int idx, u_int len) +{ + u_int startidx; + u_int linelen; + + startidx = idx; + while (idx < len) { + ND_TCHECK(*(pptr+idx)); + if (*(pptr+idx) == '\n') { + /* + * LF without CR; end of line. + * Skip the LF and print the line, with the + * exception of the LF. + */ + linelen = idx - startidx; + idx++; + goto print; + } else if (*(pptr+idx) == '\r') { + /* CR - any LF? */ + if ((idx+1) >= len) { + /* not in this packet */ + return (0); + } + ND_TCHECK(*(pptr+idx+1)); + if (*(pptr+idx+1) == '\n') { + /* + * CR-LF; end of line. + * Skip the CR-LF and print the line, with + * the exception of the CR-LF. + */ + linelen = idx - startidx; + idx += 2; + goto print; + } + + /* + * CR followed by something else; treat this + * as if it were binary data, and don't print + * it. + */ + return (0); + } else if (!isascii(*(pptr+idx)) || + (!isprint(*(pptr+idx)) && *(pptr+idx) != '\t')) { + /* + * Not a printable ASCII character and not a tab; + * treat this as if it were binary data, and + * don't print it. + */ + return (0); + } + idx++; + } + + /* + * All printable ASCII, but no line ending after that point + * in the buffer; treat this as if it were truncated. + */ +trunc: + linelen = idx - startidx; + ND_PRINT((ndo, "%s%.*s[!%s]", prefix, (int)linelen, pptr + startidx, + protoname)); + return (0); + +print: + ND_PRINT((ndo, "%s%.*s", prefix, (int)linelen, pptr + startidx)); + return (idx); +} + +void +txtproto_print(netdissect_options *ndo, const u_char *pptr, u_int len, + const char *protoname, const char **cmds, u_int flags) +{ + u_int idx, eol; + u_char token[MAX_TOKEN+1]; + const char *cmd; + int is_reqresp = 0; + const char *pnp; + + if (cmds != NULL) { + /* + * This protocol has more than just request and + * response lines; see whether this looks like a + * request or response. + */ + idx = fetch_token(ndo, pptr, 0, len, token, sizeof(token)); + if (idx != 0) { + /* Is this a valid request name? */ + while ((cmd = *cmds++) != NULL) { + if (strcasecmp((const char *)token, cmd) == 0) { + /* Yes. */ + is_reqresp = 1; + break; + } + } + + /* + * No - is this a valid response code (3 digits)? + * + * Is this token the response code, or is the next + * token the response code? + */ + if (flags & RESP_CODE_SECOND_TOKEN) { + /* + * Next token - get it. + */ + idx = fetch_token(ndo, pptr, idx, len, token, + sizeof(token)); + } + if (idx != 0) { + if (isdigit(token[0]) && isdigit(token[1]) && + isdigit(token[2]) && token[3] == '\0') { + /* Yes. */ + is_reqresp = 1; + } + } + } + } else { + /* + * This protocol has only request and response lines + * (e.g., FTP, where all the data goes over a + * different connection); assume the payload is + * a request or response. + */ + is_reqresp = 1; + } + + /* Capitalize the protocol name */ + for (pnp = protoname; *pnp != '\0'; pnp++) + ND_PRINT((ndo, "%c", toupper(*pnp))); + + if (is_reqresp) { + /* + * In non-verbose mode, just print the protocol, followed + * by the first line as the request or response info. + * + * In verbose mode, print lines as text until we run out + * of characters or see something that's not a + * printable-ASCII line. + */ + if (ndo->ndo_vflag) { + /* + * We're going to print all the text lines in the + * request or response; just print the length + * on the first line of the output. + */ + ND_PRINT((ndo, ", length: %u", len)); + for (idx = 0; + idx < len && (eol = print_txt_line(ndo, protoname, "\n\t", pptr, idx, len)) != 0; + idx = eol) + ; + } else { + /* + * Just print the first text line. + */ + print_txt_line(ndo, protoname, ": ", pptr, 0, len); + } + } +} + /* VARARGS */ void error(const char *fmt, ...) @@ -584,25 +850,41 @@ read_infile(char *fname) } void -safeputs(const char *s, int maxlen) +safeputs(netdissect_options *ndo, + const u_char *s, const u_int maxlen) { - int idx = 0; + u_int idx = 0; while (*s && idx < maxlen) { - safeputchar(*s); - idx++; + safeputchar(ndo, *s); + idx++; s++; } } void -safeputchar(int c) +safeputchar(netdissect_options *ndo, + const u_char c) { - unsigned char ch; + ND_PRINT((ndo, (c < 0x80 && ND_ISPRINT(c)) ? "%c" : "\\0x%02x", c)); +} + +#ifdef LBL_ALIGN +/* + * Some compilers try to optimize memcpy(), using the alignment constraint + * on the argument pointer type. by using this function, we try to avoid the + * optimization. + */ +void +unaligned_memcpy(void *p, const void *q, size_t l) +{ + memcpy(p, q, l); +} - ch = (unsigned char)(c & 0xff); - if (ch < 0x80 && isprint(ch)) - printf("%c", ch); - else - printf("\\0x%02x", ch); +/* As with memcpy(), so with memcmp(). */ +int +unaligned_memcmp(const void *p, const void *q, size_t l) +{ + return (memcmp(p, q, l)); } +#endif