Use longs as file offsets, so that on platforms with 64-bit "long" we
[obnox/wireshark/wip.git] / wiretap / pppdump.c
1 /* pppdump.c
2  *
3  * $Id: pppdump.c,v 1.9 2001/10/04 08:30:36 guy Exp $
4  *
5  * Copyright (c) 2000 by Gilbert Ramirez <gram@xiexie.org>
6  * 
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "wtap-int.h"
26 #include "buffer.h"
27 #include "pppdump.h"
28 #include "file_wrappers.h"
29
30 #include <glib.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35
36 /*
37 pppdump records
38 Daniel Thompson (STMicroelectronics) <daniel.thompson@st.com>
39
40 +------+
41 | 0x07 +------+------+------+         Reset time
42 |  t3  |  t2  |  t1  |  t0  |         t = time_t
43 +------+------+------+------+
44
45 +------+
46 | 0x06 |                              Time step (short)
47 |  ts  |                              ts = time step (tenths)
48 +------+
49
50 +------+
51 | 0x05 +------+------+------+         Time step (long)
52 | ts3  | ts2  | ts1  | ts0  |         ts = time step (tenths)
53 +------+------+------+------+
54
55 +------+
56 | 0x04 |                              Receive deliminator (not seen in practice)
57 +------+
58
59 +------+
60 | 0x03 |                              Send deliminator (not seen in practice)
61 +------+
62
63 +------+
64 | 0x02 +------+                       Received data
65 |  n1  |  n0  |                       n = number of bytes following
66 |    data     |
67 |             |
68
69 +------+
70 | 0x01 +------+                       Sent data
71 |  n1  |  n0  |                       n = number of bytes following
72 |    data     |
73 |             |
74 */
75
76 #define PPPD_SENT_DATA          0x01
77 #define PPPD_RECV_DATA          0x02
78 #define PPPD_SEND_DELIM         0x03
79 #define PPPD_RECV_DELIM         0x04
80 #define PPPD_TIME_STEP_LONG     0x05
81 #define PPPD_TIME_STEP_SHORT    0x06
82 #define PPPD_RESET_TIME         0x07
83
84 #define PPPD_NULL               0x00    /* For my own use */
85
86 /* this buffer must be at least (2*PPPD_MTU) + sizeof(ppp_header) + sizeof(lcp_header) + 
87  * sizeof(ipcp_header). PPPD_MTU is *very* rarely larger than 1500 so this value is fine
88  */
89 #define PPPD_BUF_SIZE           8192
90
91 typedef enum {
92         DIRECTION_SENT,
93         DIRECTION_RECV
94 } direction_enum;
95
96 static gboolean pppdump_read(wtap *wth, int *err, long *data_offset);
97 static int pppdump_seek_read(wtap *wth, long seek_off,
98         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len);
99
100 typedef struct {
101         long            offset;
102         int             num_saved_states;
103         direction_enum  dir;
104 } pkt_id;
105
106 typedef struct {
107         direction_enum  dir;
108         int             cnt;
109         gboolean        esc;
110         guint8          buf[PPPD_BUF_SIZE];
111         long            id_offset;
112 } pkt_t;
113
114 /* Partial-record state */
115 typedef struct {
116         int             num_bytes;
117         pkt_t           *pkt;
118 } prec_state;
119
120 struct _pppdump_t;
121
122 typedef struct _pppdump_t {
123         time_t                  timestamp;
124         guint                   tenths;
125         pkt_t                   spkt;
126         pkt_t                   rpkt;
127         long                    offset;
128         GList                   *precs;
129         struct _pppdump_t       *seek_state;
130         GPtrArray               *pids;
131         guint                   pkt_cnt;
132         int                     num_saved_states;
133 } pppdump_t;
134
135 static int
136 process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd, int *err,
137                 gboolean *state_saved);
138
139 static gboolean
140 collate(pppdump_t*, FILE_T fh, int *err, guint8 *pd, int *num_bytes,
141                 direction_enum *direction, pkt_id *pid);
142
143 static void
144 pppdump_close(wtap *wth);
145
146 static void
147 init_state(pppdump_t *state)
148 {
149
150         state->precs = NULL;
151
152         state->spkt.dir = DIRECTION_SENT;
153         state->spkt.cnt = 0;
154         state->spkt.esc = FALSE;
155         state->spkt.id_offset = 0;
156
157         state->rpkt.dir = DIRECTION_RECV;
158         state->rpkt.cnt = 0;
159         state->rpkt.esc = FALSE;
160         state->rpkt.id_offset = 0;
161
162         state->seek_state = NULL;
163         state->offset = 0x100000; /* to detect errors during development */
164 }
165
166         
167 int
168 pppdump_open(wtap *wth, int *err)
169 {
170         guint8          buffer[6];      /* Looking for: 0x07 t3 t2 t1 t0 ID */
171         pppdump_t       *state;
172
173         /* There is no file header, only packet records. Fortunately for us,
174         * timestamp records are separated from packet records, so we should
175         * find an "initial time stamp" (i.e., a "reset time" record, or
176         * record type 0x07) at the beginning of the file. We'll check for
177         * that, plus a valid record following the 0x07 and the four bytes
178         * representing the timestamp.
179         */
180
181         wtap_file_read_unknown_bytes(buffer, sizeof(buffer), wth->fh, err);
182
183         if (buffer[0] == PPPD_RESET_TIME &&
184                         (buffer[5] == PPPD_SENT_DATA ||
185                          buffer[5] == PPPD_RECV_DATA ||
186                          buffer[5] == PPPD_TIME_STEP_LONG ||
187                          buffer[5] == PPPD_TIME_STEP_SHORT ||
188                          buffer[5] == PPPD_RESET_TIME)) {
189
190                 goto my_file_type;
191         }
192         else {
193                 return 0;
194         }
195
196   my_file_type:
197
198         state = wth->capture.generic = g_malloc(sizeof(pppdump_t));
199         state->timestamp = pntohl(&buffer[1]);
200         state->tenths = 0;
201
202         init_state(state);
203
204         state->offset = 5; 
205         file_seek(wth->fh, 5, SEEK_SET); 
206         wth->file_encap = WTAP_ENCAP_PPP_WITH_PHDR; 
207         wth->file_type = WTAP_FILE_PPPDUMP; 
208
209         wth->snapshot_length = PPPD_BUF_SIZE; /* just guessing */ 
210         wth->subtype_read = pppdump_read; 
211         wth->subtype_seek_read = pppdump_seek_read; 
212         wth->subtype_close = pppdump_close;
213
214         state->seek_state = g_malloc(sizeof(pppdump_t));
215
216         state->pids = g_ptr_array_new();
217         state->pkt_cnt = 0;
218         state->num_saved_states = 0;
219
220         return 1;
221 }
222
223 /* Find the next packet and parse it; called from wtap_loop(). */
224 static gboolean
225 pppdump_read(wtap *wth, int *err, long *data_offset)
226 {
227         gboolean        retval;
228         int             num_bytes;
229         direction_enum  direction;
230         guint8          *buf;
231         pppdump_t       *state;
232         pkt_id          *pid;
233
234         buffer_assure_space(wth->frame_buffer, PPPD_BUF_SIZE);
235         buf = buffer_start_ptr(wth->frame_buffer);
236
237         state = wth->capture.generic;
238         pid = g_new(pkt_id, 1);
239         if (!pid) {
240                 *err = errno;   /* assume a malloc failed and set "errno" */
241                 return FALSE;
242         }
243         pid->offset = 0;
244         pid->num_saved_states = 0;
245
246         retval = collate(state, wth->fh, err, buf, &num_bytes, &direction, pid);
247
248         if (!retval) {
249                 g_free(pid);
250                 return FALSE;
251         }
252
253         pid->dir = direction;
254
255         g_ptr_array_add(state->pids, pid);
256         /* The user's data_offset is not really an offset, but a packet number. */
257         *data_offset = state->pkt_cnt;
258         state->pkt_cnt++;
259
260         wth->phdr.len           = num_bytes;
261         wth->phdr.caplen        = num_bytes;
262         wth->phdr.ts.tv_sec     = state->timestamp;
263         wth->phdr.ts.tv_usec    = state->tenths * 100000;
264         wth->phdr.pkt_encap     = WTAP_ENCAP_PPP_WITH_PHDR;
265
266         wth->pseudo_header.p2p.sent = (direction == DIRECTION_SENT ? TRUE : FALSE);
267
268         return TRUE;
269 }
270
271 #define PKT(x)  (x)->dir == DIRECTION_SENT ? "SENT" : "RECV"
272
273 static gboolean
274 save_prec_state(pppdump_t *state, int num_bytes, pkt_t *pkt)
275 {
276         prec_state      *prec;
277
278         prec = g_new(prec_state, 1);
279         if (!prec) {
280                 return FALSE;
281         }
282         prec->num_bytes = num_bytes;
283         prec->pkt = pkt;
284
285         state->precs = g_list_append(state->precs, prec);
286         return TRUE;
287 }
288
289 static int
290 process_data_from_prec_state(pppdump_t *state, FILE_T fh, guint8* pd, int *err,
291                 gboolean *state_saved, pkt_t **ppkt)
292 {
293         prec_state      *prec;
294
295         prec = state->precs->data;
296
297         state->precs = g_list_remove(state->precs, prec);
298
299         *ppkt = prec->pkt;
300
301         return process_data(state, fh, prec->pkt, prec->num_bytes, pd, err, state_saved);
302 }
303         
304
305
306 /* Returns number of bytes copied for record, -1 if failure.
307  *
308  * This is modeled after pppdump.c, the utility to parse pppd log files; it comes with the ppp
309  * distribution.
310  */
311 static int
312 process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd, int *err,
313                 gboolean *state_saved)
314 {
315         int     c;
316         int     num_bytes = n;
317         int     num_written;
318
319         *state_saved = FALSE;
320         for (; num_bytes > 0; --num_bytes) {
321                 c = file_getc(fh);
322                 state->offset++;
323                 switch (c) {
324                         case EOF:
325                                 if (*err == 0) {
326                                         *err = WTAP_ERR_SHORT_READ;
327                                 }
328                                 return -1;
329                                 break;
330
331                         case '~':
332                                 if (pkt->cnt > 0) {
333                                         pkt->esc = FALSE;
334
335                                         num_written = pkt->cnt - 2;
336                                         pkt->cnt = 0;
337                                         if (num_written <= 0) {
338                                                 return 0;
339                                         }
340
341                                         if (num_written > PPPD_BUF_SIZE) {
342                                                 *err = WTAP_ERR_UNC_OVERFLOW;
343                                                 return -1;
344                                         }
345
346                                         memcpy(pd, pkt->buf, num_written);
347
348                                         num_bytes--;
349                                         if (num_bytes > 0) {
350                                                 if (!save_prec_state(state, num_bytes, pkt)) {
351                                                         *err = errno;
352                                                         return -1;
353                                                 }
354                                                 *state_saved = TRUE;
355                                         }
356                                         return num_written;
357                                 }
358                                 break;
359
360                         case '}':
361                                 if (!pkt->esc) {
362                                         pkt->esc = TRUE;
363                                         break;
364                                 }
365                                 /* else fall through */
366
367                         default:
368                                 if (pkt->esc) {
369                                         c ^= 0x20;
370                                         pkt->esc = FALSE;
371                                 }
372                 
373                                 pkt->buf[pkt->cnt++] = c;
374                                 if (pkt->cnt > PPPD_BUF_SIZE) {
375                                         *err = WTAP_ERR_UNC_OVERFLOW;
376                                         return -1;
377                                 }
378                                 break;
379                 }
380         }
381
382         /* we could have run out of bytes to read */
383         return 0;
384
385 }
386
387
388
389
390 /* Returns TRUE if packet data copied, FALSE if error occurred or EOF (no more records). */
391 static gboolean
392 collate(pppdump_t* state, FILE_T fh, int *err, guint8 *pd, int *num_bytes,
393                 direction_enum *direction, pkt_id *pid)
394 {
395         int             id;
396         pkt_t           *pkt = NULL;
397         int             n, num_written = 0;
398         gboolean        ss = FALSE;
399         guint32         time_long;
400         guint8          time_short;
401
402         if (!state->precs) {
403                 state->num_saved_states = 0;
404         }
405         if (pid) {
406                 pid->num_saved_states = state->num_saved_states;
407         }
408
409
410         while (state->precs) {
411                 num_written = process_data_from_prec_state(state, fh, pd, err, &ss, &pkt);
412                 state->num_saved_states++;
413                 if (pid) {
414                         pid->num_saved_states++;
415                 }
416
417                 if (num_written < 0) {
418                         return FALSE;
419                 }
420                 else if (num_written > 0) {
421                         *num_bytes = num_written;
422                         *direction = pkt->dir;
423                         if (pid) {
424                                 pid->offset = pkt->id_offset;
425                         }
426                         if (!ss) {
427                                 pkt->id_offset = 0;
428                         }
429                         return TRUE;
430                 }
431                 /* if 0 bytes written, keep processing */
432         }
433
434         while ((id = file_getc(fh)) != EOF) {
435                 state->offset++;
436                 switch (id) {
437                         case PPPD_SENT_DATA:
438                         case PPPD_RECV_DATA:
439                                 pkt = id == PPPD_SENT_DATA ? &state->spkt : &state->rpkt;
440
441                                 if (pkt->id_offset == 0) {
442                                         pkt->id_offset = state->offset - 1;
443                                 }
444
445                                 n = file_getc(fh);
446                                 n = (n << 8) + file_getc(fh);
447                                 state->offset += 2;
448
449                                 num_written = process_data(state, fh, pkt, n, pd, err, &ss);
450
451                                 if (num_written < 0) {
452                                         return FALSE;
453                                 }
454                                 else if (num_written > 0) {
455                                         *num_bytes = num_written;
456                                         *direction = pkt->dir;
457                                         if (pid) {
458                                                 pid->offset = pkt->id_offset;
459                                         }
460                                         if (!ss) {
461                                                 pkt->id_offset = 0;
462                                         }
463                                         return TRUE;
464                                 }
465                                 /* if 0 bytes written, keep looping */
466                                 
467                                 break;
468
469                         case PPPD_SEND_DELIM:
470                         case PPPD_RECV_DELIM:
471                                 /* What can we do? */
472                                 break;
473
474                         case PPPD_TIME_STEP_LONG:
475                                 wtap_file_read_unknown_bytes(&time_long, sizeof(guint32), fh, err);
476                                 state->offset += sizeof(guint32);
477                                 state->timestamp = time_long;
478                                 state->tenths = 0;
479                                 break;
480
481                         case PPPD_RESET_TIME:
482                                 wtap_file_read_unknown_bytes(&time_long, sizeof(guint32), fh, err);
483                                 state->offset += sizeof(guint32);
484                                 state->tenths += time_long;
485
486                                 if (state->tenths >= 10) {
487                                         state->timestamp += state->tenths / 10;
488                                         state->tenths = state->tenths % 10;
489                                 }
490
491                                 break;
492
493                         case PPPD_TIME_STEP_SHORT:
494                                 wtap_file_read_unknown_bytes(&time_short, sizeof(guint8), fh, err);
495                                 state->offset += sizeof(guint8);
496                                 state->tenths += time_short;
497
498                                 if (state->tenths >= 10) {
499                                         state->timestamp += state->tenths / 10;
500                                         state->tenths = state->tenths % 10;
501                                 }
502
503                                 break;
504
505                         default:
506                                 /* XXX - bad file */
507                                 g_assert_not_reached();
508                 }
509
510         }
511
512         return FALSE;
513 }
514
515
516
517 /* Used to read packets in random-access fashion */
518 static int
519 pppdump_seek_read (wtap *wth,
520                  long seek_off,
521                  union wtap_pseudo_header *pseudo_header,
522                  guint8 *pd,
523                  int len)
524 {
525         int             err = 0;
526         int             num_bytes;
527         direction_enum  direction;
528         gboolean        retval;
529         pppdump_t       *state;
530         pkt_id          *pid;
531         int             i;
532
533
534         state = wth->capture.generic;
535
536         pid = g_ptr_array_index(state->pids, seek_off);
537         if (!pid) {
538                 return -1;
539         }
540
541         file_seek(wth->random_fh, pid->offset, SEEK_SET);
542
543         init_state(state->seek_state);
544
545         for (i = 0 ; i <= pid->num_saved_states; i++) {
546           again:
547                 retval = collate(state->seek_state, wth->random_fh, &err, pd, &num_bytes,
548                                 &direction, NULL);
549
550                 if (!retval) {
551                         return -1;
552                 }
553
554                 if (direction != pid->dir) {
555                         goto again;
556                 }
557         }
558
559         if (len != num_bytes) {
560                 return -1;
561         }
562
563         pseudo_header->p2p.sent = (pid->dir == DIRECTION_SENT ? TRUE : FALSE);
564
565         return 0;
566 }
567
568 static void
569 simple_g_free(gpointer data, gpointer junk)
570 {
571         if (data)
572                 g_free(data);
573 }
574
575 static void
576 pppdump_close(wtap *wth)
577 {
578         pppdump_t       *state;
579
580         state = wth->capture.generic;
581
582         if (state->precs) {
583                 g_list_foreach(state->precs, simple_g_free, NULL);
584                 g_list_free(state->precs);
585         }
586
587         if (state->seek_state) { /* should always be TRUE */
588                 g_free(state->seek_state);
589         }
590
591         if (state->pids) { /* should always be TRUE */
592                 g_ptr_array_free(state->pids, TRUE); /* free data, too */
593         }
594
595         g_free(state);
596
597 }