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