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