xref: /freebsd-src/contrib/tcpdump/util-print.c (revision 0a7e5f1f02aad2ff5fff1c60f44c6975fd07e1d9)
13340d773SGleb Smirnoff /*
23340d773SGleb Smirnoff  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
33340d773SGleb Smirnoff  *	The Regents of the University of California.  All rights reserved.
43340d773SGleb Smirnoff  *
53340d773SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
63340d773SGleb Smirnoff  * modification, are permitted provided that: (1) source code distributions
73340d773SGleb Smirnoff  * retain the above copyright notice and this paragraph in its entirety, (2)
83340d773SGleb Smirnoff  * distributions including binary code include the above copyright notice and
93340d773SGleb Smirnoff  * this paragraph in its entirety in the documentation or other materials
103340d773SGleb Smirnoff  * provided with the distribution, and (3) all advertising materials mentioning
113340d773SGleb Smirnoff  * features or use of this software display the following acknowledgement:
123340d773SGleb Smirnoff  * ``This product includes software developed by the University of California,
133340d773SGleb Smirnoff  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
143340d773SGleb Smirnoff  * the University nor the names of its contributors may be used to endorse
153340d773SGleb Smirnoff  * or promote products derived from this software without specific prior
163340d773SGleb Smirnoff  * written permission.
173340d773SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
183340d773SGleb Smirnoff  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
193340d773SGleb Smirnoff  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
203340d773SGleb Smirnoff  */
213340d773SGleb Smirnoff 
223340d773SGleb Smirnoff /*
233340d773SGleb Smirnoff  * txtproto_print() derived from original code by Hannes Gredler
240bff6a5aSEd Maste  * (hannes@gredler.at):
253340d773SGleb Smirnoff  *
263340d773SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
273340d773SGleb Smirnoff  * modification, are permitted provided that: (1) source code
283340d773SGleb Smirnoff  * distributions retain the above copyright notice and this paragraph
293340d773SGleb Smirnoff  * in its entirety, and (2) distributions including binary code include
303340d773SGleb Smirnoff  * the above copyright notice and this paragraph in its entirety in
313340d773SGleb Smirnoff  * the documentation or other materials provided with the distribution.
323340d773SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
333340d773SGleb Smirnoff  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
343340d773SGleb Smirnoff  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
353340d773SGleb Smirnoff  * FOR A PARTICULAR PURPOSE.
363340d773SGleb Smirnoff  */
373340d773SGleb Smirnoff 
38ee67461eSJoseph Mingrone #include <config.h>
393340d773SGleb Smirnoff 
40ee67461eSJoseph Mingrone #include "netdissect-stdinc.h"
413340d773SGleb Smirnoff 
423340d773SGleb Smirnoff #include <sys/stat.h>
433340d773SGleb Smirnoff 
443340d773SGleb Smirnoff #include <stdio.h>
453340d773SGleb Smirnoff #include <stdarg.h>
463340d773SGleb Smirnoff #include <stdlib.h>
473340d773SGleb Smirnoff #include <string.h>
483340d773SGleb Smirnoff 
49ee67461eSJoseph Mingrone #include "netdissect-ctype.h"
50ee67461eSJoseph Mingrone 
513340d773SGleb Smirnoff #include "netdissect.h"
52ee67461eSJoseph Mingrone #include "extract.h"
533340d773SGleb Smirnoff #include "ascii_strcasecmp.h"
543340d773SGleb Smirnoff #include "timeval-operations.h"
553340d773SGleb Smirnoff 
563340d773SGleb Smirnoff #define TOKBUFSIZE 128
573340d773SGleb Smirnoff 
58ee67461eSJoseph Mingrone enum date_flag { WITHOUT_DATE = 0, WITH_DATE = 1 };
59ee67461eSJoseph Mingrone enum time_flag { UTC_TIME = 0, LOCAL_TIME = 1 };
60ee67461eSJoseph Mingrone 
613340d773SGleb Smirnoff /*
623340d773SGleb Smirnoff  * Print out a character, filtering out the non-printable ones
633340d773SGleb Smirnoff  */
643340d773SGleb Smirnoff void
653340d773SGleb Smirnoff fn_print_char(netdissect_options *ndo, u_char c)
663340d773SGleb Smirnoff {
673340d773SGleb Smirnoff 	if (!ND_ISASCII(c)) {
683340d773SGleb Smirnoff 		c = ND_TOASCII(c);
69ee67461eSJoseph Mingrone 		ND_PRINT("M-");
703340d773SGleb Smirnoff 	}
71ee67461eSJoseph Mingrone 	if (!ND_ASCII_ISPRINT(c)) {
723340d773SGleb Smirnoff 		c ^= 0x40;	/* DEL to ?, others to alpha */
73ee67461eSJoseph Mingrone 		ND_PRINT("^");
743340d773SGleb Smirnoff 	}
75ee67461eSJoseph Mingrone 	ND_PRINT("%c", c);
763340d773SGleb Smirnoff }
773340d773SGleb Smirnoff 
783340d773SGleb Smirnoff /*
79ee67461eSJoseph Mingrone  * Print a null-terminated string, filtering out non-printable characters.
80ee67461eSJoseph Mingrone  * DON'T USE IT with a pointer on the packet buffer because there is no
81ee67461eSJoseph Mingrone  * truncation check. For this use, see the nd_printX() functions below.
823340d773SGleb Smirnoff  */
83ee67461eSJoseph Mingrone void
84ee67461eSJoseph Mingrone fn_print_str(netdissect_options *ndo, const u_char *s)
853340d773SGleb Smirnoff {
86ee67461eSJoseph Mingrone 	while (*s != '\0') {
87ee67461eSJoseph Mingrone 		fn_print_char(ndo, *s);
88ee67461eSJoseph Mingrone 		s++;
893340d773SGleb Smirnoff        }
903340d773SGleb Smirnoff }
913340d773SGleb Smirnoff 
923340d773SGleb Smirnoff /*
93ee67461eSJoseph Mingrone  * Print out a null-terminated filename (or other ASCII string) from
9439e421e8SCy Schubert  * a fixed-length field in the packet buffer, or from what remains of
9539e421e8SCy Schubert  * the packet.
9639e421e8SCy Schubert  *
9739e421e8SCy Schubert  * n is the length of the fixed-length field, or the number of bytes
9839e421e8SCy Schubert  * remaining in the packet based on its on-the-network length.
9939e421e8SCy Schubert  *
10039e421e8SCy Schubert  * If ep is non-null, it should point just past the last captured byte
10139e421e8SCy Schubert  * of the packet, e.g. ndo->ndo_snapend.  If ep is NULL, we assume no
10239e421e8SCy Schubert  * truncation check, other than the checks of the field length/remaining
10339e421e8SCy Schubert  * packet data length, is needed.
10439e421e8SCy Schubert  *
1053340d773SGleb Smirnoff  * Return the number of bytes of string processed, including the
10639e421e8SCy Schubert  * terminating null, if not truncated; as the terminating null is
10739e421e8SCy Schubert  * included in the count, and as there must be a terminating null,
10839e421e8SCy Schubert  * this will always be non-zero.  Return 0 if truncated.
1093340d773SGleb Smirnoff  */
1103340d773SGleb Smirnoff u_int
111ee67461eSJoseph Mingrone nd_printztn(netdissect_options *ndo,
112ee67461eSJoseph Mingrone          const u_char *s, u_int n, const u_char *ep)
1133340d773SGleb Smirnoff {
114ee67461eSJoseph Mingrone 	u_int bytes;
115ee67461eSJoseph Mingrone 	u_char c;
1163340d773SGleb Smirnoff 
1173340d773SGleb Smirnoff 	bytes = 0;
1183340d773SGleb Smirnoff 	for (;;) {
1193340d773SGleb Smirnoff 		if (n == 0 || (ep != NULL && s >= ep)) {
1203340d773SGleb Smirnoff 			/*
1213340d773SGleb Smirnoff 			 * Truncated.  This includes "no null before we
12239e421e8SCy Schubert 			 * got to the end of the fixed-length buffer or
12339e421e8SCy Schubert 			 * the end of the packet".
1243340d773SGleb Smirnoff 			 *
1253340d773SGleb Smirnoff 			 * XXX - BOOTP says "null-terminated", which
1263340d773SGleb Smirnoff 			 * means the maximum length of the string, in
1273340d773SGleb Smirnoff 			 * bytes, is 1 less than the size of the buffer,
1283340d773SGleb Smirnoff 			 * as there must always be a terminating null.
1293340d773SGleb Smirnoff 			 */
1303340d773SGleb Smirnoff 			bytes = 0;
1313340d773SGleb Smirnoff 			break;
1323340d773SGleb Smirnoff 		}
1333340d773SGleb Smirnoff 
134ee67461eSJoseph Mingrone 		c = GET_U_1(s);
135ee67461eSJoseph Mingrone 		s++;
1363340d773SGleb Smirnoff 		bytes++;
1373340d773SGleb Smirnoff 		n--;
1383340d773SGleb Smirnoff 		if (c == '\0') {
1393340d773SGleb Smirnoff 			/* End of string */
1403340d773SGleb Smirnoff 			break;
1413340d773SGleb Smirnoff 		}
142ee67461eSJoseph Mingrone 		fn_print_char(ndo, c);
1433340d773SGleb Smirnoff 	}
1443340d773SGleb Smirnoff 	return(bytes);
1453340d773SGleb Smirnoff }
1463340d773SGleb Smirnoff 
1473340d773SGleb Smirnoff /*
148ee67461eSJoseph Mingrone  * Print out a counted filename (or other ASCII string), part of
149ee67461eSJoseph Mingrone  * the packet buffer.
1503340d773SGleb Smirnoff  * If ep is NULL, assume no truncation check is needed.
1513340d773SGleb Smirnoff  * Return true if truncated.
1523340d773SGleb Smirnoff  * Stop at ep (if given) or after n bytes, whichever is first.
1533340d773SGleb Smirnoff  */
1543340d773SGleb Smirnoff int
155ee67461eSJoseph Mingrone nd_printn(netdissect_options *ndo,
156ee67461eSJoseph Mingrone           const u_char *s, u_int n, const u_char *ep)
1573340d773SGleb Smirnoff {
158ee67461eSJoseph Mingrone 	u_char c;
1593340d773SGleb Smirnoff 
1603340d773SGleb Smirnoff 	while (n > 0 && (ep == NULL || s < ep)) {
1613340d773SGleb Smirnoff 		n--;
162ee67461eSJoseph Mingrone 		c = GET_U_1(s);
163ee67461eSJoseph Mingrone 		s++;
164ee67461eSJoseph Mingrone 		fn_print_char(ndo, c);
1653340d773SGleb Smirnoff 	}
1663340d773SGleb Smirnoff 	return (n == 0) ? 0 : 1;
1673340d773SGleb Smirnoff }
1683340d773SGleb Smirnoff 
1693340d773SGleb Smirnoff /*
170*0a7e5f1fSJoseph Mingrone  * Print a counted filename (or other ASCII string), part of
171*0a7e5f1fSJoseph Mingrone  * the packet buffer, filtering out non-printable characters.
172*0a7e5f1fSJoseph Mingrone  * Stop if truncated (via GET_U_1/longjmp) or after n bytes,
173*0a7e5f1fSJoseph Mingrone  * whichever is first.
174*0a7e5f1fSJoseph Mingrone  * The suffix comes from: j:longJmp, n:after N bytes.
175*0a7e5f1fSJoseph Mingrone  */
176*0a7e5f1fSJoseph Mingrone void
177*0a7e5f1fSJoseph Mingrone nd_printjn(netdissect_options *ndo, const u_char *s, u_int n)
178*0a7e5f1fSJoseph Mingrone {
179*0a7e5f1fSJoseph Mingrone 	while (n > 0) {
180*0a7e5f1fSJoseph Mingrone 		fn_print_char(ndo, GET_U_1(s));
181*0a7e5f1fSJoseph Mingrone 		n--;
182*0a7e5f1fSJoseph Mingrone 		s++;
183*0a7e5f1fSJoseph Mingrone 	}
184*0a7e5f1fSJoseph Mingrone }
185*0a7e5f1fSJoseph Mingrone 
186*0a7e5f1fSJoseph Mingrone /*
187ee67461eSJoseph Mingrone  * Print a null-padded filename (or other ASCII string), part of
188ee67461eSJoseph Mingrone  * the packet buffer, filtering out non-printable characters.
189ee67461eSJoseph Mingrone  * Stop if truncated (via GET_U_1/longjmp) or after n bytes or before
190ee67461eSJoseph Mingrone  * the null char, whichever occurs first.
191ee67461eSJoseph Mingrone  * The suffix comes from: j:longJmp, n:after N bytes, p:null-Padded.
1923340d773SGleb Smirnoff  */
193ee67461eSJoseph Mingrone void
194ee67461eSJoseph Mingrone nd_printjnp(netdissect_options *ndo, const u_char *s, u_int n)
1953340d773SGleb Smirnoff {
196ee67461eSJoseph Mingrone 	u_char c;
1973340d773SGleb Smirnoff 
198ee67461eSJoseph Mingrone 	while (n > 0) {
199ee67461eSJoseph Mingrone 		c = GET_U_1(s);
200ee67461eSJoseph Mingrone 		if (c == '\0')
201ee67461eSJoseph Mingrone 			break;
202ee67461eSJoseph Mingrone 		fn_print_char(ndo, c);
2033340d773SGleb Smirnoff 		n--;
204ee67461eSJoseph Mingrone 		s++;
2053340d773SGleb Smirnoff 	}
2063340d773SGleb Smirnoff }
2073340d773SGleb Smirnoff 
2083340d773SGleb Smirnoff /*
209ee67461eSJoseph Mingrone  * Print the timestamp .FRAC part (Microseconds/nanoseconds)
2103340d773SGleb Smirnoff  */
211ee67461eSJoseph Mingrone static void
212*0a7e5f1fSJoseph Mingrone ts_frac_print(netdissect_options *ndo, const struct timeval *tv)
2133340d773SGleb Smirnoff {
2143340d773SGleb Smirnoff #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
2153340d773SGleb Smirnoff 	switch (ndo->ndo_tstamp_precision) {
2163340d773SGleb Smirnoff 
2173340d773SGleb Smirnoff 	case PCAP_TSTAMP_PRECISION_MICRO:
218*0a7e5f1fSJoseph Mingrone 		ND_PRINT(".%06u", (unsigned)tv->tv_usec);
2193340d773SGleb Smirnoff 		break;
2203340d773SGleb Smirnoff 
2213340d773SGleb Smirnoff 	case PCAP_TSTAMP_PRECISION_NANO:
222*0a7e5f1fSJoseph Mingrone 		ND_PRINT(".%09u", (unsigned)tv->tv_usec);
2233340d773SGleb Smirnoff 		break;
2243340d773SGleb Smirnoff 
2253340d773SGleb Smirnoff 	default:
226ee67461eSJoseph Mingrone 		ND_PRINT(".{unknown}");
2273340d773SGleb Smirnoff 		break;
2283340d773SGleb Smirnoff 	}
2293340d773SGleb Smirnoff #else
230*0a7e5f1fSJoseph Mingrone 	ND_PRINT(".%06u", (unsigned)tv->tv_usec);
2313340d773SGleb Smirnoff #endif
2323340d773SGleb Smirnoff }
2333340d773SGleb Smirnoff 
2343340d773SGleb Smirnoff /*
235ee67461eSJoseph Mingrone  * Print the timestamp as [YY:MM:DD] HH:MM:SS.FRAC.
236ee67461eSJoseph Mingrone  *   if time_flag == LOCAL_TIME print local time else UTC/GMT time
237ee67461eSJoseph Mingrone  *   if date_flag == WITH_DATE print YY:MM:DD before HH:MM:SS.FRAC
2383340d773SGleb Smirnoff  */
239ee67461eSJoseph Mingrone static void
240*0a7e5f1fSJoseph Mingrone ts_date_hmsfrac_print(netdissect_options *ndo, const struct timeval *tv,
241ee67461eSJoseph Mingrone 		      enum date_flag date_flag, enum time_flag time_flag)
2423340d773SGleb Smirnoff {
243ee67461eSJoseph Mingrone 	struct tm *tm;
244ee67461eSJoseph Mingrone 	char timebuf[32];
245ee67461eSJoseph Mingrone 	const char *timestr;
2463340d773SGleb Smirnoff 
247*0a7e5f1fSJoseph Mingrone 	if (tv->tv_sec < 0) {
248*0a7e5f1fSJoseph Mingrone 		ND_PRINT("[timestamp < 1970-01-01 00:00:00 UTC]");
249ee67461eSJoseph Mingrone 		return;
2503340d773SGleb Smirnoff 	}
2513340d773SGleb Smirnoff 
252ee67461eSJoseph Mingrone 	if (time_flag == LOCAL_TIME)
253*0a7e5f1fSJoseph Mingrone 		tm = localtime(&tv->tv_sec);
254ee67461eSJoseph Mingrone 	else
255*0a7e5f1fSJoseph Mingrone 		tm = gmtime(&tv->tv_sec);
2563340d773SGleb Smirnoff 
257ee67461eSJoseph Mingrone 	if (date_flag == WITH_DATE) {
258ee67461eSJoseph Mingrone 		timestr = nd_format_time(timebuf, sizeof(timebuf),
259ee67461eSJoseph Mingrone 		    "%Y-%m-%d %H:%M:%S", tm);
260ee67461eSJoseph Mingrone 	} else {
261ee67461eSJoseph Mingrone 		timestr = nd_format_time(timebuf, sizeof(timebuf),
262ee67461eSJoseph Mingrone 		    "%H:%M:%S", tm);
263ee67461eSJoseph Mingrone 	}
264ee67461eSJoseph Mingrone 	ND_PRINT("%s", timestr);
265ee67461eSJoseph Mingrone 
266*0a7e5f1fSJoseph Mingrone 	ts_frac_print(ndo, tv);
267ee67461eSJoseph Mingrone }
268ee67461eSJoseph Mingrone 
269ee67461eSJoseph Mingrone /*
270ee67461eSJoseph Mingrone  * Print the timestamp - Unix timeval style, as SECS.FRAC.
271ee67461eSJoseph Mingrone  */
272ee67461eSJoseph Mingrone static void
273*0a7e5f1fSJoseph Mingrone ts_unix_print(netdissect_options *ndo, const struct timeval *tv)
274ee67461eSJoseph Mingrone {
275*0a7e5f1fSJoseph Mingrone 	if (tv->tv_sec < 0) {
276*0a7e5f1fSJoseph Mingrone 		ND_PRINT("[timestamp < 1970-01-01 00:00:00 UTC]");
277ee67461eSJoseph Mingrone 		return;
278ee67461eSJoseph Mingrone 	}
279ee67461eSJoseph Mingrone 
280*0a7e5f1fSJoseph Mingrone 	ND_PRINT("%u", (unsigned)tv->tv_sec);
281*0a7e5f1fSJoseph Mingrone 	ts_frac_print(ndo, tv);
2823340d773SGleb Smirnoff }
2833340d773SGleb Smirnoff 
2843340d773SGleb Smirnoff /*
2853340d773SGleb Smirnoff  * Print the timestamp
2863340d773SGleb Smirnoff  */
2873340d773SGleb Smirnoff void
2883340d773SGleb Smirnoff ts_print(netdissect_options *ndo,
289ee67461eSJoseph Mingrone          const struct timeval *tvp)
2903340d773SGleb Smirnoff {
2913340d773SGleb Smirnoff 	static struct timeval tv_ref;
2923340d773SGleb Smirnoff 	struct timeval tv_result;
2933340d773SGleb Smirnoff 	int negative_offset;
2943340d773SGleb Smirnoff 	int nano_prec;
2953340d773SGleb Smirnoff 
2963340d773SGleb Smirnoff 	switch (ndo->ndo_tflag) {
2973340d773SGleb Smirnoff 
2983340d773SGleb Smirnoff 	case 0: /* Default */
299*0a7e5f1fSJoseph Mingrone 		ts_date_hmsfrac_print(ndo, tvp, WITHOUT_DATE, LOCAL_TIME);
300ee67461eSJoseph Mingrone 		ND_PRINT(" ");
3013340d773SGleb Smirnoff 		break;
3023340d773SGleb Smirnoff 
3033340d773SGleb Smirnoff 	case 1: /* No time stamp */
3043340d773SGleb Smirnoff 		break;
3053340d773SGleb Smirnoff 
3063340d773SGleb Smirnoff 	case 2: /* Unix timeval style */
307*0a7e5f1fSJoseph Mingrone 		ts_unix_print(ndo, tvp);
308ee67461eSJoseph Mingrone 		ND_PRINT(" ");
3093340d773SGleb Smirnoff 		break;
3103340d773SGleb Smirnoff 
3113340d773SGleb Smirnoff 	case 3: /* Microseconds/nanoseconds since previous packet */
3123340d773SGleb Smirnoff         case 5: /* Microseconds/nanoseconds since first packet */
3133340d773SGleb Smirnoff #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
3143340d773SGleb Smirnoff 		switch (ndo->ndo_tstamp_precision) {
3153340d773SGleb Smirnoff 		case PCAP_TSTAMP_PRECISION_MICRO:
3163340d773SGleb Smirnoff 			nano_prec = 0;
3173340d773SGleb Smirnoff 			break;
3183340d773SGleb Smirnoff 		case PCAP_TSTAMP_PRECISION_NANO:
3193340d773SGleb Smirnoff 			nano_prec = 1;
3203340d773SGleb Smirnoff 			break;
3213340d773SGleb Smirnoff 		default:
3223340d773SGleb Smirnoff 			nano_prec = 0;
3233340d773SGleb Smirnoff 			break;
3243340d773SGleb Smirnoff 		}
3253340d773SGleb Smirnoff #else
3263340d773SGleb Smirnoff 		nano_prec = 0;
3273340d773SGleb Smirnoff #endif
3283340d773SGleb Smirnoff 		if (!(netdissect_timevalisset(&tv_ref)))
3293340d773SGleb Smirnoff 			tv_ref = *tvp; /* set timestamp for first packet */
3303340d773SGleb Smirnoff 
3313340d773SGleb Smirnoff 		negative_offset = netdissect_timevalcmp(tvp, &tv_ref, <);
3323340d773SGleb Smirnoff 		if (negative_offset)
3333340d773SGleb Smirnoff 			netdissect_timevalsub(&tv_ref, tvp, &tv_result, nano_prec);
3343340d773SGleb Smirnoff 		else
3353340d773SGleb Smirnoff 			netdissect_timevalsub(tvp, &tv_ref, &tv_result, nano_prec);
3363340d773SGleb Smirnoff 
337ee67461eSJoseph Mingrone 		ND_PRINT((negative_offset ? "-" : " "));
338*0a7e5f1fSJoseph Mingrone 		ts_date_hmsfrac_print(ndo, &tv_result, WITHOUT_DATE, UTC_TIME);
339ee67461eSJoseph Mingrone 		ND_PRINT(" ");
3403340d773SGleb Smirnoff 
3413340d773SGleb Smirnoff                 if (ndo->ndo_tflag == 3)
3423340d773SGleb Smirnoff 			tv_ref = *tvp; /* set timestamp for previous packet */
3433340d773SGleb Smirnoff 		break;
3443340d773SGleb Smirnoff 
345ee67461eSJoseph Mingrone 	case 4: /* Date + Default */
346*0a7e5f1fSJoseph Mingrone 		ts_date_hmsfrac_print(ndo, tvp, WITH_DATE, LOCAL_TIME);
347ee67461eSJoseph Mingrone 		ND_PRINT(" ");
3483340d773SGleb Smirnoff 		break;
3493340d773SGleb Smirnoff 	}
3503340d773SGleb Smirnoff }
3513340d773SGleb Smirnoff 
3523340d773SGleb Smirnoff /*
3533340d773SGleb Smirnoff  * Print an unsigned relative number of seconds (e.g. hold time, prune timer)
3543340d773SGleb Smirnoff  * in the form 5m1s.  This does no truncation, so 32230861 seconds
3553340d773SGleb Smirnoff  * is represented as 1y1w1d1h1m1s.
3563340d773SGleb Smirnoff  */
3573340d773SGleb Smirnoff void
3583340d773SGleb Smirnoff unsigned_relts_print(netdissect_options *ndo,
3593340d773SGleb Smirnoff                      uint32_t secs)
3603340d773SGleb Smirnoff {
3613340d773SGleb Smirnoff 	static const char *lengths[] = {"y", "w", "d", "h", "m", "s"};
3623340d773SGleb Smirnoff 	static const u_int seconds[] = {31536000, 604800, 86400, 3600, 60, 1};
3633340d773SGleb Smirnoff 	const char **l = lengths;
3643340d773SGleb Smirnoff 	const u_int *s = seconds;
3653340d773SGleb Smirnoff 
3663340d773SGleb Smirnoff 	if (secs == 0) {
367ee67461eSJoseph Mingrone 		ND_PRINT("0s");
3683340d773SGleb Smirnoff 		return;
3693340d773SGleb Smirnoff 	}
3703340d773SGleb Smirnoff 	while (secs > 0) {
3713340d773SGleb Smirnoff 		if (secs >= *s) {
372ee67461eSJoseph Mingrone 			ND_PRINT("%u%s", secs / *s, *l);
3733340d773SGleb Smirnoff 			secs -= (secs / *s) * *s;
3743340d773SGleb Smirnoff 		}
3753340d773SGleb Smirnoff 		s++;
3763340d773SGleb Smirnoff 		l++;
3773340d773SGleb Smirnoff 	}
3783340d773SGleb Smirnoff }
3793340d773SGleb Smirnoff 
3803340d773SGleb Smirnoff /*
3813340d773SGleb Smirnoff  * Print a signed relative number of seconds (e.g. hold time, prune timer)
3823340d773SGleb Smirnoff  * in the form 5m1s.  This does no truncation, so 32230861 seconds
3833340d773SGleb Smirnoff  * is represented as 1y1w1d1h1m1s.
3843340d773SGleb Smirnoff  */
3853340d773SGleb Smirnoff void
3863340d773SGleb Smirnoff signed_relts_print(netdissect_options *ndo,
3873340d773SGleb Smirnoff                    int32_t secs)
3883340d773SGleb Smirnoff {
3893340d773SGleb Smirnoff 	if (secs < 0) {
390ee67461eSJoseph Mingrone 		ND_PRINT("-");
3913340d773SGleb Smirnoff 		if (secs == INT32_MIN) {
3923340d773SGleb Smirnoff 			/*
3933340d773SGleb Smirnoff 			 * -2^31; you can't fit its absolute value into
3943340d773SGleb Smirnoff 			 * a 32-bit signed integer.
3953340d773SGleb Smirnoff 			 *
3963340d773SGleb Smirnoff 			 * Just directly pass said absolute value to
3973340d773SGleb Smirnoff 			 * unsigned_relts_print() directly.
3983340d773SGleb Smirnoff 			 *
3993340d773SGleb Smirnoff 			 * (XXX - does ISO C guarantee that -(-2^n),
4003340d773SGleb Smirnoff 			 * when calculated and cast to an n-bit unsigned
4013340d773SGleb Smirnoff 			 * integer type, will have the value 2^n?)
4023340d773SGleb Smirnoff 			 */
4033340d773SGleb Smirnoff 			unsigned_relts_print(ndo, 2147483648U);
4043340d773SGleb Smirnoff 		} else {
4053340d773SGleb Smirnoff 			/*
4063340d773SGleb Smirnoff 			 * We now know -secs will fit into an int32_t;
4073340d773SGleb Smirnoff 			 * negate it and pass that to unsigned_relts_print().
4083340d773SGleb Smirnoff 			 */
4093340d773SGleb Smirnoff 			unsigned_relts_print(ndo, -secs);
4103340d773SGleb Smirnoff 		}
4113340d773SGleb Smirnoff 		return;
4123340d773SGleb Smirnoff 	}
4133340d773SGleb Smirnoff 	unsigned_relts_print(ndo, secs);
4143340d773SGleb Smirnoff }
4153340d773SGleb Smirnoff 
4163340d773SGleb Smirnoff /*
417ee67461eSJoseph Mingrone  * Format a struct tm with strftime().
418ee67461eSJoseph Mingrone  * If the pointer to the struct tm is null, that means that the
419ee67461eSJoseph Mingrone  * routine to convert a time_t to a struct tm failed; the localtime()
420ee67461eSJoseph Mingrone  * and gmtime() in the Microsoft Visual Studio C library will fail,
421ee67461eSJoseph Mingrone  * returning null, if the value is before the UNIX Epoch.
422ee67461eSJoseph Mingrone  */
423ee67461eSJoseph Mingrone const char *
424ee67461eSJoseph Mingrone nd_format_time(char *buf, size_t bufsize, const char *format,
425ee67461eSJoseph Mingrone          const struct tm *timeptr)
426ee67461eSJoseph Mingrone {
427ee67461eSJoseph Mingrone 	if (timeptr != NULL) {
428ee67461eSJoseph Mingrone 		if (strftime(buf, bufsize, format, timeptr) != 0)
429ee67461eSJoseph Mingrone 			return (buf);
430ee67461eSJoseph Mingrone 		else
431ee67461eSJoseph Mingrone 			return ("[nd_format_time() buffer is too small]");
432ee67461eSJoseph Mingrone 	} else
433ee67461eSJoseph Mingrone 		return ("[localtime() or gmtime() couldn't convert the date and time]");
434ee67461eSJoseph Mingrone }
435ee67461eSJoseph Mingrone 
436ee67461eSJoseph Mingrone /* Print the truncated string */
437ee67461eSJoseph Mingrone void nd_print_trunc(netdissect_options *ndo)
438ee67461eSJoseph Mingrone {
439ee67461eSJoseph Mingrone 	ND_PRINT(" [|%s]", ndo->ndo_protocol);
440ee67461eSJoseph Mingrone }
441ee67461eSJoseph Mingrone 
442ee67461eSJoseph Mingrone /* Print the protocol name */
443ee67461eSJoseph Mingrone void nd_print_protocol(netdissect_options *ndo)
444ee67461eSJoseph Mingrone {
445ee67461eSJoseph Mingrone 	ND_PRINT("%s", ndo->ndo_protocol);
446ee67461eSJoseph Mingrone }
447ee67461eSJoseph Mingrone 
448ee67461eSJoseph Mingrone /* Print the protocol name in caps (uppercases) */
449ee67461eSJoseph Mingrone void nd_print_protocol_caps(netdissect_options *ndo)
450ee67461eSJoseph Mingrone {
451ee67461eSJoseph Mingrone 	const char *p;
452ee67461eSJoseph Mingrone         for (p = ndo->ndo_protocol; *p != '\0'; p++)
453ee67461eSJoseph Mingrone                 ND_PRINT("%c", ND_ASCII_TOUPPER(*p));
454ee67461eSJoseph Mingrone }
455ee67461eSJoseph Mingrone 
456ee67461eSJoseph Mingrone /* Print the invalid string */
457ee67461eSJoseph Mingrone void nd_print_invalid(netdissect_options *ndo)
458ee67461eSJoseph Mingrone {
459ee67461eSJoseph Mingrone 	ND_PRINT(" (invalid)");
460ee67461eSJoseph Mingrone }
461ee67461eSJoseph Mingrone 
462ee67461eSJoseph Mingrone /*
4633340d773SGleb Smirnoff  *  this is a generic routine for printing unknown data;
4643340d773SGleb Smirnoff  *  we pass on the linefeed plus indentation string to
4653340d773SGleb Smirnoff  *  get a proper output - returns 0 on error
4663340d773SGleb Smirnoff  */
4673340d773SGleb Smirnoff 
4683340d773SGleb Smirnoff int
469ee67461eSJoseph Mingrone print_unknown_data(netdissect_options *ndo, const u_char *cp,
470ee67461eSJoseph Mingrone                    const char *ident, u_int len)
4713340d773SGleb Smirnoff {
472ee67461eSJoseph Mingrone 	u_int len_to_print;
473ee67461eSJoseph Mingrone 
474ee67461eSJoseph Mingrone 	len_to_print = len;
475ee67461eSJoseph Mingrone 	if (!ND_TTEST_LEN(cp, 0)) {
476ee67461eSJoseph Mingrone 		ND_PRINT("%sDissector error: print_unknown_data called with pointer past end of packet",
477ee67461eSJoseph Mingrone 		    ident);
4783340d773SGleb Smirnoff 		return(0);
4793340d773SGleb Smirnoff 	}
480ee67461eSJoseph Mingrone 	if (ND_BYTES_AVAILABLE_AFTER(cp) < len_to_print)
481ee67461eSJoseph Mingrone 		len_to_print = ND_BYTES_AVAILABLE_AFTER(cp);
482ee67461eSJoseph Mingrone 	hex_print(ndo, ident, cp, len_to_print);
4833340d773SGleb Smirnoff 	return(1); /* everything is ok */
4843340d773SGleb Smirnoff }
4853340d773SGleb Smirnoff 
4863340d773SGleb Smirnoff /*
4873340d773SGleb Smirnoff  * Convert a token value to a string; use "fmt" if not found.
4883340d773SGleb Smirnoff  */
489ee67461eSJoseph Mingrone static const char *
490ee67461eSJoseph Mingrone tok2strbuf(const struct tok *lp, const char *fmt,
491ee67461eSJoseph Mingrone 	   u_int v, char *buf, size_t bufsize)
4923340d773SGleb Smirnoff {
4933340d773SGleb Smirnoff 	if (lp != NULL) {
4943340d773SGleb Smirnoff 		while (lp->s != NULL) {
4953340d773SGleb Smirnoff 			if (lp->v == v)
4963340d773SGleb Smirnoff 				return (lp->s);
4973340d773SGleb Smirnoff 			++lp;
4983340d773SGleb Smirnoff 		}
4993340d773SGleb Smirnoff 	}
5003340d773SGleb Smirnoff 	if (fmt == NULL)
5013340d773SGleb Smirnoff 		fmt = "#%d";
5023340d773SGleb Smirnoff 
5033340d773SGleb Smirnoff 	(void)snprintf(buf, bufsize, fmt, v);
5043340d773SGleb Smirnoff 	return (const char *)buf;
5053340d773SGleb Smirnoff }
5063340d773SGleb Smirnoff 
5073340d773SGleb Smirnoff /*
5083340d773SGleb Smirnoff  * Convert a token value to a string; use "fmt" if not found.
509ee67461eSJoseph Mingrone  * Uses tok2strbuf() on one of four local static buffers of size TOKBUFSIZE
510ee67461eSJoseph Mingrone  * in round-robin fashion.
5113340d773SGleb Smirnoff  */
5123340d773SGleb Smirnoff const char *
513ee67461eSJoseph Mingrone tok2str(const struct tok *lp, const char *fmt,
514ee67461eSJoseph Mingrone 	u_int v)
5153340d773SGleb Smirnoff {
5163340d773SGleb Smirnoff 	static char buf[4][TOKBUFSIZE];
5173340d773SGleb Smirnoff 	static int idx = 0;
5183340d773SGleb Smirnoff 	char *ret;
5193340d773SGleb Smirnoff 
5203340d773SGleb Smirnoff 	ret = buf[idx];
5213340d773SGleb Smirnoff 	idx = (idx+1) & 3;
5223340d773SGleb Smirnoff 	return tok2strbuf(lp, fmt, v, ret, sizeof(buf[0]));
5233340d773SGleb Smirnoff }
5243340d773SGleb Smirnoff 
5253340d773SGleb Smirnoff /*
5263340d773SGleb Smirnoff  * Convert a bit token value to a string; use "fmt" if not found.
527ee67461eSJoseph Mingrone  * this is useful for parsing bitfields, the output strings are separated
5283340d773SGleb Smirnoff  * if the s field is positive.
529ee67461eSJoseph Mingrone  *
530ee67461eSJoseph Mingrone  * A token matches iff it has one or more bits set and every bit that is set
531ee67461eSJoseph Mingrone  * in the token is set in v. Consequently, a 0 token never matches.
5323340d773SGleb Smirnoff  */
5333340d773SGleb Smirnoff static char *
534ee67461eSJoseph Mingrone bittok2str_internal(const struct tok *lp, const char *fmt,
535ee67461eSJoseph Mingrone 	   u_int v, const char *sep)
5363340d773SGleb Smirnoff {
5370bff6a5aSEd Maste         static char buf[1024+1]; /* our string buffer */
5380bff6a5aSEd Maste         char *bufp = buf;
5390bff6a5aSEd Maste         size_t space_left = sizeof(buf), string_size;
5403340d773SGleb Smirnoff         const char * sepstr = "";
5413340d773SGleb Smirnoff 
5423340d773SGleb Smirnoff         while (lp != NULL && lp->s != NULL) {
543ee67461eSJoseph Mingrone             if (lp->v && (v & lp->v) == lp->v) {
5443340d773SGleb Smirnoff                 /* ok we have found something */
5450bff6a5aSEd Maste                 if (space_left <= 1)
5460bff6a5aSEd Maste                     return (buf); /* only enough room left for NUL, if that */
5470bff6a5aSEd Maste                 string_size = strlcpy(bufp, sepstr, space_left);
5480bff6a5aSEd Maste                 if (string_size >= space_left)
5490bff6a5aSEd Maste                     return (buf);    /* we ran out of room */
5500bff6a5aSEd Maste                 bufp += string_size;
5510bff6a5aSEd Maste                 space_left -= string_size;
5520bff6a5aSEd Maste                 if (space_left <= 1)
5530bff6a5aSEd Maste                     return (buf); /* only enough room left for NUL, if that */
5540bff6a5aSEd Maste                 string_size = strlcpy(bufp, lp->s, space_left);
5550bff6a5aSEd Maste                 if (string_size >= space_left)
5560bff6a5aSEd Maste                     return (buf);    /* we ran out of room */
5570bff6a5aSEd Maste                 bufp += string_size;
5580bff6a5aSEd Maste                 space_left -= string_size;
5593340d773SGleb Smirnoff                 sepstr = sep;
5603340d773SGleb Smirnoff             }
5613340d773SGleb Smirnoff             lp++;
5623340d773SGleb Smirnoff         }
5633340d773SGleb Smirnoff 
5640bff6a5aSEd Maste         if (bufp == buf)
5653340d773SGleb Smirnoff             /* bummer - lets print the "unknown" message as advised in the fmt string if we got one */
5663340d773SGleb Smirnoff             (void)snprintf(buf, sizeof(buf), fmt == NULL ? "#%08x" : fmt, v);
5673340d773SGleb Smirnoff         return (buf);
5683340d773SGleb Smirnoff }
5693340d773SGleb Smirnoff 
5703340d773SGleb Smirnoff /*
5713340d773SGleb Smirnoff  * Convert a bit token value to a string; use "fmt" if not found.
572ee67461eSJoseph Mingrone  * this is useful for parsing bitfields, the output strings are not separated.
5733340d773SGleb Smirnoff  */
5743340d773SGleb Smirnoff char *
575ee67461eSJoseph Mingrone bittok2str_nosep(const struct tok *lp, const char *fmt,
576ee67461eSJoseph Mingrone 	   u_int v)
5773340d773SGleb Smirnoff {
5783340d773SGleb Smirnoff     return (bittok2str_internal(lp, fmt, v, ""));
5793340d773SGleb Smirnoff }
5803340d773SGleb Smirnoff 
5813340d773SGleb Smirnoff /*
5823340d773SGleb Smirnoff  * Convert a bit token value to a string; use "fmt" if not found.
583ee67461eSJoseph Mingrone  * this is useful for parsing bitfields, the output strings are comma separated.
5843340d773SGleb Smirnoff  */
5853340d773SGleb Smirnoff char *
586ee67461eSJoseph Mingrone bittok2str(const struct tok *lp, const char *fmt,
587ee67461eSJoseph Mingrone 	   u_int v)
5883340d773SGleb Smirnoff {
5893340d773SGleb Smirnoff     return (bittok2str_internal(lp, fmt, v, ", "));
5903340d773SGleb Smirnoff }
5913340d773SGleb Smirnoff 
5923340d773SGleb Smirnoff /*
5933340d773SGleb Smirnoff  * Convert a value to a string using an array; the macro
5943340d773SGleb Smirnoff  * tok2strary() in <netdissect.h> is the public interface to
5953340d773SGleb Smirnoff  * this function and ensures that the second argument is
5963340d773SGleb Smirnoff  * correct for bounds-checking.
5973340d773SGleb Smirnoff  */
5983340d773SGleb Smirnoff const char *
599ee67461eSJoseph Mingrone tok2strary_internal(const char **lp, int n, const char *fmt,
600ee67461eSJoseph Mingrone 	int v)
6013340d773SGleb Smirnoff {
6023340d773SGleb Smirnoff 	static char buf[TOKBUFSIZE];
6033340d773SGleb Smirnoff 
6043340d773SGleb Smirnoff 	if (v >= 0 && v < n && lp[v] != NULL)
6053340d773SGleb Smirnoff 		return lp[v];
6063340d773SGleb Smirnoff 	if (fmt == NULL)
6073340d773SGleb Smirnoff 		fmt = "#%d";
6083340d773SGleb Smirnoff 	(void)snprintf(buf, sizeof(buf), fmt, v);
6093340d773SGleb Smirnoff 	return (buf);
6103340d773SGleb Smirnoff }
6113340d773SGleb Smirnoff 
612ee67461eSJoseph Mingrone const struct tok *
613ee67461eSJoseph Mingrone uint2tokary_internal(const struct uint_tokary dict[], const size_t size,
614ee67461eSJoseph Mingrone                      const u_int val)
615ee67461eSJoseph Mingrone {
616ee67461eSJoseph Mingrone 	size_t i;
617ee67461eSJoseph Mingrone 	/* Try a direct lookup before the full scan. */
618ee67461eSJoseph Mingrone 	if (val < size && dict[val].uintval == val)
619ee67461eSJoseph Mingrone 		return dict[val].tokary; /* OK if NULL */
620ee67461eSJoseph Mingrone 	for (i = 0; i < size; i++)
621ee67461eSJoseph Mingrone 		if (dict[i].uintval == val)
622ee67461eSJoseph Mingrone 			return dict[i].tokary; /* OK if NULL */
623ee67461eSJoseph Mingrone 	return NULL;
624ee67461eSJoseph Mingrone }
625ee67461eSJoseph Mingrone 
6263340d773SGleb Smirnoff /*
6273340d773SGleb Smirnoff  * Convert a 32-bit netmask to prefixlen if possible
6283340d773SGleb Smirnoff  * the function returns the prefix-len; if plen == -1
6293340d773SGleb Smirnoff  * then conversion was not possible;
6303340d773SGleb Smirnoff  */
6313340d773SGleb Smirnoff 
6323340d773SGleb Smirnoff int
6333340d773SGleb Smirnoff mask2plen(uint32_t mask)
6343340d773SGleb Smirnoff {
635ee67461eSJoseph Mingrone 	const uint32_t bitmasks[33] = {
6363340d773SGleb Smirnoff 		0x00000000,
6373340d773SGleb Smirnoff 		0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
6383340d773SGleb Smirnoff 		0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
6393340d773SGleb Smirnoff 		0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
6403340d773SGleb Smirnoff 		0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
6413340d773SGleb Smirnoff 		0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
6423340d773SGleb Smirnoff 		0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
6433340d773SGleb Smirnoff 		0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
6443340d773SGleb Smirnoff 		0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff
6453340d773SGleb Smirnoff 	};
6463340d773SGleb Smirnoff 	int prefix_len = 32;
6473340d773SGleb Smirnoff 
6483340d773SGleb Smirnoff 	/* let's see if we can transform the mask into a prefixlen */
6493340d773SGleb Smirnoff 	while (prefix_len >= 0) {
6503340d773SGleb Smirnoff 		if (bitmasks[prefix_len] == mask)
6513340d773SGleb Smirnoff 			break;
6523340d773SGleb Smirnoff 		prefix_len--;
6533340d773SGleb Smirnoff 	}
6543340d773SGleb Smirnoff 	return (prefix_len);
6553340d773SGleb Smirnoff }
6563340d773SGleb Smirnoff 
6573340d773SGleb Smirnoff int
6583340d773SGleb Smirnoff mask62plen(const u_char *mask)
6593340d773SGleb Smirnoff {
6603340d773SGleb Smirnoff 	u_char bitmasks[9] = {
6613340d773SGleb Smirnoff 		0x00,
6623340d773SGleb Smirnoff 		0x80, 0xc0, 0xe0, 0xf0,
6633340d773SGleb Smirnoff 		0xf8, 0xfc, 0xfe, 0xff
6643340d773SGleb Smirnoff 	};
6653340d773SGleb Smirnoff 	int byte;
6663340d773SGleb Smirnoff 	int cidr_len = 0;
6673340d773SGleb Smirnoff 
6683340d773SGleb Smirnoff 	for (byte = 0; byte < 16; byte++) {
6693340d773SGleb Smirnoff 		u_int bits;
6703340d773SGleb Smirnoff 
6713340d773SGleb Smirnoff 		for (bits = 0; bits < (sizeof (bitmasks) / sizeof (bitmasks[0])); bits++) {
6723340d773SGleb Smirnoff 			if (mask[byte] == bitmasks[bits]) {
6733340d773SGleb Smirnoff 				cidr_len += bits;
6743340d773SGleb Smirnoff 				break;
6753340d773SGleb Smirnoff 			}
6763340d773SGleb Smirnoff 		}
6773340d773SGleb Smirnoff 
6783340d773SGleb Smirnoff 		if (mask[byte] != 0xff)
6793340d773SGleb Smirnoff 			break;
6803340d773SGleb Smirnoff 	}
6813340d773SGleb Smirnoff 	return (cidr_len);
6823340d773SGleb Smirnoff }
6833340d773SGleb Smirnoff 
6843340d773SGleb Smirnoff /*
6853340d773SGleb Smirnoff  * Routine to print out information for text-based protocols such as FTP,
6863340d773SGleb Smirnoff  * HTTP, SMTP, RTSP, SIP, ....
6873340d773SGleb Smirnoff  */
6883340d773SGleb Smirnoff #define MAX_TOKEN	128
6893340d773SGleb Smirnoff 
6903340d773SGleb Smirnoff /*
6913340d773SGleb Smirnoff  * Fetch a token from a packet, starting at the specified index,
6923340d773SGleb Smirnoff  * and return the length of the token.
6933340d773SGleb Smirnoff  *
6943340d773SGleb Smirnoff  * Returns 0 on error; yes, this is indistinguishable from an empty
6953340d773SGleb Smirnoff  * token, but an "empty token" isn't a valid token - it just means
6963340d773SGleb Smirnoff  * either a space character at the beginning of the line (this
6973340d773SGleb Smirnoff  * includes a blank line) or no more tokens remaining on the line.
6983340d773SGleb Smirnoff  */
6993340d773SGleb Smirnoff static int
7003340d773SGleb Smirnoff fetch_token(netdissect_options *ndo, const u_char *pptr, u_int idx, u_int len,
7013340d773SGleb Smirnoff     u_char *tbuf, size_t tbuflen)
7023340d773SGleb Smirnoff {
7033340d773SGleb Smirnoff 	size_t toklen = 0;
704ee67461eSJoseph Mingrone 	u_char c;
7053340d773SGleb Smirnoff 
7063340d773SGleb Smirnoff 	for (; idx < len; idx++) {
707ee67461eSJoseph Mingrone 		if (!ND_TTEST_1(pptr + idx)) {
7083340d773SGleb Smirnoff 			/* ran past end of captured data */
7093340d773SGleb Smirnoff 			return (0);
7103340d773SGleb Smirnoff 		}
711ee67461eSJoseph Mingrone 		c = GET_U_1(pptr + idx);
712ee67461eSJoseph Mingrone 		if (!ND_ISASCII(c)) {
7133340d773SGleb Smirnoff 			/* not an ASCII character */
7143340d773SGleb Smirnoff 			return (0);
7153340d773SGleb Smirnoff 		}
716ee67461eSJoseph Mingrone 		if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
7173340d773SGleb Smirnoff 			/* end of token */
7183340d773SGleb Smirnoff 			break;
7193340d773SGleb Smirnoff 		}
720ee67461eSJoseph Mingrone 		if (!ND_ASCII_ISPRINT(c)) {
7213340d773SGleb Smirnoff 			/* not part of a command token or response code */
7223340d773SGleb Smirnoff 			return (0);
7233340d773SGleb Smirnoff 		}
7243340d773SGleb Smirnoff 		if (toklen + 2 > tbuflen) {
7253340d773SGleb Smirnoff 			/* no room for this character and terminating '\0' */
7263340d773SGleb Smirnoff 			return (0);
7273340d773SGleb Smirnoff 		}
728ee67461eSJoseph Mingrone 		tbuf[toklen] = c;
7293340d773SGleb Smirnoff 		toklen++;
7303340d773SGleb Smirnoff 	}
7313340d773SGleb Smirnoff 	if (toklen == 0) {
7323340d773SGleb Smirnoff 		/* no token */
7333340d773SGleb Smirnoff 		return (0);
7343340d773SGleb Smirnoff 	}
7353340d773SGleb Smirnoff 	tbuf[toklen] = '\0';
7363340d773SGleb Smirnoff 
7373340d773SGleb Smirnoff 	/*
7383340d773SGleb Smirnoff 	 * Skip past any white space after the token, until we see
7393340d773SGleb Smirnoff 	 * an end-of-line (CR or LF).
7403340d773SGleb Smirnoff 	 */
7413340d773SGleb Smirnoff 	for (; idx < len; idx++) {
742ee67461eSJoseph Mingrone 		if (!ND_TTEST_1(pptr + idx)) {
7433340d773SGleb Smirnoff 			/* ran past end of captured data */
7443340d773SGleb Smirnoff 			break;
7453340d773SGleb Smirnoff 		}
746ee67461eSJoseph Mingrone 		c = GET_U_1(pptr + idx);
747ee67461eSJoseph Mingrone 		if (c == '\r' || c == '\n') {
7483340d773SGleb Smirnoff 			/* end of line */
7493340d773SGleb Smirnoff 			break;
7503340d773SGleb Smirnoff 		}
751ee67461eSJoseph Mingrone 		if (!ND_ASCII_ISPRINT(c)) {
7523340d773SGleb Smirnoff 			/* not a printable ASCII character */
7533340d773SGleb Smirnoff 			break;
7543340d773SGleb Smirnoff 		}
755ee67461eSJoseph Mingrone 		if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
7563340d773SGleb Smirnoff 			/* beginning of next token */
7573340d773SGleb Smirnoff 			break;
7583340d773SGleb Smirnoff 		}
7593340d773SGleb Smirnoff 	}
7603340d773SGleb Smirnoff 	return (idx);
7613340d773SGleb Smirnoff }
7623340d773SGleb Smirnoff 
7633340d773SGleb Smirnoff /*
7643340d773SGleb Smirnoff  * Scan a buffer looking for a line ending - LF or CR-LF.
7653340d773SGleb Smirnoff  * Return the index of the character after the line ending or 0 if
7663340d773SGleb Smirnoff  * we encounter a non-ASCII or non-printable character or don't find
7673340d773SGleb Smirnoff  * the line ending.
7683340d773SGleb Smirnoff  */
7693340d773SGleb Smirnoff static u_int
770ee67461eSJoseph Mingrone print_txt_line(netdissect_options *ndo, const char *prefix,
771ee67461eSJoseph Mingrone 	       const u_char *pptr, u_int idx, u_int len)
7723340d773SGleb Smirnoff {
7733340d773SGleb Smirnoff 	u_int startidx;
7743340d773SGleb Smirnoff 	u_int linelen;
775ee67461eSJoseph Mingrone 	u_char c;
7763340d773SGleb Smirnoff 
7773340d773SGleb Smirnoff 	startidx = idx;
7783340d773SGleb Smirnoff 	while (idx < len) {
779ee67461eSJoseph Mingrone 		c = GET_U_1(pptr + idx);
780ee67461eSJoseph Mingrone 		if (c == '\n') {
7813340d773SGleb Smirnoff 			/*
7823340d773SGleb Smirnoff 			 * LF without CR; end of line.
7833340d773SGleb Smirnoff 			 * Skip the LF and print the line, with the
7843340d773SGleb Smirnoff 			 * exception of the LF.
7853340d773SGleb Smirnoff 			 */
7863340d773SGleb Smirnoff 			linelen = idx - startidx;
7873340d773SGleb Smirnoff 			idx++;
7883340d773SGleb Smirnoff 			goto print;
789ee67461eSJoseph Mingrone 		} else if (c == '\r') {
7903340d773SGleb Smirnoff 			/* CR - any LF? */
7913340d773SGleb Smirnoff 			if ((idx+1) >= len) {
7923340d773SGleb Smirnoff 				/* not in this packet */
7933340d773SGleb Smirnoff 				return (0);
7943340d773SGleb Smirnoff 			}
795ee67461eSJoseph Mingrone 			if (GET_U_1(pptr + idx + 1) == '\n') {
7963340d773SGleb Smirnoff 				/*
7973340d773SGleb Smirnoff 				 * CR-LF; end of line.
7983340d773SGleb Smirnoff 				 * Skip the CR-LF and print the line, with
7993340d773SGleb Smirnoff 				 * the exception of the CR-LF.
8003340d773SGleb Smirnoff 				 */
8013340d773SGleb Smirnoff 				linelen = idx - startidx;
8023340d773SGleb Smirnoff 				idx += 2;
8033340d773SGleb Smirnoff 				goto print;
8043340d773SGleb Smirnoff 			}
8053340d773SGleb Smirnoff 
8063340d773SGleb Smirnoff 			/*
8073340d773SGleb Smirnoff 			 * CR followed by something else; treat this
8083340d773SGleb Smirnoff 			 * as if it were binary data, and don't print
8093340d773SGleb Smirnoff 			 * it.
8103340d773SGleb Smirnoff 			 */
8113340d773SGleb Smirnoff 			return (0);
812ee67461eSJoseph Mingrone 		} else if (!ND_ASCII_ISPRINT(c) && c != '\t') {
8133340d773SGleb Smirnoff 			/*
8143340d773SGleb Smirnoff 			 * Not a printable ASCII character and not a tab;
8153340d773SGleb Smirnoff 			 * treat this as if it were binary data, and
8163340d773SGleb Smirnoff 			 * don't print it.
8173340d773SGleb Smirnoff 			 */
8183340d773SGleb Smirnoff 			return (0);
8193340d773SGleb Smirnoff 		}
8203340d773SGleb Smirnoff 		idx++;
8213340d773SGleb Smirnoff 	}
8223340d773SGleb Smirnoff 
8233340d773SGleb Smirnoff 	/*
8243340d773SGleb Smirnoff 	 * All printable ASCII, but no line ending after that point
825*0a7e5f1fSJoseph Mingrone 	 * in the buffer.
8263340d773SGleb Smirnoff 	 */
8273340d773SGleb Smirnoff 	linelen = idx - startidx;
828ee67461eSJoseph Mingrone 	ND_PRINT("%s%.*s", prefix, (int)linelen, pptr + startidx);
8293340d773SGleb Smirnoff 	return (0);
8303340d773SGleb Smirnoff 
8313340d773SGleb Smirnoff print:
832ee67461eSJoseph Mingrone 	ND_PRINT("%s%.*s", prefix, (int)linelen, pptr + startidx);
8333340d773SGleb Smirnoff 	return (idx);
8343340d773SGleb Smirnoff }
8353340d773SGleb Smirnoff 
836ee67461eSJoseph Mingrone /* Assign needed before calling txtproto_print(): ndo->ndo_protocol = "proto" */
8373340d773SGleb Smirnoff void
8383340d773SGleb Smirnoff txtproto_print(netdissect_options *ndo, const u_char *pptr, u_int len,
839ee67461eSJoseph Mingrone 	       const char **cmds, u_int flags)
8403340d773SGleb Smirnoff {
8413340d773SGleb Smirnoff 	u_int idx, eol;
8423340d773SGleb Smirnoff 	u_char token[MAX_TOKEN+1];
8433340d773SGleb Smirnoff 	const char *cmd;
844ee67461eSJoseph Mingrone 	int print_this = 0;
8453340d773SGleb Smirnoff 
8463340d773SGleb Smirnoff 	if (cmds != NULL) {
8473340d773SGleb Smirnoff 		/*
8483340d773SGleb Smirnoff 		 * This protocol has more than just request and
8493340d773SGleb Smirnoff 		 * response lines; see whether this looks like a
850ee67461eSJoseph Mingrone 		 * request or response and, if so, print it and,
851ee67461eSJoseph Mingrone 		 * in verbose mode, print everything after it.
852ee67461eSJoseph Mingrone 		 *
853ee67461eSJoseph Mingrone 		 * This is for HTTP-like protocols, where we
854ee67461eSJoseph Mingrone 		 * want to print requests and responses, but
855ee67461eSJoseph Mingrone 		 * don't want to print continuations of request
856ee67461eSJoseph Mingrone 		 * or response bodies in packets that don't
857ee67461eSJoseph Mingrone 		 * contain the request or response line.
8583340d773SGleb Smirnoff 		 */
8593340d773SGleb Smirnoff 		idx = fetch_token(ndo, pptr, 0, len, token, sizeof(token));
8603340d773SGleb Smirnoff 		if (idx != 0) {
8613340d773SGleb Smirnoff 			/* Is this a valid request name? */
8623340d773SGleb Smirnoff 			while ((cmd = *cmds++) != NULL) {
8633340d773SGleb Smirnoff 				if (ascii_strcasecmp((const char *)token, cmd) == 0) {
8643340d773SGleb Smirnoff 					/* Yes. */
865ee67461eSJoseph Mingrone 					print_this = 1;
8663340d773SGleb Smirnoff 					break;
8673340d773SGleb Smirnoff 				}
8683340d773SGleb Smirnoff 			}
8693340d773SGleb Smirnoff 
8703340d773SGleb Smirnoff 			/*
8713340d773SGleb Smirnoff 			 * No - is this a valid response code (3 digits)?
8723340d773SGleb Smirnoff 			 *
8733340d773SGleb Smirnoff 			 * Is this token the response code, or is the next
8743340d773SGleb Smirnoff 			 * token the response code?
8753340d773SGleb Smirnoff 			 */
8763340d773SGleb Smirnoff 			if (flags & RESP_CODE_SECOND_TOKEN) {
8773340d773SGleb Smirnoff 				/*
8783340d773SGleb Smirnoff 				 * Next token - get it.
8793340d773SGleb Smirnoff 				 */
8803340d773SGleb Smirnoff 				idx = fetch_token(ndo, pptr, idx, len, token,
8813340d773SGleb Smirnoff 				    sizeof(token));
8823340d773SGleb Smirnoff 			}
8833340d773SGleb Smirnoff 			if (idx != 0) {
884ee67461eSJoseph Mingrone 				if (ND_ASCII_ISDIGIT(token[0]) && ND_ASCII_ISDIGIT(token[1]) &&
885ee67461eSJoseph Mingrone 				    ND_ASCII_ISDIGIT(token[2]) && token[3] == '\0') {
8863340d773SGleb Smirnoff 					/* Yes. */
887ee67461eSJoseph Mingrone 					print_this = 1;
8883340d773SGleb Smirnoff 				}
8893340d773SGleb Smirnoff 			}
8903340d773SGleb Smirnoff 		}
8913340d773SGleb Smirnoff 	} else {
8923340d773SGleb Smirnoff 		/*
893ee67461eSJoseph Mingrone 		 * Either:
894ee67461eSJoseph Mingrone 		 *
895ee67461eSJoseph Mingrone 		 * 1) This protocol has only request and response lines
896ee67461eSJoseph Mingrone 		 *    (e.g., FTP, where all the data goes over a different
897ee67461eSJoseph Mingrone 		 *    connection); assume the payload is a request or
898ee67461eSJoseph Mingrone 		 *    response.
899ee67461eSJoseph Mingrone 		 *
900ee67461eSJoseph Mingrone 		 * or
901ee67461eSJoseph Mingrone 		 *
902ee67461eSJoseph Mingrone 		 * 2) This protocol is just text, so that we should
903ee67461eSJoseph Mingrone 		 *    always, at minimum, print the first line and,
904ee67461eSJoseph Mingrone 		 *    in verbose mode, print all lines.
9053340d773SGleb Smirnoff 		 */
906ee67461eSJoseph Mingrone 		print_this = 1;
9073340d773SGleb Smirnoff 	}
9083340d773SGleb Smirnoff 
909ee67461eSJoseph Mingrone 	nd_print_protocol_caps(ndo);
9103340d773SGleb Smirnoff 
911ee67461eSJoseph Mingrone 	if (print_this) {
9123340d773SGleb Smirnoff 		/*
9133340d773SGleb Smirnoff 		 * In non-verbose mode, just print the protocol, followed
914ee67461eSJoseph Mingrone 		 * by the first line.
9153340d773SGleb Smirnoff 		 *
9163340d773SGleb Smirnoff 		 * In verbose mode, print lines as text until we run out
9173340d773SGleb Smirnoff 		 * of characters or see something that's not a
9183340d773SGleb Smirnoff 		 * printable-ASCII line.
9193340d773SGleb Smirnoff 		 */
9203340d773SGleb Smirnoff 		if (ndo->ndo_vflag) {
9213340d773SGleb Smirnoff 			/*
9223340d773SGleb Smirnoff 			 * We're going to print all the text lines in the
9233340d773SGleb Smirnoff 			 * request or response; just print the length
9243340d773SGleb Smirnoff 			 * on the first line of the output.
9253340d773SGleb Smirnoff 			 */
926ee67461eSJoseph Mingrone 			ND_PRINT(", length: %u", len);
9273340d773SGleb Smirnoff 			for (idx = 0;
928ee67461eSJoseph Mingrone 			    idx < len && (eol = print_txt_line(ndo, "\n\t", pptr, idx, len)) != 0;
9293340d773SGleb Smirnoff 			    idx = eol)
9303340d773SGleb Smirnoff 				;
9313340d773SGleb Smirnoff 		} else {
9323340d773SGleb Smirnoff 			/*
9333340d773SGleb Smirnoff 			 * Just print the first text line.
9343340d773SGleb Smirnoff 			 */
935ee67461eSJoseph Mingrone 			print_txt_line(ndo, ": ", pptr, 0, len);
9363340d773SGleb Smirnoff 		}
9373340d773SGleb Smirnoff 	}
9383340d773SGleb Smirnoff }
9393340d773SGleb Smirnoff 
940ee67461eSJoseph Mingrone #if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \
941ee67461eSJoseph Mingrone     (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__)) || \
942ee67461eSJoseph Mingrone     (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \
943ee67461eSJoseph Mingrone     (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \
944ee67461eSJoseph Mingrone     (defined(__s390__) || defined(__s390x__) || defined(__zarch__)) || \
945ee67461eSJoseph Mingrone     defined(__vax__)
9463340d773SGleb Smirnoff /*
947ee67461eSJoseph Mingrone  * The processor natively handles unaligned loads, so just use memcpy()
948ee67461eSJoseph Mingrone  * and memcmp(), to enable those optimizations.
949ee67461eSJoseph Mingrone  *
950ee67461eSJoseph Mingrone  * XXX - are those all the x86 tests we need?
951ee67461eSJoseph Mingrone  * XXX - do we need to worry about ARMv1 through ARMv5, which didn't
952ee67461eSJoseph Mingrone  * support unaligned loads, and, if so, do we need to worry about all
953ee67461eSJoseph Mingrone  * of them, or just some of them, e.g. ARMv5?
954ee67461eSJoseph Mingrone  * XXX - are those the only 68k tests we need not to generated
955ee67461eSJoseph Mingrone  * unaligned accesses if the target is the 68000 or 68010?
956ee67461eSJoseph Mingrone  * XXX - are there any tests we don't need, because some definitions are for
957ee67461eSJoseph Mingrone  * compilers that also predefine the GCC symbols?
958ee67461eSJoseph Mingrone  * XXX - do we need to test for both 32-bit and 64-bit versions of those
959ee67461eSJoseph Mingrone  * architectures in all cases?
960ee67461eSJoseph Mingrone  */
961ee67461eSJoseph Mingrone #else
962ee67461eSJoseph Mingrone /*
963ee67461eSJoseph Mingrone  * The processor doesn't natively handle unaligned loads,
964ee67461eSJoseph Mingrone  * and the compiler might "helpfully" optimize memcpy()
965ee67461eSJoseph Mingrone  * and memcmp(), when handed pointers that would normally
966ee67461eSJoseph Mingrone  * be properly aligned, into sequences that assume proper
967ee67461eSJoseph Mingrone  * alignment.
968ee67461eSJoseph Mingrone  *
969ee67461eSJoseph Mingrone  * Do copies and compares of possibly-unaligned data by
970ee67461eSJoseph Mingrone  * calling routines that wrap memcpy() and memcmp(), to
971ee67461eSJoseph Mingrone  * prevent that optimization.
9723340d773SGleb Smirnoff  */
9733340d773SGleb Smirnoff void
9743340d773SGleb Smirnoff unaligned_memcpy(void *p, const void *q, size_t l)
9753340d773SGleb Smirnoff {
9763340d773SGleb Smirnoff 	memcpy(p, q, l);
9773340d773SGleb Smirnoff }
9783340d773SGleb Smirnoff 
9793340d773SGleb Smirnoff /* As with memcpy(), so with memcmp(). */
9803340d773SGleb Smirnoff int
9813340d773SGleb Smirnoff unaligned_memcmp(const void *p, const void *q, size_t l)
9823340d773SGleb Smirnoff {
9833340d773SGleb Smirnoff 	return (memcmp(p, q, l));
9843340d773SGleb Smirnoff }
9853340d773SGleb Smirnoff #endif
9863340d773SGleb Smirnoff 
987