xref: /netbsd-src/external/bsd/tcpdump/dist/util-print.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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.5 2017/09/08 14:01:13 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 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[1024+1]; /* our string buffer */
530         char *bufp = buf;
531         size_t space_left = sizeof(buf), string_size;
532         register u_int rotbit; /* this is the bit we rotate through all bitpositions */
533         register u_int tokval;
534         const char * sepstr = "";
535 
536 	while (lp != NULL && lp->s != NULL) {
537             tokval=lp->v;   /* load our first value */
538             rotbit=1;
539             while (rotbit != 0) {
540                 /*
541                  * lets AND the rotating bit with our token value
542                  * and see if we have got a match
543                  */
544 		if (tokval == (v&rotbit)) {
545                     /* ok we have found something */
546                     if (space_left <= 1)
547                         return (buf); /* only enough room left for NUL, if that */
548                     string_size = strlcpy(bufp, sepstr, space_left);
549                     if (string_size >= space_left)
550                         return (buf);    /* we ran out of room */
551                     bufp += string_size;
552                     space_left -= string_size;
553                     if (space_left <= 1)
554                         return (buf); /* only enough room left for NUL, if that */
555                     string_size = strlcpy(bufp, lp->s, 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                     sepstr = sep;
561                     break;
562                 }
563                 rotbit=rotbit<<1; /* no match - lets shift and try again */
564             }
565             lp++;
566 	}
567 
568         if (bufp == buf)
569             /* bummer - lets print the "unknown" message as advised in the fmt string if we got one */
570             (void)snprintf(buf, sizeof(buf), fmt == NULL ? "#%08x" : fmt, v);
571         return (buf);
572 }
573 
574 /*
575  * Convert a bit token value to a string; use "fmt" if not found.
576  * this is useful for parsing bitfields, the output strings are not seperated.
577  */
578 char *
579 bittok2str_nosep(register const struct tok *lp, register const char *fmt,
580 	   register u_int v)
581 {
582     return (bittok2str_internal(lp, fmt, v, ""));
583 }
584 
585 /*
586  * Convert a bit token value to a string; use "fmt" if not found.
587  * this is useful for parsing bitfields, the output strings are comma seperated.
588  */
589 char *
590 bittok2str(register const struct tok *lp, register const char *fmt,
591 	   register u_int v)
592 {
593     return (bittok2str_internal(lp, fmt, v, ", "));
594 }
595 
596 /*
597  * Convert a value to a string using an array; the macro
598  * tok2strary() in <netdissect.h> is the public interface to
599  * this function and ensures that the second argument is
600  * correct for bounds-checking.
601  */
602 const char *
603 tok2strary_internal(register const char **lp, int n, register const char *fmt,
604 	register int v)
605 {
606 	static char buf[TOKBUFSIZE];
607 
608 	if (v >= 0 && v < n && lp[v] != NULL)
609 		return lp[v];
610 	if (fmt == NULL)
611 		fmt = "#%d";
612 	(void)snprintf(buf, sizeof(buf), fmt, v);
613 	return (buf);
614 }
615 
616 /*
617  * Convert a 32-bit netmask to prefixlen if possible
618  * the function returns the prefix-len; if plen == -1
619  * then conversion was not possible;
620  */
621 
622 int
623 mask2plen(uint32_t mask)
624 {
625 	uint32_t bitmasks[33] = {
626 		0x00000000,
627 		0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
628 		0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
629 		0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
630 		0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
631 		0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
632 		0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
633 		0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
634 		0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff
635 	};
636 	int prefix_len = 32;
637 
638 	/* let's see if we can transform the mask into a prefixlen */
639 	while (prefix_len >= 0) {
640 		if (bitmasks[prefix_len] == mask)
641 			break;
642 		prefix_len--;
643 	}
644 	return (prefix_len);
645 }
646 
647 int
648 mask62plen(const u_char *mask)
649 {
650 	u_char bitmasks[9] = {
651 		0x00,
652 		0x80, 0xc0, 0xe0, 0xf0,
653 		0xf8, 0xfc, 0xfe, 0xff
654 	};
655 	int byte;
656 	int cidr_len = 0;
657 
658 	for (byte = 0; byte < 16; byte++) {
659 		u_int bits;
660 
661 		for (bits = 0; bits < (sizeof (bitmasks) / sizeof (bitmasks[0])); bits++) {
662 			if (mask[byte] == bitmasks[bits]) {
663 				cidr_len += bits;
664 				break;
665 			}
666 		}
667 
668 		if (mask[byte] != 0xff)
669 			break;
670 	}
671 	return (cidr_len);
672 }
673 
674 /*
675  * Routine to print out information for text-based protocols such as FTP,
676  * HTTP, SMTP, RTSP, SIP, ....
677  */
678 #define MAX_TOKEN	128
679 
680 /*
681  * Fetch a token from a packet, starting at the specified index,
682  * and return the length of the token.
683  *
684  * Returns 0 on error; yes, this is indistinguishable from an empty
685  * token, but an "empty token" isn't a valid token - it just means
686  * either a space character at the beginning of the line (this
687  * includes a blank line) or no more tokens remaining on the line.
688  */
689 static int
690 fetch_token(netdissect_options *ndo, const u_char *pptr, u_int idx, u_int len,
691     u_char *tbuf, size_t tbuflen)
692 {
693 	size_t toklen = 0;
694 
695 	for (; idx < len; idx++) {
696 		if (!ND_TTEST(*(pptr + idx))) {
697 			/* ran past end of captured data */
698 			return (0);
699 		}
700 		if (!isascii(*(pptr + idx))) {
701 			/* not an ASCII character */
702 			return (0);
703 		}
704 		if (isspace(*(pptr + idx))) {
705 			/* end of token */
706 			break;
707 		}
708 		if (!isprint(*(pptr + idx))) {
709 			/* not part of a command token or response code */
710 			return (0);
711 		}
712 		if (toklen + 2 > tbuflen) {
713 			/* no room for this character and terminating '\0' */
714 			return (0);
715 		}
716 		tbuf[toklen] = *(pptr + idx);
717 		toklen++;
718 	}
719 	if (toklen == 0) {
720 		/* no token */
721 		return (0);
722 	}
723 	tbuf[toklen] = '\0';
724 
725 	/*
726 	 * Skip past any white space after the token, until we see
727 	 * an end-of-line (CR or LF).
728 	 */
729 	for (; idx < len; idx++) {
730 		if (!ND_TTEST(*(pptr + idx))) {
731 			/* ran past end of captured data */
732 			break;
733 		}
734 		if (*(pptr + idx) == '\r' || *(pptr + idx) == '\n') {
735 			/* end of line */
736 			break;
737 		}
738 		if (!isascii(*(pptr + idx)) || !isprint(*(pptr + idx))) {
739 			/* not a printable ASCII character */
740 			break;
741 		}
742 		if (!isspace(*(pptr + idx))) {
743 			/* beginning of next token */
744 			break;
745 		}
746 	}
747 	return (idx);
748 }
749 
750 /*
751  * Scan a buffer looking for a line ending - LF or CR-LF.
752  * Return the index of the character after the line ending or 0 if
753  * we encounter a non-ASCII or non-printable character or don't find
754  * the line ending.
755  */
756 static u_int
757 print_txt_line(netdissect_options *ndo, const char *protoname,
758     const char *prefix, const u_char *pptr, u_int idx, u_int len)
759 {
760 	u_int startidx;
761 	u_int linelen;
762 
763 	startidx = idx;
764 	while (idx < len) {
765 		ND_TCHECK(*(pptr+idx));
766 		if (*(pptr+idx) == '\n') {
767 			/*
768 			 * LF without CR; end of line.
769 			 * Skip the LF and print the line, with the
770 			 * exception of the LF.
771 			 */
772 			linelen = idx - startidx;
773 			idx++;
774 			goto print;
775 		} else if (*(pptr+idx) == '\r') {
776 			/* CR - any LF? */
777 			if ((idx+1) >= len) {
778 				/* not in this packet */
779 				return (0);
780 			}
781 			ND_TCHECK(*(pptr+idx+1));
782 			if (*(pptr+idx+1) == '\n') {
783 				/*
784 				 * CR-LF; end of line.
785 				 * Skip the CR-LF and print the line, with
786 				 * the exception of the CR-LF.
787 				 */
788 				linelen = idx - startidx;
789 				idx += 2;
790 				goto print;
791 			}
792 
793 			/*
794 			 * CR followed by something else; treat this
795 			 * as if it were binary data, and don't print
796 			 * it.
797 			 */
798 			return (0);
799 		} else if (!isascii(*(pptr+idx)) ||
800 		    (!isprint(*(pptr+idx)) && *(pptr+idx) != '\t')) {
801 			/*
802 			 * Not a printable ASCII character and not a tab;
803 			 * treat this as if it were binary data, and
804 			 * don't print it.
805 			 */
806 			return (0);
807 		}
808 		idx++;
809 	}
810 
811 	/*
812 	 * All printable ASCII, but no line ending after that point
813 	 * in the buffer; treat this as if it were truncated.
814 	 */
815 trunc:
816 	linelen = idx - startidx;
817 	ND_PRINT((ndo, "%s%.*s[!%s]", prefix, (int)linelen, pptr + startidx,
818 	    protoname));
819 	return (0);
820 
821 print:
822 	ND_PRINT((ndo, "%s%.*s", prefix, (int)linelen, pptr + startidx));
823 	return (idx);
824 }
825 
826 void
827 txtproto_print(netdissect_options *ndo, const u_char *pptr, u_int len,
828     const char *protoname, const char **cmds, u_int flags)
829 {
830 	u_int idx, eol;
831 	u_char token[MAX_TOKEN+1];
832 	const char *cmd;
833 	int is_reqresp = 0;
834 	const char *pnp;
835 
836 	if (cmds != NULL) {
837 		/*
838 		 * This protocol has more than just request and
839 		 * response lines; see whether this looks like a
840 		 * request or response.
841 		 */
842 		idx = fetch_token(ndo, pptr, 0, len, token, sizeof(token));
843 		if (idx != 0) {
844 			/* Is this a valid request name? */
845 			while ((cmd = *cmds++) != NULL) {
846 				if (ascii_strcasecmp((const char *)token, cmd) == 0) {
847 					/* Yes. */
848 					is_reqresp = 1;
849 					break;
850 				}
851 			}
852 
853 			/*
854 			 * No - is this a valid response code (3 digits)?
855 			 *
856 			 * Is this token the response code, or is the next
857 			 * token the response code?
858 			 */
859 			if (flags & RESP_CODE_SECOND_TOKEN) {
860 				/*
861 				 * Next token - get it.
862 				 */
863 				idx = fetch_token(ndo, pptr, idx, len, token,
864 				    sizeof(token));
865 			}
866 			if (idx != 0) {
867 				if (isdigit(token[0]) && isdigit(token[1]) &&
868 				    isdigit(token[2]) && token[3] == '\0') {
869 					/* Yes. */
870 					is_reqresp = 1;
871 				}
872 			}
873 		}
874 	} else {
875 		/*
876 		 * This protocol has only request and response lines
877 		 * (e.g., FTP, where all the data goes over a
878 		 * different connection); assume the payload is
879 		 * a request or response.
880 		 */
881 		is_reqresp = 1;
882 	}
883 
884 	/* Capitalize the protocol name */
885 	for (pnp = protoname; *pnp != '\0'; pnp++)
886 		ND_PRINT((ndo, "%c", toupper((unsigned char)*pnp)));
887 
888 	if (is_reqresp) {
889 		/*
890 		 * In non-verbose mode, just print the protocol, followed
891 		 * by the first line as the request or response info.
892 		 *
893 		 * In verbose mode, print lines as text until we run out
894 		 * of characters or see something that's not a
895 		 * printable-ASCII line.
896 		 */
897 		if (ndo->ndo_vflag) {
898 			/*
899 			 * We're going to print all the text lines in the
900 			 * request or response; just print the length
901 			 * on the first line of the output.
902 			 */
903 			ND_PRINT((ndo, ", length: %u", len));
904 			for (idx = 0;
905 			    idx < len && (eol = print_txt_line(ndo, protoname, "\n\t", pptr, idx, len)) != 0;
906 			    idx = eol)
907 				;
908 		} else {
909 			/*
910 			 * Just print the first text line.
911 			 */
912 			print_txt_line(ndo, protoname, ": ", pptr, 0, len);
913 		}
914 	}
915 }
916 
917 void
918 safeputs(netdissect_options *ndo,
919          const u_char *s, const u_int maxlen)
920 {
921 	u_int idx = 0;
922 
923 	while (idx < maxlen && *s) {
924 		safeputchar(ndo, *s);
925 		idx++;
926 		s++;
927 	}
928 }
929 
930 void
931 safeputchar(netdissect_options *ndo,
932             const u_char c)
933 {
934 	ND_PRINT((ndo, (c < 0x80 && ND_ISPRINT(c)) ? "%c" : "\\0x%02x", c));
935 }
936 
937 #ifdef LBL_ALIGN
938 /*
939  * Some compilers try to optimize memcpy(), using the alignment constraint
940  * on the argument pointer type.  by using this function, we try to avoid the
941  * optimization.
942  */
943 void
944 unaligned_memcpy(void *p, const void *q, size_t l)
945 {
946 	memcpy(p, q, l);
947 }
948 
949 /* As with memcpy(), so with memcmp(). */
950 int
951 unaligned_memcmp(const void *p, const void *q, size_t l)
952 {
953 	return (memcmp(p, q, l));
954 }
955 #endif
956 
957