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