1313024Sglebius/*
2313024Sglebius * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3313024Sglebius *	The Regents of the University of California.  All rights reserved.
4313024Sglebius *
5313024Sglebius * Redistribution and use in source and binary forms, with or without
6313024Sglebius * modification, are permitted provided that: (1) source code distributions
7313024Sglebius * retain the above copyright notice and this paragraph in its entirety, (2)
8313024Sglebius * distributions including binary code include the above copyright notice and
9313024Sglebius * this paragraph in its entirety in the documentation or other materials
10313024Sglebius * provided with the distribution, and (3) all advertising materials mentioning
11313024Sglebius * features or use of this software display the following acknowledgement:
12313024Sglebius * ``This product includes software developed by the University of California,
13313024Sglebius * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14313024Sglebius * the University nor the names of its contributors may be used to endorse
15313024Sglebius * or promote products derived from this software without specific prior
16313024Sglebius * written permission.
17313024Sglebius * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18313024Sglebius * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19313024Sglebius * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20313024Sglebius */
21313024Sglebius
22313024Sglebius/*
23313024Sglebius * txtproto_print() derived from original code by Hannes Gredler
24327234Semaste * (hannes@gredler.at):
25313024Sglebius *
26313024Sglebius * Redistribution and use in source and binary forms, with or without
27313024Sglebius * modification, are permitted provided that: (1) source code
28313024Sglebius * distributions retain the above copyright notice and this paragraph
29313024Sglebius * in its entirety, and (2) distributions including binary code include
30313024Sglebius * the above copyright notice and this paragraph in its entirety in
31313024Sglebius * the documentation or other materials provided with the distribution.
32313024Sglebius * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
33313024Sglebius * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
34313024Sglebius * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
35313024Sglebius * FOR A PARTICULAR PURPOSE.
36313024Sglebius */
37313024Sglebius
38313024Sglebius#ifdef HAVE_CONFIG_H
39313024Sglebius#include "config.h"
40313024Sglebius#endif
41313024Sglebius
42313024Sglebius#include <netdissect-stdinc.h>
43313024Sglebius
44313024Sglebius#include <sys/stat.h>
45313024Sglebius
46313024Sglebius#ifdef HAVE_FCNTL_H
47313024Sglebius#include <fcntl.h>
48313024Sglebius#endif
49313024Sglebius#include <ctype.h>
50313024Sglebius#include <stdio.h>
51313024Sglebius#include <stdarg.h>
52313024Sglebius#include <stdlib.h>
53313024Sglebius#include <string.h>
54313024Sglebius
55313024Sglebius#include "netdissect.h"
56313024Sglebius#include "ascii_strcasecmp.h"
57313024Sglebius#include "timeval-operations.h"
58313024Sglebius
59313024Sglebiusint32_t thiszone;		/* seconds offset from gmt to local time */
60313024Sglebius/* invalid string to print '(invalid)' for malformed or corrupted packets */
61313024Sglebiusconst char istr[] = " (invalid)";
62313024Sglebius
63313024Sglebius/*
64313024Sglebius * timestamp display buffer size, the biggest size of both formats is needed
65313024Sglebius * sizeof("0000000000.000000000") > sizeof("00:00:00.000000000")
66313024Sglebius */
67313024Sglebius#define TS_BUF_SIZE sizeof("0000000000.000000000")
68313024Sglebius
69313024Sglebius#define TOKBUFSIZE 128
70313024Sglebius
71313024Sglebius/*
72313024Sglebius * Print out a character, filtering out the non-printable ones
73313024Sglebius */
74313024Sglebiusvoid
75313024Sglebiusfn_print_char(netdissect_options *ndo, u_char c)
76313024Sglebius{
77313024Sglebius	if (!ND_ISASCII(c)) {
78313024Sglebius		c = ND_TOASCII(c);
79313024Sglebius		ND_PRINT((ndo, "M-"));
80313024Sglebius	}
81313024Sglebius	if (!ND_ISPRINT(c)) {
82313024Sglebius		c ^= 0x40;	/* DEL to ?, others to alpha */
83313024Sglebius		ND_PRINT((ndo, "^"));
84313024Sglebius	}
85313024Sglebius	ND_PRINT((ndo, "%c", c));
86313024Sglebius}
87313024Sglebius
88313024Sglebius/*
89313024Sglebius * Print out a null-terminated filename (or other ascii string).
90313024Sglebius * If ep is NULL, assume no truncation check is needed.
91313024Sglebius * Return true if truncated.
92313024Sglebius * Stop at ep (if given) or before the null char, whichever is first.
93313024Sglebius */
94313024Sglebiusint
95313024Sglebiusfn_print(netdissect_options *ndo,
96313024Sglebius         register const u_char *s, register const u_char *ep)
97313024Sglebius{
98313024Sglebius	register int ret;
99313024Sglebius	register u_char c;
100313024Sglebius
101313024Sglebius	ret = 1;			/* assume truncated */
102313024Sglebius	while (ep == NULL || s < ep) {
103313024Sglebius		c = *s++;
104313024Sglebius		if (c == '\0') {
105313024Sglebius			ret = 0;
106313024Sglebius			break;
107313024Sglebius		}
108313024Sglebius		if (!ND_ISASCII(c)) {
109313024Sglebius			c = ND_TOASCII(c);
110313024Sglebius			ND_PRINT((ndo, "M-"));
111313024Sglebius		}
112313024Sglebius		if (!ND_ISPRINT(c)) {
113313024Sglebius			c ^= 0x40;	/* DEL to ?, others to alpha */
114313024Sglebius			ND_PRINT((ndo, "^"));
115313024Sglebius		}
116313024Sglebius		ND_PRINT((ndo, "%c", c));
117313024Sglebius	}
118313024Sglebius	return(ret);
119313024Sglebius}
120313024Sglebius
121313024Sglebius/*
122313024Sglebius * Print out a null-terminated filename (or other ascii string) from
123356341Scy * a fixed-length field in the packet buffer, or from what remains of
124356341Scy * the packet.
125356341Scy *
126356341Scy * n is the length of the fixed-length field, or the number of bytes
127356341Scy * remaining in the packet based on its on-the-network length.
128356341Scy *
129356341Scy * If ep is non-null, it should point just past the last captured byte
130356341Scy * of the packet, e.g. ndo->ndo_snapend.  If ep is NULL, we assume no
131356341Scy * truncation check, other than the checks of the field length/remaining
132356341Scy * packet data length, is needed.
133356341Scy *
134313024Sglebius * Return the number of bytes of string processed, including the
135356341Scy * terminating null, if not truncated; as the terminating null is
136356341Scy * included in the count, and as there must be a terminating null,
137356341Scy * this will always be non-zero.  Return 0 if truncated.
138313024Sglebius */
139313024Sglebiusu_int
140313024Sglebiusfn_printztn(netdissect_options *ndo,
141313024Sglebius         register const u_char *s, register u_int n, register const u_char *ep)
142313024Sglebius{
143313024Sglebius	register u_int bytes;
144313024Sglebius	register u_char c;
145313024Sglebius
146313024Sglebius	bytes = 0;
147313024Sglebius	for (;;) {
148313024Sglebius		if (n == 0 || (ep != NULL && s >= ep)) {
149313024Sglebius			/*
150313024Sglebius			 * Truncated.  This includes "no null before we
151356341Scy			 * got to the end of the fixed-length buffer or
152356341Scy			 * the end of the packet".
153313024Sglebius			 *
154313024Sglebius			 * XXX - BOOTP says "null-terminated", which
155313024Sglebius			 * means the maximum length of the string, in
156313024Sglebius			 * bytes, is 1 less than the size of the buffer,
157313024Sglebius			 * as there must always be a terminating null.
158313024Sglebius			 */
159313024Sglebius			bytes = 0;
160313024Sglebius			break;
161313024Sglebius		}
162313024Sglebius
163313024Sglebius		c = *s++;
164313024Sglebius		bytes++;
165313024Sglebius		n--;
166313024Sglebius		if (c == '\0') {
167313024Sglebius			/* End of string */
168313024Sglebius			break;
169313024Sglebius		}
170313024Sglebius		if (!ND_ISASCII(c)) {
171313024Sglebius			c = ND_TOASCII(c);
172313024Sglebius			ND_PRINT((ndo, "M-"));
173313024Sglebius		}
174313024Sglebius		if (!ND_ISPRINT(c)) {
175313024Sglebius			c ^= 0x40;	/* DEL to ?, others to alpha */
176313024Sglebius			ND_PRINT((ndo, "^"));
177313024Sglebius		}
178313024Sglebius		ND_PRINT((ndo, "%c", c));
179313024Sglebius	}
180313024Sglebius	return(bytes);
181313024Sglebius}
182313024Sglebius
183313024Sglebius/*
184313024Sglebius * Print out a counted filename (or other ascii string).
185313024Sglebius * If ep is NULL, assume no truncation check is needed.
186313024Sglebius * Return true if truncated.
187313024Sglebius * Stop at ep (if given) or after n bytes, whichever is first.
188313024Sglebius */
189313024Sglebiusint
190313024Sglebiusfn_printn(netdissect_options *ndo,
191313024Sglebius          register const u_char *s, register u_int n, register const u_char *ep)
192313024Sglebius{
193313024Sglebius	register u_char c;
194313024Sglebius
195313024Sglebius	while (n > 0 && (ep == NULL || s < ep)) {
196313024Sglebius		n--;
197313024Sglebius		c = *s++;
198313024Sglebius		if (!ND_ISASCII(c)) {
199313024Sglebius			c = ND_TOASCII(c);
200313024Sglebius			ND_PRINT((ndo, "M-"));
201313024Sglebius		}
202313024Sglebius		if (!ND_ISPRINT(c)) {
203313024Sglebius			c ^= 0x40;	/* DEL to ?, others to alpha */
204313024Sglebius			ND_PRINT((ndo, "^"));
205313024Sglebius		}
206313024Sglebius		ND_PRINT((ndo, "%c", c));
207313024Sglebius	}
208313024Sglebius	return (n == 0) ? 0 : 1;
209313024Sglebius}
210313024Sglebius
211313024Sglebius/*
212313024Sglebius * Print out a null-padded filename (or other ascii string).
213313024Sglebius * If ep is NULL, assume no truncation check is needed.
214313024Sglebius * Return true if truncated.
215313024Sglebius * Stop at ep (if given) or after n bytes or before the null char,
216313024Sglebius * whichever is first.
217313024Sglebius */
218313024Sglebiusint
219313024Sglebiusfn_printzp(netdissect_options *ndo,
220313024Sglebius           register const u_char *s, register u_int n,
221313024Sglebius           register const u_char *ep)
222313024Sglebius{
223313024Sglebius	register int ret;
224313024Sglebius	register u_char c;
225313024Sglebius
226313024Sglebius	ret = 1;			/* assume truncated */
227313024Sglebius	while (n > 0 && (ep == NULL || s < ep)) {
228313024Sglebius		n--;
229313024Sglebius		c = *s++;
230313024Sglebius		if (c == '\0') {
231313024Sglebius			ret = 0;
232313024Sglebius			break;
233313024Sglebius		}
234313024Sglebius		if (!ND_ISASCII(c)) {
235313024Sglebius			c = ND_TOASCII(c);
236313024Sglebius			ND_PRINT((ndo, "M-"));
237313024Sglebius		}
238313024Sglebius		if (!ND_ISPRINT(c)) {
239313024Sglebius			c ^= 0x40;	/* DEL to ?, others to alpha */
240313024Sglebius			ND_PRINT((ndo, "^"));
241313024Sglebius		}
242313024Sglebius		ND_PRINT((ndo, "%c", c));
243313024Sglebius	}
244313024Sglebius	return (n == 0) ? 0 : ret;
245313024Sglebius}
246313024Sglebius
247313024Sglebius/*
248313024Sglebius * Format the timestamp
249313024Sglebius */
250313024Sglebiusstatic char *
251313024Sglebiusts_format(netdissect_options *ndo
252313024Sglebius#ifndef HAVE_PCAP_SET_TSTAMP_PRECISION
253313024Sglebius_U_
254313024Sglebius#endif
255313024Sglebius, int sec, int usec, char *buf)
256313024Sglebius{
257313024Sglebius	const char *format;
258313024Sglebius
259313024Sglebius#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
260313024Sglebius	switch (ndo->ndo_tstamp_precision) {
261313024Sglebius
262313024Sglebius	case PCAP_TSTAMP_PRECISION_MICRO:
263313024Sglebius		format = "%02d:%02d:%02d.%06u";
264313024Sglebius		break;
265313024Sglebius
266313024Sglebius	case PCAP_TSTAMP_PRECISION_NANO:
267313024Sglebius		format = "%02d:%02d:%02d.%09u";
268313024Sglebius		break;
269313024Sglebius
270313024Sglebius	default:
271313024Sglebius		format = "%02d:%02d:%02d.{unknown}";
272313024Sglebius		break;
273313024Sglebius	}
274313024Sglebius#else
275313024Sglebius	format = "%02d:%02d:%02d.%06u";
276313024Sglebius#endif
277313024Sglebius
278313024Sglebius	snprintf(buf, TS_BUF_SIZE, format,
279313024Sglebius                 sec / 3600, (sec % 3600) / 60, sec % 60, usec);
280313024Sglebius
281313024Sglebius        return buf;
282313024Sglebius}
283313024Sglebius
284313024Sglebius/*
285313024Sglebius * Format the timestamp - Unix timeval style
286313024Sglebius */
287313024Sglebiusstatic char *
288313024Sglebiusts_unix_format(netdissect_options *ndo
289313024Sglebius#ifndef HAVE_PCAP_SET_TSTAMP_PRECISION
290313024Sglebius_U_
291313024Sglebius#endif
292313024Sglebius, int sec, int usec, char *buf)
293313024Sglebius{
294313024Sglebius	const char *format;
295313024Sglebius
296313024Sglebius#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
297313024Sglebius	switch (ndo->ndo_tstamp_precision) {
298313024Sglebius
299313024Sglebius	case PCAP_TSTAMP_PRECISION_MICRO:
300313024Sglebius		format = "%u.%06u";
301313024Sglebius		break;
302313024Sglebius
303313024Sglebius	case PCAP_TSTAMP_PRECISION_NANO:
304313024Sglebius		format = "%u.%09u";
305313024Sglebius		break;
306313024Sglebius
307313024Sglebius	default:
308313024Sglebius		format = "%u.{unknown}";
309313024Sglebius		break;
310313024Sglebius	}
311313024Sglebius#else
312313024Sglebius	format = "%u.%06u";
313313024Sglebius#endif
314313024Sglebius
315313024Sglebius	snprintf(buf, TS_BUF_SIZE, format,
316313024Sglebius		 (unsigned)sec, (unsigned)usec);
317313024Sglebius
318313024Sglebius	return buf;
319313024Sglebius}
320313024Sglebius
321313024Sglebius/*
322313024Sglebius * Print the timestamp
323313024Sglebius */
324313024Sglebiusvoid
325313024Sglebiusts_print(netdissect_options *ndo,
326313024Sglebius         register const struct timeval *tvp)
327313024Sglebius{
328313024Sglebius	register int s;
329313024Sglebius	struct tm *tm;
330313024Sglebius	time_t Time;
331313024Sglebius	char buf[TS_BUF_SIZE];
332313024Sglebius	static struct timeval tv_ref;
333313024Sglebius	struct timeval tv_result;
334313024Sglebius	int negative_offset;
335313024Sglebius	int nano_prec;
336313024Sglebius
337313024Sglebius	switch (ndo->ndo_tflag) {
338313024Sglebius
339313024Sglebius	case 0: /* Default */
340313024Sglebius		s = (tvp->tv_sec + thiszone) % 86400;
341313024Sglebius		ND_PRINT((ndo, "%s ", ts_format(ndo, s, tvp->tv_usec, buf)));
342313024Sglebius		break;
343313024Sglebius
344313024Sglebius	case 1: /* No time stamp */
345313024Sglebius		break;
346313024Sglebius
347313024Sglebius	case 2: /* Unix timeval style */
348313024Sglebius		ND_PRINT((ndo, "%s ", ts_unix_format(ndo,
349313024Sglebius			  tvp->tv_sec, tvp->tv_usec, buf)));
350313024Sglebius		break;
351313024Sglebius
352313024Sglebius	case 3: /* Microseconds/nanoseconds since previous packet */
353313024Sglebius        case 5: /* Microseconds/nanoseconds since first packet */
354313024Sglebius#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
355313024Sglebius		switch (ndo->ndo_tstamp_precision) {
356313024Sglebius		case PCAP_TSTAMP_PRECISION_MICRO:
357313024Sglebius			nano_prec = 0;
358313024Sglebius			break;
359313024Sglebius		case PCAP_TSTAMP_PRECISION_NANO:
360313024Sglebius			nano_prec = 1;
361313024Sglebius			break;
362313024Sglebius		default:
363313024Sglebius			nano_prec = 0;
364313024Sglebius			break;
365313024Sglebius		}
366313024Sglebius#else
367313024Sglebius		nano_prec = 0;
368313024Sglebius#endif
369313024Sglebius		if (!(netdissect_timevalisset(&tv_ref)))
370313024Sglebius			tv_ref = *tvp; /* set timestamp for first packet */
371313024Sglebius
372313024Sglebius		negative_offset = netdissect_timevalcmp(tvp, &tv_ref, <);
373313024Sglebius		if (negative_offset)
374313024Sglebius			netdissect_timevalsub(&tv_ref, tvp, &tv_result, nano_prec);
375313024Sglebius		else
376313024Sglebius			netdissect_timevalsub(tvp, &tv_ref, &tv_result, nano_prec);
377313024Sglebius
378313024Sglebius		ND_PRINT((ndo, (negative_offset ? "-" : " ")));
379313024Sglebius
380313024Sglebius		ND_PRINT((ndo, "%s ", ts_format(ndo,
381313024Sglebius			  tv_result.tv_sec, tv_result.tv_usec, buf)));
382313024Sglebius
383313024Sglebius                if (ndo->ndo_tflag == 3)
384313024Sglebius			tv_ref = *tvp; /* set timestamp for previous packet */
385313024Sglebius		break;
386313024Sglebius
387313024Sglebius	case 4: /* Default + Date */
388313024Sglebius		s = (tvp->tv_sec + thiszone) % 86400;
389313024Sglebius		Time = (tvp->tv_sec + thiszone) - s;
390313024Sglebius		tm = gmtime (&Time);
391313024Sglebius		if (!tm)
392313024Sglebius			ND_PRINT((ndo, "Date fail  "));
393313024Sglebius		else
394313024Sglebius			ND_PRINT((ndo, "%04d-%02d-%02d %s ",
395313024Sglebius                               tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
396313024Sglebius                               ts_format(ndo, s, tvp->tv_usec, buf)));
397313024Sglebius		break;
398313024Sglebius	}
399313024Sglebius}
400313024Sglebius
401313024Sglebius/*
402313024Sglebius * Print an unsigned relative number of seconds (e.g. hold time, prune timer)
403313024Sglebius * in the form 5m1s.  This does no truncation, so 32230861 seconds
404313024Sglebius * is represented as 1y1w1d1h1m1s.
405313024Sglebius */
406313024Sglebiusvoid
407313024Sglebiusunsigned_relts_print(netdissect_options *ndo,
408313024Sglebius                     uint32_t secs)
409313024Sglebius{
410313024Sglebius	static const char *lengths[] = {"y", "w", "d", "h", "m", "s"};
411313024Sglebius	static const u_int seconds[] = {31536000, 604800, 86400, 3600, 60, 1};
412313024Sglebius	const char **l = lengths;
413313024Sglebius	const u_int *s = seconds;
414313024Sglebius
415313024Sglebius	if (secs == 0) {
416313024Sglebius		ND_PRINT((ndo, "0s"));
417313024Sglebius		return;
418313024Sglebius	}
419313024Sglebius	while (secs > 0) {
420313024Sglebius		if (secs >= *s) {
421313024Sglebius			ND_PRINT((ndo, "%d%s", secs / *s, *l));
422313024Sglebius			secs -= (secs / *s) * *s;
423313024Sglebius		}
424313024Sglebius		s++;
425313024Sglebius		l++;
426313024Sglebius	}
427313024Sglebius}
428313024Sglebius
429313024Sglebius/*
430313024Sglebius * Print a signed relative number of seconds (e.g. hold time, prune timer)
431313024Sglebius * in the form 5m1s.  This does no truncation, so 32230861 seconds
432313024Sglebius * is represented as 1y1w1d1h1m1s.
433313024Sglebius */
434313024Sglebiusvoid
435313024Sglebiussigned_relts_print(netdissect_options *ndo,
436313024Sglebius                   int32_t secs)
437313024Sglebius{
438313024Sglebius	if (secs < 0) {
439313024Sglebius		ND_PRINT((ndo, "-"));
440313024Sglebius		if (secs == INT32_MIN) {
441313024Sglebius			/*
442313024Sglebius			 * -2^31; you can't fit its absolute value into
443313024Sglebius			 * a 32-bit signed integer.
444313024Sglebius			 *
445313024Sglebius			 * Just directly pass said absolute value to
446313024Sglebius			 * unsigned_relts_print() directly.
447313024Sglebius			 *
448313024Sglebius			 * (XXX - does ISO C guarantee that -(-2^n),
449313024Sglebius			 * when calculated and cast to an n-bit unsigned
450313024Sglebius			 * integer type, will have the value 2^n?)
451313024Sglebius			 */
452313024Sglebius			unsigned_relts_print(ndo, 2147483648U);
453313024Sglebius		} else {
454313024Sglebius			/*
455313024Sglebius			 * We now know -secs will fit into an int32_t;
456313024Sglebius			 * negate it and pass that to unsigned_relts_print().
457313024Sglebius			 */
458313024Sglebius			unsigned_relts_print(ndo, -secs);
459313024Sglebius		}
460313024Sglebius		return;
461313024Sglebius	}
462313024Sglebius	unsigned_relts_print(ndo, secs);
463313024Sglebius}
464313024Sglebius
465313024Sglebius/*
466313024Sglebius *  this is a generic routine for printing unknown data;
467313024Sglebius *  we pass on the linefeed plus indentation string to
468313024Sglebius *  get a proper output - returns 0 on error
469313024Sglebius */
470313024Sglebius
471313024Sglebiusint
472313024Sglebiusprint_unknown_data(netdissect_options *ndo, const u_char *cp,const char *ident,int len)
473313024Sglebius{
474313024Sglebius	if (len < 0) {
475313024Sglebius          ND_PRINT((ndo,"%sDissector error: print_unknown_data called with negative length",
476313024Sglebius		    ident));
477313024Sglebius		return(0);
478313024Sglebius	}
479313024Sglebius	if (ndo->ndo_snapend - cp < len)
480313024Sglebius		len = ndo->ndo_snapend - cp;
481313024Sglebius	if (len < 0) {
482313024Sglebius          ND_PRINT((ndo,"%sDissector error: print_unknown_data called with pointer past end of packet",
483313024Sglebius		    ident));
484313024Sglebius		return(0);
485313024Sglebius	}
486313024Sglebius        hex_print(ndo, ident,cp,len);
487313024Sglebius	return(1); /* everything is ok */
488313024Sglebius}
489313024Sglebius
490313024Sglebius/*
491313024Sglebius * Convert a token value to a string; use "fmt" if not found.
492313024Sglebius */
493313024Sglebiusconst char *
494313024Sglebiustok2strbuf(register const struct tok *lp, register const char *fmt,
495313024Sglebius	   register u_int v, char *buf, size_t bufsize)
496313024Sglebius{
497313024Sglebius	if (lp != NULL) {
498313024Sglebius		while (lp->s != NULL) {
499313024Sglebius			if (lp->v == v)
500313024Sglebius				return (lp->s);
501313024Sglebius			++lp;
502313024Sglebius		}
503313024Sglebius	}
504313024Sglebius	if (fmt == NULL)
505313024Sglebius		fmt = "#%d";
506313024Sglebius
507313024Sglebius	(void)snprintf(buf, bufsize, fmt, v);
508313024Sglebius	return (const char *)buf;
509313024Sglebius}
510313024Sglebius
511313024Sglebius/*
512313024Sglebius * Convert a token value to a string; use "fmt" if not found.
513313024Sglebius */
514313024Sglebiusconst char *
515313024Sglebiustok2str(register const struct tok *lp, register const char *fmt,
516313024Sglebius	register u_int v)
517313024Sglebius{
518313024Sglebius	static char buf[4][TOKBUFSIZE];
519313024Sglebius	static int idx = 0;
520313024Sglebius	char *ret;
521313024Sglebius
522313024Sglebius	ret = buf[idx];
523313024Sglebius	idx = (idx+1) & 3;
524313024Sglebius	return tok2strbuf(lp, fmt, v, ret, sizeof(buf[0]));
525313024Sglebius}
526313024Sglebius
527313024Sglebius/*
528313024Sglebius * Convert a bit token value to a string; use "fmt" if not found.
529313024Sglebius * this is useful for parsing bitfields, the output strings are seperated
530313024Sglebius * if the s field is positive.
531313024Sglebius */
532313024Sglebiusstatic char *
533313024Sglebiusbittok2str_internal(register const struct tok *lp, register const char *fmt,
534313024Sglebius	   register u_int v, const char *sep)
535313024Sglebius{
536327234Semaste        static char buf[1024+1]; /* our string buffer */
537327234Semaste        char *bufp = buf;
538327234Semaste        size_t space_left = sizeof(buf), string_size;
539313024Sglebius        register u_int rotbit; /* this is the bit we rotate through all bitpositions */
540313024Sglebius        register u_int tokval;
541313024Sglebius        const char * sepstr = "";
542313024Sglebius
543313024Sglebius	while (lp != NULL && lp->s != NULL) {
544313024Sglebius            tokval=lp->v;   /* load our first value */
545313024Sglebius            rotbit=1;
546313024Sglebius            while (rotbit != 0) {
547313024Sglebius                /*
548313024Sglebius                 * lets AND the rotating bit with our token value
549313024Sglebius                 * and see if we have got a match
550313024Sglebius                 */
551313024Sglebius		if (tokval == (v&rotbit)) {
552313024Sglebius                    /* ok we have found something */
553327234Semaste                    if (space_left <= 1)
554327234Semaste                        return (buf); /* only enough room left for NUL, if that */
555327234Semaste                    string_size = strlcpy(bufp, sepstr, space_left);
556327234Semaste                    if (string_size >= space_left)
557327234Semaste                        return (buf);    /* we ran out of room */
558327234Semaste                    bufp += string_size;
559327234Semaste                    space_left -= string_size;
560327234Semaste                    if (space_left <= 1)
561327234Semaste                        return (buf); /* only enough room left for NUL, if that */
562327234Semaste                    string_size = strlcpy(bufp, lp->s, space_left);
563327234Semaste                    if (string_size >= space_left)
564327234Semaste                        return (buf);    /* we ran out of room */
565327234Semaste                    bufp += string_size;
566327234Semaste                    space_left -= string_size;
567313024Sglebius                    sepstr = sep;
568313024Sglebius                    break;
569313024Sglebius                }
570313024Sglebius                rotbit=rotbit<<1; /* no match - lets shift and try again */
571313024Sglebius            }
572313024Sglebius            lp++;
573313024Sglebius	}
574313024Sglebius
575327234Semaste        if (bufp == buf)
576313024Sglebius            /* bummer - lets print the "unknown" message as advised in the fmt string if we got one */
577313024Sglebius            (void)snprintf(buf, sizeof(buf), fmt == NULL ? "#%08x" : fmt, v);
578313024Sglebius        return (buf);
579313024Sglebius}
580313024Sglebius
581313024Sglebius/*
582313024Sglebius * Convert a bit token value to a string; use "fmt" if not found.
583313024Sglebius * this is useful for parsing bitfields, the output strings are not seperated.
584313024Sglebius */
585313024Sglebiuschar *
586313024Sglebiusbittok2str_nosep(register const struct tok *lp, register const char *fmt,
587313024Sglebius	   register u_int v)
588313024Sglebius{
589313024Sglebius    return (bittok2str_internal(lp, fmt, v, ""));
590313024Sglebius}
591313024Sglebius
592313024Sglebius/*
593313024Sglebius * Convert a bit token value to a string; use "fmt" if not found.
594313024Sglebius * this is useful for parsing bitfields, the output strings are comma seperated.
595313024Sglebius */
596313024Sglebiuschar *
597313024Sglebiusbittok2str(register const struct tok *lp, register const char *fmt,
598313024Sglebius	   register u_int v)
599313024Sglebius{
600313024Sglebius    return (bittok2str_internal(lp, fmt, v, ", "));
601313024Sglebius}
602313024Sglebius
603313024Sglebius/*
604313024Sglebius * Convert a value to a string using an array; the macro
605313024Sglebius * tok2strary() in <netdissect.h> is the public interface to
606313024Sglebius * this function and ensures that the second argument is
607313024Sglebius * correct for bounds-checking.
608313024Sglebius */
609313024Sglebiusconst char *
610313024Sglebiustok2strary_internal(register const char **lp, int n, register const char *fmt,
611313024Sglebius	register int v)
612313024Sglebius{
613313024Sglebius	static char buf[TOKBUFSIZE];
614313024Sglebius
615313024Sglebius	if (v >= 0 && v < n && lp[v] != NULL)
616313024Sglebius		return lp[v];
617313024Sglebius	if (fmt == NULL)
618313024Sglebius		fmt = "#%d";
619313024Sglebius	(void)snprintf(buf, sizeof(buf), fmt, v);
620313024Sglebius	return (buf);
621313024Sglebius}
622313024Sglebius
623313024Sglebius/*
624313024Sglebius * Convert a 32-bit netmask to prefixlen if possible
625313024Sglebius * the function returns the prefix-len; if plen == -1
626313024Sglebius * then conversion was not possible;
627313024Sglebius */
628313024Sglebius
629313024Sglebiusint
630313024Sglebiusmask2plen(uint32_t mask)
631313024Sglebius{
632313024Sglebius	uint32_t bitmasks[33] = {
633313024Sglebius		0x00000000,
634313024Sglebius		0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
635313024Sglebius		0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
636313024Sglebius		0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
637313024Sglebius		0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
638313024Sglebius		0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
639313024Sglebius		0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
640313024Sglebius		0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
641313024Sglebius		0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff
642313024Sglebius	};
643313024Sglebius	int prefix_len = 32;
644313024Sglebius
645313024Sglebius	/* let's see if we can transform the mask into a prefixlen */
646313024Sglebius	while (prefix_len >= 0) {
647313024Sglebius		if (bitmasks[prefix_len] == mask)
648313024Sglebius			break;
649313024Sglebius		prefix_len--;
650313024Sglebius	}
651313024Sglebius	return (prefix_len);
652313024Sglebius}
653313024Sglebius
654313024Sglebiusint
655313024Sglebiusmask62plen(const u_char *mask)
656313024Sglebius{
657313024Sglebius	u_char bitmasks[9] = {
658313024Sglebius		0x00,
659313024Sglebius		0x80, 0xc0, 0xe0, 0xf0,
660313024Sglebius		0xf8, 0xfc, 0xfe, 0xff
661313024Sglebius	};
662313024Sglebius	int byte;
663313024Sglebius	int cidr_len = 0;
664313024Sglebius
665313024Sglebius	for (byte = 0; byte < 16; byte++) {
666313024Sglebius		u_int bits;
667313024Sglebius
668313024Sglebius		for (bits = 0; bits < (sizeof (bitmasks) / sizeof (bitmasks[0])); bits++) {
669313024Sglebius			if (mask[byte] == bitmasks[bits]) {
670313024Sglebius				cidr_len += bits;
671313024Sglebius				break;
672313024Sglebius			}
673313024Sglebius		}
674313024Sglebius
675313024Sglebius		if (mask[byte] != 0xff)
676313024Sglebius			break;
677313024Sglebius	}
678313024Sglebius	return (cidr_len);
679313024Sglebius}
680313024Sglebius
681313024Sglebius/*
682313024Sglebius * Routine to print out information for text-based protocols such as FTP,
683313024Sglebius * HTTP, SMTP, RTSP, SIP, ....
684313024Sglebius */
685313024Sglebius#define MAX_TOKEN	128
686313024Sglebius
687313024Sglebius/*
688313024Sglebius * Fetch a token from a packet, starting at the specified index,
689313024Sglebius * and return the length of the token.
690313024Sglebius *
691313024Sglebius * Returns 0 on error; yes, this is indistinguishable from an empty
692313024Sglebius * token, but an "empty token" isn't a valid token - it just means
693313024Sglebius * either a space character at the beginning of the line (this
694313024Sglebius * includes a blank line) or no more tokens remaining on the line.
695313024Sglebius */
696313024Sglebiusstatic int
697313024Sglebiusfetch_token(netdissect_options *ndo, const u_char *pptr, u_int idx, u_int len,
698313024Sglebius    u_char *tbuf, size_t tbuflen)
699313024Sglebius{
700313024Sglebius	size_t toklen = 0;
701313024Sglebius
702313024Sglebius	for (; idx < len; idx++) {
703313024Sglebius		if (!ND_TTEST(*(pptr + idx))) {
704313024Sglebius			/* ran past end of captured data */
705313024Sglebius			return (0);
706313024Sglebius		}
707313024Sglebius		if (!isascii(*(pptr + idx))) {
708313024Sglebius			/* not an ASCII character */
709313024Sglebius			return (0);
710313024Sglebius		}
711313024Sglebius		if (isspace(*(pptr + idx))) {
712313024Sglebius			/* end of token */
713313024Sglebius			break;
714313024Sglebius		}
715313024Sglebius		if (!isprint(*(pptr + idx))) {
716313024Sglebius			/* not part of a command token or response code */
717313024Sglebius			return (0);
718313024Sglebius		}
719313024Sglebius		if (toklen + 2 > tbuflen) {
720313024Sglebius			/* no room for this character and terminating '\0' */
721313024Sglebius			return (0);
722313024Sglebius		}
723313024Sglebius		tbuf[toklen] = *(pptr + idx);
724313024Sglebius		toklen++;
725313024Sglebius	}
726313024Sglebius	if (toklen == 0) {
727313024Sglebius		/* no token */
728313024Sglebius		return (0);
729313024Sglebius	}
730313024Sglebius	tbuf[toklen] = '\0';
731313024Sglebius
732313024Sglebius	/*
733313024Sglebius	 * Skip past any white space after the token, until we see
734313024Sglebius	 * an end-of-line (CR or LF).
735313024Sglebius	 */
736313024Sglebius	for (; idx < len; idx++) {
737313024Sglebius		if (!ND_TTEST(*(pptr + idx))) {
738313024Sglebius			/* ran past end of captured data */
739313024Sglebius			break;
740313024Sglebius		}
741313024Sglebius		if (*(pptr + idx) == '\r' || *(pptr + idx) == '\n') {
742313024Sglebius			/* end of line */
743313024Sglebius			break;
744313024Sglebius		}
745313024Sglebius		if (!isascii(*(pptr + idx)) || !isprint(*(pptr + idx))) {
746313024Sglebius			/* not a printable ASCII character */
747313024Sglebius			break;
748313024Sglebius		}
749313024Sglebius		if (!isspace(*(pptr + idx))) {
750313024Sglebius			/* beginning of next token */
751313024Sglebius			break;
752313024Sglebius		}
753313024Sglebius	}
754313024Sglebius	return (idx);
755313024Sglebius}
756313024Sglebius
757313024Sglebius/*
758313024Sglebius * Scan a buffer looking for a line ending - LF or CR-LF.
759313024Sglebius * Return the index of the character after the line ending or 0 if
760313024Sglebius * we encounter a non-ASCII or non-printable character or don't find
761313024Sglebius * the line ending.
762313024Sglebius */
763313024Sglebiusstatic u_int
764313024Sglebiusprint_txt_line(netdissect_options *ndo, const char *protoname,
765313024Sglebius    const char *prefix, const u_char *pptr, u_int idx, u_int len)
766313024Sglebius{
767313024Sglebius	u_int startidx;
768313024Sglebius	u_int linelen;
769313024Sglebius
770313024Sglebius	startidx = idx;
771313024Sglebius	while (idx < len) {
772313024Sglebius		ND_TCHECK(*(pptr+idx));
773313024Sglebius		if (*(pptr+idx) == '\n') {
774313024Sglebius			/*
775313024Sglebius			 * LF without CR; end of line.
776313024Sglebius			 * Skip the LF and print the line, with the
777313024Sglebius			 * exception of the LF.
778313024Sglebius			 */
779313024Sglebius			linelen = idx - startidx;
780313024Sglebius			idx++;
781313024Sglebius			goto print;
782313024Sglebius		} else if (*(pptr+idx) == '\r') {
783313024Sglebius			/* CR - any LF? */
784313024Sglebius			if ((idx+1) >= len) {
785313024Sglebius				/* not in this packet */
786313024Sglebius				return (0);
787313024Sglebius			}
788313024Sglebius			ND_TCHECK(*(pptr+idx+1));
789313024Sglebius			if (*(pptr+idx+1) == '\n') {
790313024Sglebius				/*
791313024Sglebius				 * CR-LF; end of line.
792313024Sglebius				 * Skip the CR-LF and print the line, with
793313024Sglebius				 * the exception of the CR-LF.
794313024Sglebius				 */
795313024Sglebius				linelen = idx - startidx;
796313024Sglebius				idx += 2;
797313024Sglebius				goto print;
798313024Sglebius			}
799313024Sglebius
800313024Sglebius			/*
801313024Sglebius			 * CR followed by something else; treat this
802313024Sglebius			 * as if it were binary data, and don't print
803313024Sglebius			 * it.
804313024Sglebius			 */
805313024Sglebius			return (0);
806313024Sglebius		} else if (!isascii(*(pptr+idx)) ||
807313024Sglebius		    (!isprint(*(pptr+idx)) && *(pptr+idx) != '\t')) {
808313024Sglebius			/*
809313024Sglebius			 * Not a printable ASCII character and not a tab;
810313024Sglebius			 * treat this as if it were binary data, and
811313024Sglebius			 * don't print it.
812313024Sglebius			 */
813313024Sglebius			return (0);
814313024Sglebius		}
815313024Sglebius		idx++;
816313024Sglebius	}
817313024Sglebius
818313024Sglebius	/*
819313024Sglebius	 * All printable ASCII, but no line ending after that point
820313024Sglebius	 * in the buffer; treat this as if it were truncated.
821313024Sglebius	 */
822313024Sglebiustrunc:
823313024Sglebius	linelen = idx - startidx;
824313024Sglebius	ND_PRINT((ndo, "%s%.*s[!%s]", prefix, (int)linelen, pptr + startidx,
825313024Sglebius	    protoname));
826313024Sglebius	return (0);
827313024Sglebius
828313024Sglebiusprint:
829313024Sglebius	ND_PRINT((ndo, "%s%.*s", prefix, (int)linelen, pptr + startidx));
830313024Sglebius	return (idx);
831313024Sglebius}
832313024Sglebius
833313024Sglebiusvoid
834313024Sglebiustxtproto_print(netdissect_options *ndo, const u_char *pptr, u_int len,
835313024Sglebius    const char *protoname, const char **cmds, u_int flags)
836313024Sglebius{
837313024Sglebius	u_int idx, eol;
838313024Sglebius	u_char token[MAX_TOKEN+1];
839313024Sglebius	const char *cmd;
840313024Sglebius	int is_reqresp = 0;
841313024Sglebius	const char *pnp;
842313024Sglebius
843313024Sglebius	if (cmds != NULL) {
844313024Sglebius		/*
845313024Sglebius		 * This protocol has more than just request and
846313024Sglebius		 * response lines; see whether this looks like a
847313024Sglebius		 * request or response.
848313024Sglebius		 */
849313024Sglebius		idx = fetch_token(ndo, pptr, 0, len, token, sizeof(token));
850313024Sglebius		if (idx != 0) {
851313024Sglebius			/* Is this a valid request name? */
852313024Sglebius			while ((cmd = *cmds++) != NULL) {
853313024Sglebius				if (ascii_strcasecmp((const char *)token, cmd) == 0) {
854313024Sglebius					/* Yes. */
855313024Sglebius					is_reqresp = 1;
856313024Sglebius					break;
857313024Sglebius				}
858313024Sglebius			}
859313024Sglebius
860313024Sglebius			/*
861313024Sglebius			 * No - is this a valid response code (3 digits)?
862313024Sglebius			 *
863313024Sglebius			 * Is this token the response code, or is the next
864313024Sglebius			 * token the response code?
865313024Sglebius			 */
866313024Sglebius			if (flags & RESP_CODE_SECOND_TOKEN) {
867313024Sglebius				/*
868313024Sglebius				 * Next token - get it.
869313024Sglebius				 */
870313024Sglebius				idx = fetch_token(ndo, pptr, idx, len, token,
871313024Sglebius				    sizeof(token));
872313024Sglebius			}
873313024Sglebius			if (idx != 0) {
874313024Sglebius				if (isdigit(token[0]) && isdigit(token[1]) &&
875313024Sglebius				    isdigit(token[2]) && token[3] == '\0') {
876313024Sglebius					/* Yes. */
877313024Sglebius					is_reqresp = 1;
878313024Sglebius				}
879313024Sglebius			}
880313024Sglebius		}
881313024Sglebius	} else {
882313024Sglebius		/*
883313024Sglebius		 * This protocol has only request and response lines
884313024Sglebius		 * (e.g., FTP, where all the data goes over a
885313024Sglebius		 * different connection); assume the payload is
886313024Sglebius		 * a request or response.
887313024Sglebius		 */
888313024Sglebius		is_reqresp = 1;
889313024Sglebius	}
890313024Sglebius
891313024Sglebius	/* Capitalize the protocol name */
892313024Sglebius	for (pnp = protoname; *pnp != '\0'; pnp++)
893313024Sglebius		ND_PRINT((ndo, "%c", toupper((u_char)*pnp)));
894313024Sglebius
895313024Sglebius	if (is_reqresp) {
896313024Sglebius		/*
897313024Sglebius		 * In non-verbose mode, just print the protocol, followed
898313024Sglebius		 * by the first line as the request or response info.
899313024Sglebius		 *
900313024Sglebius		 * In verbose mode, print lines as text until we run out
901313024Sglebius		 * of characters or see something that's not a
902313024Sglebius		 * printable-ASCII line.
903313024Sglebius		 */
904313024Sglebius		if (ndo->ndo_vflag) {
905313024Sglebius			/*
906313024Sglebius			 * We're going to print all the text lines in the
907313024Sglebius			 * request or response; just print the length
908313024Sglebius			 * on the first line of the output.
909313024Sglebius			 */
910313024Sglebius			ND_PRINT((ndo, ", length: %u", len));
911313024Sglebius			for (idx = 0;
912313024Sglebius			    idx < len && (eol = print_txt_line(ndo, protoname, "\n\t", pptr, idx, len)) != 0;
913313024Sglebius			    idx = eol)
914313024Sglebius				;
915313024Sglebius		} else {
916313024Sglebius			/*
917313024Sglebius			 * Just print the first text line.
918313024Sglebius			 */
919313024Sglebius			print_txt_line(ndo, protoname, ": ", pptr, 0, len);
920313024Sglebius		}
921313024Sglebius	}
922313024Sglebius}
923313024Sglebius
924313024Sglebiusvoid
925313024Sglebiussafeputs(netdissect_options *ndo,
926313024Sglebius         const u_char *s, const u_int maxlen)
927313024Sglebius{
928313024Sglebius	u_int idx = 0;
929313024Sglebius
930327234Semaste	while (idx < maxlen && *s) {
931313024Sglebius		safeputchar(ndo, *s);
932313024Sglebius		idx++;
933313024Sglebius		s++;
934313024Sglebius	}
935313024Sglebius}
936313024Sglebius
937313024Sglebiusvoid
938313024Sglebiussafeputchar(netdissect_options *ndo,
939313024Sglebius            const u_char c)
940313024Sglebius{
941313024Sglebius	ND_PRINT((ndo, (c < 0x80 && ND_ISPRINT(c)) ? "%c" : "\\0x%02x", c));
942313024Sglebius}
943313024Sglebius
944313024Sglebius#ifdef LBL_ALIGN
945313024Sglebius/*
946313024Sglebius * Some compilers try to optimize memcpy(), using the alignment constraint
947313024Sglebius * on the argument pointer type.  by using this function, we try to avoid the
948313024Sglebius * optimization.
949313024Sglebius */
950313024Sglebiusvoid
951313024Sglebiusunaligned_memcpy(void *p, const void *q, size_t l)
952313024Sglebius{
953313024Sglebius	memcpy(p, q, l);
954313024Sglebius}
955313024Sglebius
956313024Sglebius/* As with memcpy(), so with memcmp(). */
957313024Sglebiusint
958313024Sglebiusunaligned_memcmp(const void *p, const void *q, size_t l)
959313024Sglebius{
960313024Sglebius	return (memcmp(p, q, l));
961313024Sglebius}
962313024Sglebius#endif
963313024Sglebius
964