Home | History | Annotate | Download | only in tcpdump
      1 /*
      2  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
      3  *	The Regents of the University of California.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that: (1) source code distributions
      7  * retain the above copyright notice and this paragraph in its entirety, (2)
      8  * distributions including binary code include the above copyright notice and
      9  * this paragraph in its entirety in the documentation or other materials
     10  * provided with the distribution, and (3) all advertising materials mentioning
     11  * features or use of this software display the following acknowledgement:
     12  * ``This product includes software developed by the University of California,
     13  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
     14  * the University nor the names of its contributors may be used to endorse
     15  * or promote products derived from this software without specific prior
     16  * written permission.
     17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
     18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
     19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     20  */
     21 
     22 /*
     23  * txtproto_print() derived from original code by Hannes Gredler
     24  * (hannes (at) gredler.at):
     25  *
     26  * Redistribution and use in source and binary forms, with or without
     27  * modification, are permitted provided that: (1) source code
     28  * distributions retain the above copyright notice and this paragraph
     29  * in its entirety, and (2) distributions including binary code include
     30  * the above copyright notice and this paragraph in its entirety in
     31  * the documentation or other materials provided with the distribution.
     32  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
     33  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
     34  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     35  * FOR A PARTICULAR PURPOSE.
     36  */
     37 
     38 #ifdef HAVE_CONFIG_H
     39 #include "config.h"
     40 #endif
     41 
     42 #include <netdissect-stdinc.h>
     43 
     44 #include <sys/stat.h>
     45 
     46 #ifdef HAVE_FCNTL_H
     47 #include <fcntl.h>
     48 #endif
     49 #include <ctype.h>
     50 #include <stdio.h>
     51 #include <stdarg.h>
     52 #include <stdlib.h>
     53 #include <string.h>
     54 
     55 #include "netdissect.h"
     56 #include "ascii_strcasecmp.h"
     57 #include "timeval-operations.h"
     58 
     59 int32_t thiszone;		/* seconds offset from gmt to local time */
     60 /* invalid string to print '(invalid)' for malformed or corrupted packets */
     61 const char istr[] = " (invalid)";
     62 
     63 /*
     64  * timestamp display buffer size, the biggest size of both formats is needed
     65  * sizeof("0000000000.000000000") > sizeof("00:00:00.000000000")
     66  */
     67 #define TS_BUF_SIZE sizeof("0000000000.000000000")
     68 
     69 #define TOKBUFSIZE 128
     70 
     71 /*
     72  * Print out a character, filtering out the non-printable ones
     73  */
     74 void
     75 fn_print_char(netdissect_options *ndo, u_char c)
     76 {
     77 	if (!ND_ISASCII(c)) {
     78 		c = ND_TOASCII(c);
     79 		ND_PRINT((ndo, "M-"));
     80 	}
     81 	if (!ND_ISPRINT(c)) {
     82 		c ^= 0x40;	/* DEL to ?, others to alpha */
     83 		ND_PRINT((ndo, "^"));
     84 	}
     85 	ND_PRINT((ndo, "%c", c));
     86 }
     87 
     88 /*
     89  * Print out a null-terminated filename (or other ascii string).
     90  * If ep is NULL, assume no truncation check is needed.
     91  * Return true if truncated.
     92  * Stop at ep (if given) or before the null char, whichever is first.
     93  */
     94 int
     95 fn_print(netdissect_options *ndo,
     96          register const u_char *s, register const u_char *ep)
     97 {
     98 	register int ret;
     99 	register u_char c;
    100 
    101 	ret = 1;			/* assume truncated */
    102 	while (ep == NULL || s < ep) {
    103 		c = *s++;
    104 		if (c == '\0') {
    105 			ret = 0;
    106 			break;
    107 		}
    108 		if (!ND_ISASCII(c)) {
    109 			c = ND_TOASCII(c);
    110 			ND_PRINT((ndo, "M-"));
    111 		}
    112 		if (!ND_ISPRINT(c)) {
    113 			c ^= 0x40;	/* DEL to ?, others to alpha */
    114 			ND_PRINT((ndo, "^"));
    115 		}
    116 		ND_PRINT((ndo, "%c", c));
    117 	}
    118 	return(ret);
    119 }
    120 
    121 /*
    122  * Print out a null-terminated filename (or other ascii string) from
    123  * a fixed-length buffer.
    124  * If ep is NULL, assume no truncation check is needed.
    125  * Return the number of bytes of string processed, including the
    126  * terminating null, if not truncated.  Return 0 if truncated.
    127  */
    128 u_int
    129 fn_printztn(netdissect_options *ndo,
    130          register const u_char *s, register u_int n, register const u_char *ep)
    131 {
    132 	register u_int bytes;
    133 	register u_char c;
    134 
    135 	bytes = 0;
    136 	for (;;) {
    137 		if (n == 0 || (ep != NULL && s >= ep)) {
    138 			/*
    139 			 * Truncated.  This includes "no null before we
    140 			 * got to the end of the fixed-length buffer".
    141 			 *
    142 			 * XXX - BOOTP says "null-terminated", which
    143 			 * means the maximum length of the string, in
    144 			 * bytes, is 1 less than the size of the buffer,
    145 			 * as there must always be a terminating null.
    146 			 */
    147 			bytes = 0;
    148 			break;
    149 		}
    150 
    151 		c = *s++;
    152 		bytes++;
    153 		n--;
    154 		if (c == '\0') {
    155 			/* End of string */
    156 			break;
    157 		}
    158 		if (!ND_ISASCII(c)) {
    159 			c = ND_TOASCII(c);
    160 			ND_PRINT((ndo, "M-"));
    161 		}
    162 		if (!ND_ISPRINT(c)) {
    163 			c ^= 0x40;	/* DEL to ?, others to alpha */
    164 			ND_PRINT((ndo, "^"));
    165 		}
    166 		ND_PRINT((ndo, "%c", c));
    167 	}
    168 	return(bytes);
    169 }
    170 
    171 /*
    172  * Print out a counted filename (or other ascii string).
    173  * If ep is NULL, assume no truncation check is needed.
    174  * Return true if truncated.
    175  * Stop at ep (if given) or after n bytes, whichever is first.
    176  */
    177 int
    178 fn_printn(netdissect_options *ndo,
    179           register const u_char *s, register u_int n, register const u_char *ep)
    180 {
    181 	register u_char c;
    182 
    183 	while (n > 0 && (ep == NULL || s < ep)) {
    184 		n--;
    185 		c = *s++;
    186 		if (!ND_ISASCII(c)) {
    187 			c = ND_TOASCII(c);
    188 			ND_PRINT((ndo, "M-"));
    189 		}
    190 		if (!ND_ISPRINT(c)) {
    191 			c ^= 0x40;	/* DEL to ?, others to alpha */
    192 			ND_PRINT((ndo, "^"));
    193 		}
    194 		ND_PRINT((ndo, "%c", c));
    195 	}
    196 	return (n == 0) ? 0 : 1;
    197 }
    198 
    199 /*
    200  * Print out a null-padded filename (or other ascii string).
    201  * If ep is NULL, assume no truncation check is needed.
    202  * Return true if truncated.
    203  * Stop at ep (if given) or after n bytes or before the null char,
    204  * whichever is first.
    205  */
    206 int
    207 fn_printzp(netdissect_options *ndo,
    208            register const u_char *s, register u_int n,
    209            register const u_char *ep)
    210 {
    211 	register int ret;
    212 	register u_char c;
    213 
    214 	ret = 1;			/* assume truncated */
    215 	while (n > 0 && (ep == NULL || s < ep)) {
    216 		n--;
    217 		c = *s++;
    218 		if (c == '\0') {
    219 			ret = 0;
    220 			break;
    221 		}
    222 		if (!ND_ISASCII(c)) {
    223 			c = ND_TOASCII(c);
    224 			ND_PRINT((ndo, "M-"));
    225 		}
    226 		if (!ND_ISPRINT(c)) {
    227 			c ^= 0x40;	/* DEL to ?, others to alpha */
    228 			ND_PRINT((ndo, "^"));
    229 		}
    230 		ND_PRINT((ndo, "%c", c));
    231 	}
    232 	return (n == 0) ? 0 : ret;
    233 }
    234 
    235 /*
    236  * Format the timestamp
    237  */
    238 static char *
    239 ts_format(netdissect_options *ndo
    240 #ifndef HAVE_PCAP_SET_TSTAMP_PRECISION
    241 _U_
    242 #endif
    243 , int sec, int usec, char *buf)
    244 {
    245 	const char *format;
    246 
    247 #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
    248 	switch (ndo->ndo_tstamp_precision) {
    249 
    250 	case PCAP_TSTAMP_PRECISION_MICRO:
    251 		format = "%02d:%02d:%02d.%06u";
    252 		break;
    253 
    254 	case PCAP_TSTAMP_PRECISION_NANO:
    255 		format = "%02d:%02d:%02d.%09u";
    256 		break;
    257 
    258 	default:
    259 		format = "%02d:%02d:%02d.{unknown}";
    260 		break;
    261 	}
    262 #else
    263 	format = "%02d:%02d:%02d.%06u";
    264 #endif
    265 
    266 	snprintf(buf, TS_BUF_SIZE, format,
    267                  sec / 3600, (sec % 3600) / 60, sec % 60, usec);
    268 
    269         return buf;
    270 }
    271 
    272 /*
    273  * Format the timestamp - Unix timeval style
    274  */
    275 static char *
    276 ts_unix_format(netdissect_options *ndo
    277 #ifndef HAVE_PCAP_SET_TSTAMP_PRECISION
    278 _U_
    279 #endif
    280 , int sec, int usec, char *buf)
    281 {
    282 	const char *format;
    283 
    284 #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
    285 	switch (ndo->ndo_tstamp_precision) {
    286 
    287 	case PCAP_TSTAMP_PRECISION_MICRO:
    288 		format = "%u.%06u";
    289 		break;
    290 
    291 	case PCAP_TSTAMP_PRECISION_NANO:
    292 		format = "%u.%09u";
    293 		break;
    294 
    295 	default:
    296 		format = "%u.{unknown}";
    297 		break;
    298 	}
    299 #else
    300 	format = "%u.%06u";
    301 #endif
    302 
    303 	snprintf(buf, TS_BUF_SIZE, format,
    304 		 (unsigned)sec, (unsigned)usec);
    305 
    306 	return buf;
    307 }
    308 
    309 /*
    310  * Print the timestamp
    311  */
    312 void
    313 ts_print(netdissect_options *ndo,
    314          register const struct timeval *tvp)
    315 {
    316 	register int s;
    317 	struct tm *tm;
    318 	time_t Time;
    319 	char buf[TS_BUF_SIZE];
    320 	static struct timeval tv_ref;
    321 	struct timeval tv_result;
    322 	int negative_offset;
    323 	int nano_prec;
    324 
    325 	switch (ndo->ndo_tflag) {
    326 
    327 	case 0: /* Default */
    328 		s = (tvp->tv_sec + thiszone) % 86400;
    329 		ND_PRINT((ndo, "%s ", ts_format(ndo, s, tvp->tv_usec, buf)));
    330 		break;
    331 
    332 	case 1: /* No time stamp */
    333 		break;
    334 
    335 	case 2: /* Unix timeval style */
    336 		ND_PRINT((ndo, "%s ", ts_unix_format(ndo,
    337 			  tvp->tv_sec, tvp->tv_usec, buf)));
    338 		break;
    339 
    340 	case 3: /* Microseconds/nanoseconds since previous packet */
    341         case 5: /* Microseconds/nanoseconds since first packet */
    342 #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
    343 		switch (ndo->ndo_tstamp_precision) {
    344 		case PCAP_TSTAMP_PRECISION_MICRO:
    345 			nano_prec = 0;
    346 			break;
    347 		case PCAP_TSTAMP_PRECISION_NANO:
    348 			nano_prec = 1;
    349 			break;
    350 		default:
    351 			nano_prec = 0;
    352 			break;
    353 		}
    354 #else
    355 		nano_prec = 0;
    356 #endif
    357 		if (!(netdissect_timevalisset(&tv_ref)))
    358 			tv_ref = *tvp; /* set timestamp for first packet */
    359 
    360 		negative_offset = netdissect_timevalcmp(tvp, &tv_ref, <);
    361 		if (negative_offset)
    362 			netdissect_timevalsub(&tv_ref, tvp, &tv_result, nano_prec);
    363 		else
    364 			netdissect_timevalsub(tvp, &tv_ref, &tv_result, nano_prec);
    365 
    366 		ND_PRINT((ndo, (negative_offset ? "-" : " ")));
    367 
    368 		ND_PRINT((ndo, "%s ", ts_format(ndo,
    369 			  tv_result.tv_sec, tv_result.tv_usec, buf)));
    370 
    371                 if (ndo->ndo_tflag == 3)
    372 			tv_ref = *tvp; /* set timestamp for previous packet */
    373 		break;
    374 
    375 	case 4: /* Default + Date */
    376 		s = (tvp->tv_sec + thiszone) % 86400;
    377 		Time = (tvp->tv_sec + thiszone) - s;
    378 		tm = gmtime (&Time);
    379 		if (!tm)
    380 			ND_PRINT((ndo, "Date fail  "));
    381 		else
    382 			ND_PRINT((ndo, "%04d-%02d-%02d %s ",
    383                                tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
    384                                ts_format(ndo, s, tvp->tv_usec, buf)));
    385 		break;
    386 	}
    387 }
    388 
    389 /*
    390  * Print an unsigned relative number of seconds (e.g. hold time, prune timer)
    391  * in the form 5m1s.  This does no truncation, so 32230861 seconds
    392  * is represented as 1y1w1d1h1m1s.
    393  */
    394 void
    395 unsigned_relts_print(netdissect_options *ndo,
    396                      uint32_t secs)
    397 {
    398 	static const char *lengths[] = {"y", "w", "d", "h", "m", "s"};
    399 	static const u_int seconds[] = {31536000, 604800, 86400, 3600, 60, 1};
    400 	const char **l = lengths;
    401 	const u_int *s = seconds;
    402 
    403 	if (secs == 0) {
    404 		ND_PRINT((ndo, "0s"));
    405 		return;
    406 	}
    407 	while (secs > 0) {
    408 		if (secs >= *s) {
    409 			ND_PRINT((ndo, "%d%s", secs / *s, *l));
    410 			secs -= (secs / *s) * *s;
    411 		}
    412 		s++;
    413 		l++;
    414 	}
    415 }
    416 
    417 /*
    418  * Print a signed relative number of seconds (e.g. hold time, prune timer)
    419  * in the form 5m1s.  This does no truncation, so 32230861 seconds
    420  * is represented as 1y1w1d1h1m1s.
    421  */
    422 void
    423 signed_relts_print(netdissect_options *ndo,
    424                    int32_t secs)
    425 {
    426 	if (secs < 0) {
    427 		ND_PRINT((ndo, "-"));
    428 		if (secs == INT32_MIN) {
    429 			/*
    430 			 * -2^31; you can't fit its absolute value into
    431 			 * a 32-bit signed integer.
    432 			 *
    433 			 * Just directly pass said absolute value to
    434 			 * unsigned_relts_print() directly.
    435 			 *
    436 			 * (XXX - does ISO C guarantee that -(-2^n),
    437 			 * when calculated and cast to an n-bit unsigned
    438 			 * integer type, will have the value 2^n?)
    439 			 */
    440 			unsigned_relts_print(ndo, 2147483648U);
    441 		} else {
    442 			/*
    443 			 * We now know -secs will fit into an int32_t;
    444 			 * negate it and pass that to unsigned_relts_print().
    445 			 */
    446 			unsigned_relts_print(ndo, -secs);
    447 		}
    448 		return;
    449 	}
    450 	unsigned_relts_print(ndo, secs);
    451 }
    452 
    453 /*
    454  *  this is a generic routine for printing unknown data;
    455  *  we pass on the linefeed plus indentation string to
    456  *  get a proper output - returns 0 on error
    457  */
    458 
    459 int
    460 print_unknown_data(netdissect_options *ndo, const u_char *cp,const char *ident,int len)
    461 {
    462 	if (len < 0) {
    463           ND_PRINT((ndo,"%sDissector error: print_unknown_data called with negative length",
    464 		    ident));
    465 		return(0);
    466 	}
    467 	if (ndo->ndo_snapend - cp < len)
    468 		len = ndo->ndo_snapend - cp;
    469 	if (len < 0) {
    470           ND_PRINT((ndo,"%sDissector error: print_unknown_data called with pointer past end of packet",
    471 		    ident));
    472 		return(0);
    473 	}
    474         hex_print(ndo, ident,cp,len);
    475 	return(1); /* everything is ok */
    476 }
    477 
    478 /*
    479  * Convert a token value to a string; use "fmt" if not found.
    480  */
    481 const char *
    482 tok2strbuf(register const struct tok *lp, register const char *fmt,
    483 	   register u_int v, char *buf, size_t bufsize)
    484 {
    485 	if (lp != NULL) {
    486 		while (lp->s != NULL) {
    487 			if (lp->v == v)
    488 				return (lp->s);
    489 			++lp;
    490 		}
    491 	}
    492 	if (fmt == NULL)
    493 		fmt = "#%d";
    494 
    495 	(void)snprintf(buf, bufsize, fmt, v);
    496 	return (const char *)buf;
    497 }
    498 
    499 /*
    500  * Convert a token value to a string; use "fmt" if not found.
    501  */
    502 const char *
    503 tok2str(register const struct tok *lp, register const char *fmt,
    504 	register u_int v)
    505 {
    506 	static char buf[4][TOKBUFSIZE];
    507 	static int idx = 0;
    508 	char *ret;
    509 
    510 	ret = buf[idx];
    511 	idx = (idx+1) & 3;
    512 	return tok2strbuf(lp, fmt, v, ret, sizeof(buf[0]));
    513 }
    514 
    515 /*
    516  * Convert a bit token value to a string; use "fmt" if not found.
    517  * this is useful for parsing bitfields, the output strings are seperated
    518  * if the s field is positive.
    519  */
    520 static char *
    521 bittok2str_internal(register const struct tok *lp, register const char *fmt,
    522 	   register u_int v, const char *sep)
    523 {
    524         static char buf[1024+1]; /* our string buffer */
    525         char *bufp = buf;
    526         size_t space_left = sizeof(buf), string_size;
    527         register u_int rotbit; /* this is the bit we rotate through all bitpositions */
    528         register u_int tokval;
    529         const char * sepstr = "";
    530 
    531 	while (lp != NULL && lp->s != NULL) {
    532             tokval=lp->v;   /* load our first value */
    533             rotbit=1;
    534             while (rotbit != 0) {
    535                 /*
    536                  * lets AND the rotating bit with our token value
    537                  * and see if we have got a match
    538                  */
    539 		if (tokval == (v&rotbit)) {
    540                     /* ok we have found something */
    541                     if (space_left <= 1)
    542                         return (buf); /* only enough room left for NUL, if that */
    543                     string_size = strlcpy(bufp, sepstr, space_left);
    544                     if (string_size >= space_left)
    545                         return (buf);    /* we ran out of room */
    546                     bufp += string_size;
    547                     space_left -= string_size;
    548                     if (space_left <= 1)
    549                         return (buf); /* only enough room left for NUL, if that */
    550                     string_size = strlcpy(bufp, lp->s, space_left);
    551                     if (string_size >= space_left)
    552                         return (buf);    /* we ran out of room */
    553                     bufp += string_size;
    554                     space_left -= string_size;
    555                     sepstr = sep;
    556                     break;
    557                 }
    558                 rotbit=rotbit<<1; /* no match - lets shift and try again */
    559             }
    560             lp++;
    561 	}
    562 
    563         if (bufp == buf)
    564             /* bummer - lets print the "unknown" message as advised in the fmt string if we got one */
    565             (void)snprintf(buf, sizeof(buf), fmt == NULL ? "#%08x" : fmt, v);
    566         return (buf);
    567 }
    568 
    569 /*
    570  * Convert a bit token value to a string; use "fmt" if not found.
    571  * this is useful for parsing bitfields, the output strings are not seperated.
    572  */
    573 char *
    574 bittok2str_nosep(register const struct tok *lp, register const char *fmt,
    575 	   register u_int v)
    576 {
    577     return (bittok2str_internal(lp, fmt, v, ""));
    578 }
    579 
    580 /*
    581  * Convert a bit token value to a string; use "fmt" if not found.
    582  * this is useful for parsing bitfields, the output strings are comma seperated.
    583  */
    584 char *
    585 bittok2str(register const struct tok *lp, register const char *fmt,
    586 	   register u_int v)
    587 {
    588     return (bittok2str_internal(lp, fmt, v, ", "));
    589 }
    590 
    591 /*
    592  * Convert a value to a string using an array; the macro
    593  * tok2strary() in <netdissect.h> is the public interface to
    594  * this function and ensures that the second argument is
    595  * correct for bounds-checking.
    596  */
    597 const char *
    598 tok2strary_internal(register const char **lp, int n, register const char *fmt,
    599 	register int v)
    600 {
    601 	static char buf[TOKBUFSIZE];
    602 
    603 	if (v >= 0 && v < n && lp[v] != NULL)
    604 		return lp[v];
    605 	if (fmt == NULL)
    606 		fmt = "#%d";
    607 	(void)snprintf(buf, sizeof(buf), fmt, v);
    608 	return (buf);
    609 }
    610 
    611 /*
    612  * Convert a 32-bit netmask to prefixlen if possible
    613  * the function returns the prefix-len; if plen == -1
    614  * then conversion was not possible;
    615  */
    616 
    617 int
    618 mask2plen(uint32_t mask)
    619 {
    620 	uint32_t bitmasks[33] = {
    621 		0x00000000,
    622 		0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
    623 		0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
    624 		0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
    625 		0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
    626 		0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
    627 		0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
    628 		0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
    629 		0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff
    630 	};
    631 	int prefix_len = 32;
    632 
    633 	/* let's see if we can transform the mask into a prefixlen */
    634 	while (prefix_len >= 0) {
    635 		if (bitmasks[prefix_len] == mask)
    636 			break;
    637 		prefix_len--;
    638 	}
    639 	return (prefix_len);
    640 }
    641 
    642 int
    643 mask62plen(const u_char *mask)
    644 {
    645 	u_char bitmasks[9] = {
    646 		0x00,
    647 		0x80, 0xc0, 0xe0, 0xf0,
    648 		0xf8, 0xfc, 0xfe, 0xff
    649 	};
    650 	int byte;
    651 	int cidr_len = 0;
    652 
    653 	for (byte = 0; byte < 16; byte++) {
    654 		u_int bits;
    655 
    656 		for (bits = 0; bits < (sizeof (bitmasks) / sizeof (bitmasks[0])); bits++) {
    657 			if (mask[byte] == bitmasks[bits]) {
    658 				cidr_len += bits;
    659 				break;
    660 			}
    661 		}
    662 
    663 		if (mask[byte] != 0xff)
    664 			break;
    665 	}
    666 	return (cidr_len);
    667 }
    668 
    669 /*
    670  * Routine to print out information for text-based protocols such as FTP,
    671  * HTTP, SMTP, RTSP, SIP, ....
    672  */
    673 #define MAX_TOKEN	128
    674 
    675 /*
    676  * Fetch a token from a packet, starting at the specified index,
    677  * and return the length of the token.
    678  *
    679  * Returns 0 on error; yes, this is indistinguishable from an empty
    680  * token, but an "empty token" isn't a valid token - it just means
    681  * either a space character at the beginning of the line (this
    682  * includes a blank line) or no more tokens remaining on the line.
    683  */
    684 static int
    685 fetch_token(netdissect_options *ndo, const u_char *pptr, u_int idx, u_int len,
    686     u_char *tbuf, size_t tbuflen)
    687 {
    688 	size_t toklen = 0;
    689 
    690 	for (; idx < len; idx++) {
    691 		if (!ND_TTEST(*(pptr + idx))) {
    692 			/* ran past end of captured data */
    693 			return (0);
    694 		}
    695 		if (!isascii(*(pptr + idx))) {
    696 			/* not an ASCII character */
    697 			return (0);
    698 		}
    699 		if (isspace(*(pptr + idx))) {
    700 			/* end of token */
    701 			break;
    702 		}
    703 		if (!isprint(*(pptr + idx))) {
    704 			/* not part of a command token or response code */
    705 			return (0);
    706 		}
    707 		if (toklen + 2 > tbuflen) {
    708 			/* no room for this character and terminating '\0' */
    709 			return (0);
    710 		}
    711 		tbuf[toklen] = *(pptr + idx);
    712 		toklen++;
    713 	}
    714 	if (toklen == 0) {
    715 		/* no token */
    716 		return (0);
    717 	}
    718 	tbuf[toklen] = '\0';
    719 
    720 	/*
    721 	 * Skip past any white space after the token, until we see
    722 	 * an end-of-line (CR or LF).
    723 	 */
    724 	for (; idx < len; idx++) {
    725 		if (!ND_TTEST(*(pptr + idx))) {
    726 			/* ran past end of captured data */
    727 			break;
    728 		}
    729 		if (*(pptr + idx) == '\r' || *(pptr + idx) == '\n') {
    730 			/* end of line */
    731 			break;
    732 		}
    733 		if (!isascii(*(pptr + idx)) || !isprint(*(pptr + idx))) {
    734 			/* not a printable ASCII character */
    735 			break;
    736 		}
    737 		if (!isspace(*(pptr + idx))) {
    738 			/* beginning of next token */
    739 			break;
    740 		}
    741 	}
    742 	return (idx);
    743 }
    744 
    745 /*
    746  * Scan a buffer looking for a line ending - LF or CR-LF.
    747  * Return the index of the character after the line ending or 0 if
    748  * we encounter a non-ASCII or non-printable character or don't find
    749  * the line ending.
    750  */
    751 static u_int
    752 print_txt_line(netdissect_options *ndo, const char *protoname,
    753     const char *prefix, const u_char *pptr, u_int idx, u_int len)
    754 {
    755 	u_int startidx;
    756 	u_int linelen;
    757 
    758 	startidx = idx;
    759 	while (idx < len) {
    760 		ND_TCHECK(*(pptr+idx));
    761 		if (*(pptr+idx) == '\n') {
    762 			/*
    763 			 * LF without CR; end of line.
    764 			 * Skip the LF and print the line, with the
    765 			 * exception of the LF.
    766 			 */
    767 			linelen = idx - startidx;
    768 			idx++;
    769 			goto print;
    770 		} else if (*(pptr+idx) == '\r') {
    771 			/* CR - any LF? */
    772 			if ((idx+1) >= len) {
    773 				/* not in this packet */
    774 				return (0);
    775 			}
    776 			ND_TCHECK(*(pptr+idx+1));
    777 			if (*(pptr+idx+1) == '\n') {
    778 				/*
    779 				 * CR-LF; end of line.
    780 				 * Skip the CR-LF and print the line, with
    781 				 * the exception of the CR-LF.
    782 				 */
    783 				linelen = idx - startidx;
    784 				idx += 2;
    785 				goto print;
    786 			}
    787 
    788 			/*
    789 			 * CR followed by something else; treat this
    790 			 * as if it were binary data, and don't print
    791 			 * it.
    792 			 */
    793 			return (0);
    794 		} else if (!isascii(*(pptr+idx)) ||
    795 		    (!isprint(*(pptr+idx)) && *(pptr+idx) != '\t')) {
    796 			/*
    797 			 * Not a printable ASCII character and not a tab;
    798 			 * treat this as if it were binary data, and
    799 			 * don't print it.
    800 			 */
    801 			return (0);
    802 		}
    803 		idx++;
    804 	}
    805 
    806 	/*
    807 	 * All printable ASCII, but no line ending after that point
    808 	 * in the buffer; treat this as if it were truncated.
    809 	 */
    810 trunc:
    811 	linelen = idx - startidx;
    812 	ND_PRINT((ndo, "%s%.*s[!%s]", prefix, (int)linelen, pptr + startidx,
    813 	    protoname));
    814 	return (0);
    815 
    816 print:
    817 	ND_PRINT((ndo, "%s%.*s", prefix, (int)linelen, pptr + startidx));
    818 	return (idx);
    819 }
    820 
    821 void
    822 txtproto_print(netdissect_options *ndo, const u_char *pptr, u_int len,
    823     const char *protoname, const char **cmds, u_int flags)
    824 {
    825 	u_int idx, eol;
    826 	u_char token[MAX_TOKEN+1];
    827 	const char *cmd;
    828 	int is_reqresp = 0;
    829 	const char *pnp;
    830 
    831 	if (cmds != NULL) {
    832 		/*
    833 		 * This protocol has more than just request and
    834 		 * response lines; see whether this looks like a
    835 		 * request or response.
    836 		 */
    837 		idx = fetch_token(ndo, pptr, 0, len, token, sizeof(token));
    838 		if (idx != 0) {
    839 			/* Is this a valid request name? */
    840 			while ((cmd = *cmds++) != NULL) {
    841 				if (ascii_strcasecmp((const char *)token, cmd) == 0) {
    842 					/* Yes. */
    843 					is_reqresp = 1;
    844 					break;
    845 				}
    846 			}
    847 
    848 			/*
    849 			 * No - is this a valid response code (3 digits)?
    850 			 *
    851 			 * Is this token the response code, or is the next
    852 			 * token the response code?
    853 			 */
    854 			if (flags & RESP_CODE_SECOND_TOKEN) {
    855 				/*
    856 				 * Next token - get it.
    857 				 */
    858 				idx = fetch_token(ndo, pptr, idx, len, token,
    859 				    sizeof(token));
    860 			}
    861 			if (idx != 0) {
    862 				if (isdigit(token[0]) && isdigit(token[1]) &&
    863 				    isdigit(token[2]) && token[3] == '\0') {
    864 					/* Yes. */
    865 					is_reqresp = 1;
    866 				}
    867 			}
    868 		}
    869 	} else {
    870 		/*
    871 		 * This protocol has only request and response lines
    872 		 * (e.g., FTP, where all the data goes over a
    873 		 * different connection); assume the payload is
    874 		 * a request or response.
    875 		 */
    876 		is_reqresp = 1;
    877 	}
    878 
    879 	/* Capitalize the protocol name */
    880 	for (pnp = protoname; *pnp != '\0'; pnp++)
    881 		ND_PRINT((ndo, "%c", toupper((u_char)*pnp)));
    882 
    883 	if (is_reqresp) {
    884 		/*
    885 		 * In non-verbose mode, just print the protocol, followed
    886 		 * by the first line as the request or response info.
    887 		 *
    888 		 * In verbose mode, print lines as text until we run out
    889 		 * of characters or see something that's not a
    890 		 * printable-ASCII line.
    891 		 */
    892 		if (ndo->ndo_vflag) {
    893 			/*
    894 			 * We're going to print all the text lines in the
    895 			 * request or response; just print the length
    896 			 * on the first line of the output.
    897 			 */
    898 			ND_PRINT((ndo, ", length: %u", len));
    899 			for (idx = 0;
    900 			    idx < len && (eol = print_txt_line(ndo, protoname, "\n\t", pptr, idx, len)) != 0;
    901 			    idx = eol)
    902 				;
    903 		} else {
    904 			/*
    905 			 * Just print the first text line.
    906 			 */
    907 			print_txt_line(ndo, protoname, ": ", pptr, 0, len);
    908 		}
    909 	}
    910 }
    911 
    912 void
    913 safeputs(netdissect_options *ndo,
    914          const u_char *s, const u_int maxlen)
    915 {
    916 	u_int idx = 0;
    917 
    918 	while (idx < maxlen && *s) {
    919 		safeputchar(ndo, *s);
    920 		idx++;
    921 		s++;
    922 	}
    923 }
    924 
    925 void
    926 safeputchar(netdissect_options *ndo,
    927             const u_char c)
    928 {
    929 	ND_PRINT((ndo, (c < 0x80 && ND_ISPRINT(c)) ? "%c" : "\\0x%02x", c));
    930 }
    931 
    932 #ifdef LBL_ALIGN
    933 /*
    934  * Some compilers try to optimize memcpy(), using the alignment constraint
    935  * on the argument pointer type.  by using this function, we try to avoid the
    936  * optimization.
    937  */
    938 void
    939 unaligned_memcpy(void *p, const void *q, size_t l)
    940 {
    941 	memcpy(p, q, l);
    942 }
    943 
    944 /* As with memcpy(), so with memcmp(). */
    945 int
    946 unaligned_memcmp(const void *p, const void *q, size_t l)
    947 {
    948 	return (memcmp(p, q, l));
    949 }
    950 #endif
    951 
    952