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