1/*	$NetBSD: pppdump.c,v 1.2 2005/02/20 10:47:17 cube Exp $	*/
2
3/*
4 * pppdump - print out the contents of a record file generated by
5 * pppd in readable form.
6 *
7 * Copyright (c) 1999 Paul Mackerras. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in
18 *    the documentation and/or other materials provided with the
19 *    distribution.
20 *
21 * 3. The name(s) of the authors of this software must not be used to
22 *    endorse or promote products derived from this software without
23 *    prior written permission.
24 *
25 * 4. Redistributions of any form whatsoever must retain the following
26 *    acknowledgment:
27 *    "This product includes software developed by Paul Mackerras
28 *     <paulus@samba.org>".
29 *
30 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
31 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
32 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
33 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
34 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
35 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
36 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
37 */
38#include <stdio.h>
39#include <unistd.h>
40#include <stdlib.h>
41#include <time.h>
42#include <sys/types.h>
43#include "pppdump.h"
44#include <net/ppp_defs.h>
45#include <net/ppp-comp.h>
46
47int hexmode;
48int pppmode;
49int reverse;
50int decompress;
51int mru = 1500;
52int abs_times;
53time_t start_time;
54int start_time_tenths;
55int tot_sent, tot_rcvd;
56
57extern int optind;
58extern char *optarg;
59
60extern struct compressor ppp_bsd_compress, ppp_deflate;
61
62struct compressor *compressors[] = {
63#if DO_BSD_COMPRESS
64    &ppp_bsd_compress,
65#endif
66#if DO_DEFLATE
67    &ppp_deflate,
68#endif
69    NULL
70};
71
72static struct pkt {
73    int	cnt;
74    int	esc;
75    int	flags;
76    struct compressor *comp;
77    void *state;
78    unsigned char buf[8192];
79} spkt, rpkt;
80
81static void dumplog __P((FILE *));
82static void dumpppp __P((FILE *));
83static void show_time __P((FILE *, int));
84static void handle_ccp __P((struct pkt *, u_char *, int));
85int main __P((int, char **));
86
87int
88main(ac, av)
89    int ac;
90    char **av;
91{
92    int i;
93    char *p;
94    FILE *f;
95
96    while ((i = getopt(ac, av, "hprdm:a")) != -1) {
97	switch (i) {
98	case 'h':
99	    hexmode = 1;
100	    break;
101	case 'p':
102	    pppmode = 1;
103	    break;
104	case 'r':
105	    reverse = 1;
106	    break;
107	case 'd':
108	    decompress = 1;
109	    break;
110	case 'm':
111	    mru = atoi(optarg);
112	    break;
113	case 'a':
114	    abs_times = 1;
115	    break;
116	default:
117	    fprintf(stderr, "Usage: %s [-h | -p[d]] [-r] [-m mru] [-a] [file ...]\n", av[0]);
118	    exit(1);
119	}
120    }
121    if (optind >= ac)
122	dumplog(stdin);
123    else {
124	for (i = optind; i < ac; ++i) {
125	    p = av[i];
126	    if ((f = fopen(p, "r")) == NULL) {
127		perror(p);
128		exit(1);
129	    }
130	    if (pppmode)
131		dumpppp(f);
132	    else
133		dumplog(f);
134	    fclose(f);
135	}
136    }
137    exit(0);
138}
139
140static void
141dumplog(f)
142    FILE *f;
143{
144    int c, n, k, col;
145    int nb, c2;
146    unsigned char buf[16];
147
148    while ((c = getc(f)) != EOF) {
149	switch (c) {
150	case 1:
151	case 2:
152	    if (reverse)
153		c = 3 - c;
154	    printf("%s %c", c==1? "sent": "rcvd", hexmode? ' ': '"');
155	    col = 6;
156	    n = getc(f);
157	    n = (n << 8) + getc(f);
158	    *(c==1? &tot_sent: &tot_rcvd) += n;
159	    nb = 0;
160	    for (; n > 0; --n) {
161		c = getc(f);
162		if (c == EOF) {
163		    printf("\nEOF\n");
164		    exit(0);
165		}
166		if (hexmode) {
167		    if (nb >= 16) {
168			printf("  ");
169			for (k = 0; k < nb; ++k) {
170			    c2 = buf[k];
171			    putchar((' ' <= c2 && c2 <= '~')? c2: '.');
172			}
173			printf("\n      ");
174			nb = 0;
175		    }
176		    buf[nb++] = c;
177		    printf(" %.2x", c);
178		} else {
179		    k = (' ' <= c && c <= '~')? (c != '\\' && c != '"')? 1: 2: 3;
180		    if ((col += k) >= 78) {
181			printf("\n      ");
182			col = 6 + k;
183		    }
184		    switch (k) {
185		    case 1:
186			putchar(c);
187			break;
188		    case 2:
189			printf("\\%c", c);
190			break;
191		    case 3:
192			printf("\\%.2x", c);
193			break;
194		    }
195		}
196	    }
197	    if (hexmode) {
198		for (k = nb; k < 16; ++k)
199		    printf("   ");
200		printf("  ");
201		for (k = 0; k < nb; ++k) {
202		    c2 = buf[k];
203		    putchar((' ' <= c2 && c2 <= '~')? c2: '.');
204		}
205	    } else
206		putchar('"');
207	    printf("\n");
208	    break;
209	case 3:
210	case 4:
211	    printf("end %s\n", c==3? "send": "recv");
212	    break;
213	case 5:
214	case 6:
215	case 7:
216	    show_time(f, c);
217	    break;
218	default:
219	    printf("?%.2x\n", c);
220	}
221    }
222}
223
224/*
225 * FCS lookup table as calculated by genfcstab.
226 */
227static u_short fcstab[256] = {
228	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
229	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
230	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
231	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
232	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
233	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
234	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
235	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
236	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
237	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
238	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
239	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
240	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
241	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
242	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
243	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
244	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
245	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
246	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
247	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
248	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
249	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
250	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
251	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
252	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
253	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
254	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
255	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
256	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
257	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
258	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
259	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
260};
261
262/* Values for flags */
263#define CCP_ISUP	1
264#define CCP_ERROR	2
265#define CCP_FATALERROR	4
266#define CCP_ERR		(CCP_ERROR | CCP_FATALERROR)
267#define CCP_DECOMP_RUN	8
268
269unsigned char dbuf[8192];
270
271static void
272dumpppp(f)
273    FILE *f;
274{
275    int c, n, k;
276    int nb, nl, dn, proto, rv;
277    char *dir, *q;
278    unsigned char *p, *r, *endp;
279    unsigned char *d;
280    unsigned short fcs;
281    struct pkt *pkt;
282
283    spkt.cnt = rpkt.cnt = 0;
284    spkt.esc = rpkt.esc = 0;
285    while ((c = getc(f)) != EOF) {
286	switch (c) {
287	case 1:
288	case 2:
289	    if (reverse)
290		c = 3 - c;
291	    dir = c==1? "sent": "rcvd";
292	    pkt = c==1? &spkt: &rpkt;
293	    n = getc(f);
294	    n = (n << 8) + getc(f);
295	    *(c==1? &tot_sent: &tot_rcvd) += n;
296	    for (; n > 0; --n) {
297		c = getc(f);
298		switch (c) {
299		case EOF:
300		    printf("\nEOF\n");
301		    if (spkt.cnt > 0)
302			printf("[%d bytes in incomplete send packet]\n",
303			       spkt.cnt);
304		    if (rpkt.cnt > 0)
305			printf("[%d bytes in incomplete recv packet]\n",
306			       rpkt.cnt);
307		    exit(0);
308		case '~':
309		    if (pkt->cnt > 0) {
310			q = dir;
311			if (pkt->esc) {
312			    printf("%s aborted packet:\n     ", dir);
313			    q = "    ";
314			}
315			nb = pkt->cnt;
316			p = pkt->buf;
317			pkt->cnt = 0;
318			pkt->esc = 0;
319			if (nb <= 2) {
320			    printf("%s short packet [%d bytes]:", q, nb);
321			    for (k = 0; k < nb; ++k)
322				printf(" %.2x", p[k]);
323			    printf("\n");
324			    break;
325			}
326			fcs = PPP_INITFCS;
327			for (k = 0; k < nb; ++k)
328			    fcs = PPP_FCS(fcs, p[k]);
329			fcs &= 0xFFFF;
330			nb -= 2;
331			endp = p + nb;
332			r = p;
333			if (r[0] == 0xff && r[1] == 3)
334			    r += 2;
335			if ((r[0] & 1) == 0)
336			    ++r;
337			++r;
338			if (endp - r > mru)
339			    printf("     ERROR: length (%d) > MRU (%d)\n",
340				   (int)(endp - r), mru);
341			if (decompress && fcs == PPP_GOODFCS) {
342			    /* See if this is a CCP or compressed packet */
343			    d = dbuf;
344			    r = p;
345			    if (r[0] == 0xff && r[1] == 3) {
346				*d++ = *r++;
347				*d++ = *r++;
348			    }
349			    proto = r[0];
350			    if ((proto & 1) == 0)
351				proto = (proto << 8) + r[1];
352			    if (proto == PPP_CCP) {
353				handle_ccp(pkt, r + 2, endp - r - 2);
354			    } else if (proto == PPP_COMP) {
355				if ((pkt->flags & CCP_ISUP)
356				    && (pkt->flags & CCP_DECOMP_RUN)
357				    && pkt->state
358				    && (pkt->flags & CCP_ERR) == 0) {
359				    struct packet in, out, *outp;
360				    in.buf = r;
361				    in.len = endp - r;
362				    out.buf = d;
363				    outp = &out;
364				    rv = pkt->comp->decompress(pkt->state, &in,
365					&outp);
366				    dn = outp->len;
367				    d = outp->buf;
368				    switch (rv) {
369				    case DECOMP_OK:
370					p = dbuf;
371					nb = d + dn - p;
372					if ((d[0] & 1) == 0)
373					    --dn;
374					--dn;
375					if (dn > mru)
376					    printf("     ERROR: decompressed length (%d) > MRU (%d)\n", dn, mru);
377					break;
378				    case DECOMP_ERROR:
379					printf("     DECOMPRESSION ERROR\n");
380					pkt->flags |= CCP_ERROR;
381					break;
382				    case DECOMP_FATALERROR:
383					printf("     FATAL DECOMPRESSION ERROR\n");
384					pkt->flags |= CCP_FATALERROR;
385					break;
386				    }
387				}
388			    } else if (pkt->state
389				       && (pkt->flags & CCP_DECOMP_RUN)) {
390				struct packet in;
391				in.buf = r;
392				in.len = endp - r;
393				pkt->comp->incomp(pkt->state, &in);
394			    }
395			}
396			do {
397			    nl = nb < 16? nb: 16;
398			    printf("%s ", q);
399			    for (k = 0; k < nl; ++k)
400				printf(" %.2x", p[k]);
401			    for (; k < 16; ++k)
402				printf("   ");
403			    printf("  ");
404			    for (k = 0; k < nl; ++k) {
405				c = p[k];
406				putchar((' ' <= c && c <= '~')? c: '.');
407			    }
408			    printf("\n");
409			    q = "    ";
410			    p += nl;
411			    nb -= nl;
412			} while (nb > 0);
413			if (fcs != PPP_GOODFCS)
414			    printf("     BAD FCS: (residue = %x)\n", fcs);
415		    }
416		    break;
417		case '}':
418		    if (!pkt->esc) {
419			pkt->esc = 1;
420			break;
421		    }
422		    /* else fall through */
423		default:
424		    if (pkt->esc) {
425			c ^= 0x20;
426			pkt->esc = 0;
427		    }
428		    pkt->buf[pkt->cnt++] = c;
429		    break;
430		}
431	    }
432	    break;
433	case 3:
434	case 4:
435	    if (reverse)
436		c = 7 - c;
437	    dir = c==3? "send": "recv";
438	    pkt = c==3? &spkt: &rpkt;
439	    printf("end %s", dir);
440	    if (pkt->cnt > 0)
441		printf("  [%d bytes in incomplete packet]", pkt->cnt);
442	    printf("\n");
443	    break;
444	case 5:
445	case 6:
446	case 7:
447	    show_time(f, c);
448	    break;
449	default:
450	    printf("?%.2x\n", c);
451	}
452    }
453}
454
455static void
456handle_ccp(cp, dp, len)
457    struct pkt *cp;
458    u_char *dp;
459    int len;
460{
461    int clen;
462    struct compressor **comp;
463
464    if (len < CCP_HDRLEN)
465	return;
466    clen = CCP_LENGTH(dp);
467    if (clen > len)
468	return;
469
470    switch (CCP_CODE(dp)) {
471    case CCP_CONFACK:
472	cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
473	if (clen < CCP_HDRLEN + CCP_OPT_MINLEN
474	    || clen < CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN))
475	    break;
476	dp += CCP_HDRLEN;
477	clen -= CCP_HDRLEN;
478	for (comp = compressors; *comp != NULL; ++comp) {
479	    if ((*comp)->compress_proto == dp[0]) {
480		if (cp->state != NULL) {
481		    (*cp->comp->decomp_free)(cp->state);
482		    cp->state = NULL;
483		}
484		cp->comp = *comp;
485		cp->state = (*comp)->decomp_alloc(dp, CCP_OPT_LENGTH(dp));
486		cp->flags |= CCP_ISUP;
487		if (cp->state != NULL
488		    && (*cp->comp->decomp_init)
489		        (cp->state, dp, clen, 0, 0, 8192, 1))
490		    cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
491		break;
492	    }
493	}
494	break;
495
496    case CCP_CONFNAK:
497    case CCP_CONFREJ:
498	cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
499	break;
500
501    case CCP_RESETACK:
502	if (cp->flags & CCP_ISUP) {
503	    if (cp->state && (cp->flags & CCP_DECOMP_RUN)) {
504		(*cp->comp->decomp_reset)(cp->state);
505		cp->flags &= ~CCP_ERROR;
506	    }
507	}
508	break;
509    }
510}
511
512static void
513show_time(f, c)
514    FILE *f;
515    int c;
516{
517    time_t t;
518    int n;
519    struct tm *tm;
520
521    if (c == 7) {
522	t = getc(f);
523	t = (t << 8) + getc(f);
524	t = (t << 8) + getc(f);
525	t = (t << 8) + getc(f);
526	printf("start %s", ctime(&t));
527	start_time = t;
528	start_time_tenths = 0;
529	tot_sent = tot_rcvd = 0;
530    } else {
531	n = getc(f);
532	if (c == 5) {
533	    for (c = 3; c > 0; --c)
534		n = (n << 8) + getc(f);
535	}
536	if (abs_times) {
537	    n += start_time_tenths;
538	    start_time += n / 10;
539	    start_time_tenths = n % 10;
540	    tm = localtime(&start_time);
541	    printf("time  %.2d:%.2d:%.2d.%d", tm->tm_hour, tm->tm_min,
542		   tm->tm_sec, start_time_tenths);
543	    printf("  (sent %d, rcvd %d)\n", tot_sent, tot_rcvd);
544	} else
545	    printf("time  %.1fs\n", (double) n / 10);
546    }
547}
548