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