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