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