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