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