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