3 * $Id: pppdump.c,v 1.15 2002/03/04 00:25:35 guy Exp $
5 * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
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.
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.
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.
28 #include "file_wrappers.h"
38 Daniel Thompson (STMicroelectronics) <daniel.thompson@st.com>
41 | 0x07 +------+------+------+ Reset time
42 | t3 | t2 | t1 | t0 | t = time_t
43 +------+------+------+------+
46 | 0x06 | Time step (short)
47 | ts | ts = time step (tenths)
51 | 0x05 +------+------+------+ Time step (long)
52 | ts3 | ts2 | ts1 | ts0 | ts = time step (tenths)
53 +------+------+------+------+
56 | 0x04 | Receive deliminator (not seen in practice)
60 | 0x03 | Send deliminator (not seen in practice)
64 | 0x02 +------+ Received data
65 | n1 | n0 | n = number of bytes following
70 | 0x01 +------+ Sent data
71 | n1 | n0 | n = number of bytes following
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
84 #define PPPD_NULL 0x00 /* For my own use */
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
89 #define PPPD_BUF_SIZE 8192
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);
102 int num_saved_states;
110 guint8 buf[PPPD_BUF_SIZE];
114 /* Partial-record state */
122 typedef struct _pppdump_t {
129 struct _pppdump_t *seek_state;
132 int num_saved_states;
136 process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd, int *err,
137 gboolean *state_saved);
140 collate(pppdump_t*, FILE_T fh, int *err, guint8 *pd, int *num_bytes,
141 direction_enum *direction, pkt_id *pid);
144 pppdump_close(wtap *wth);
147 init_state(pppdump_t *state)
152 state->spkt.dir = DIRECTION_SENT;
154 state->spkt.esc = FALSE;
155 state->spkt.id_offset = 0;
157 state->rpkt.dir = DIRECTION_RECV;
159 state->rpkt.esc = FALSE;
160 state->rpkt.id_offset = 0;
162 state->seek_state = NULL;
163 state->offset = 0x100000; /* to detect errors during development */
168 pppdump_open(wtap *wth, int *err)
170 guint8 buffer[6]; /* Looking for: 0x07 t3 t2 t1 t0 ID */
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.
181 wtap_file_read_unknown_bytes(buffer, sizeof(buffer), wth->fh, err);
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)) {
198 if (file_seek(wth->fh, 5, SEEK_SET) == -1) {
199 *err = file_error(wth->fh);
203 state = wth->capture.generic = g_malloc(sizeof(pppdump_t));
204 state->timestamp = pntohl(&buffer[1]);
210 wth->file_encap = WTAP_ENCAP_PPP_WITH_PHDR;
211 wth->file_type = WTAP_FILE_PPPDUMP;
213 wth->snapshot_length = PPPD_BUF_SIZE; /* just guessing */
214 wth->subtype_read = pppdump_read;
215 wth->subtype_seek_read = pppdump_seek_read;
216 wth->subtype_close = pppdump_close;
218 state->seek_state = g_malloc(sizeof(pppdump_t));
220 state->pids = g_ptr_array_new();
222 state->num_saved_states = 0;
227 /* Find the next packet and parse it; called from wtap_loop(). */
229 pppdump_read(wtap *wth, int *err, long *data_offset)
233 direction_enum direction;
238 buffer_assure_space(wth->frame_buffer, PPPD_BUF_SIZE);
239 buf = buffer_start_ptr(wth->frame_buffer);
241 state = wth->capture.generic;
242 pid = g_new(pkt_id, 1);
244 *err = errno; /* assume a malloc failed and set "errno" */
248 pid->num_saved_states = 0;
250 retval = collate(state, wth->fh, err, buf, &num_bytes, &direction, pid);
257 pid->dir = direction;
259 g_ptr_array_add(state->pids, pid);
260 /* The user's data_offset is not really an offset, but a packet number. */
261 *data_offset = state->pkt_cnt;
264 wth->phdr.len = num_bytes;
265 wth->phdr.caplen = num_bytes;
266 wth->phdr.ts.tv_sec = state->timestamp;
267 wth->phdr.ts.tv_usec = state->tenths * 100000;
268 wth->phdr.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
270 wth->pseudo_header.p2p.sent = (direction == DIRECTION_SENT ? TRUE : FALSE);
275 #define PKT(x) (x)->dir == DIRECTION_SENT ? "SENT" : "RECV"
278 save_prec_state(pppdump_t *state, int num_bytes, pkt_t *pkt)
282 prec = g_new(prec_state, 1);
286 prec->num_bytes = num_bytes;
289 state->precs = g_list_append(state->precs, prec);
294 process_data_from_prec_state(pppdump_t *state, FILE_T fh, guint8* pd, int *err,
295 gboolean *state_saved, pkt_t **ppkt)
299 prec = state->precs->data;
301 state->precs = g_list_remove(state->precs, prec);
305 return process_data(state, fh, prec->pkt, prec->num_bytes, pd, err, state_saved);
310 /* Returns number of bytes copied for record, -1 if failure.
312 * This is modeled after pppdump.c, the utility to parse pppd log files; it comes with the ppp
316 process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd, int *err,
317 gboolean *state_saved)
323 *state_saved = FALSE;
324 for (; num_bytes > 0; --num_bytes) {
330 *err = WTAP_ERR_SHORT_READ;
339 num_written = pkt->cnt;
341 if (num_written <= 0) {
345 if (num_written > PPPD_BUF_SIZE) {
346 *err = WTAP_ERR_UNC_OVERFLOW;
350 memcpy(pd, pkt->buf, num_written);
354 if (!save_prec_state(state, num_bytes, pkt)) {
369 /* else fall through */
377 pkt->buf[pkt->cnt++] = c;
378 if (pkt->cnt > PPPD_BUF_SIZE) {
379 *err = WTAP_ERR_UNC_OVERFLOW;
386 /* we could have run out of bytes to read */
394 /* Returns TRUE if packet data copied, FALSE if error occurred or EOF (no more records). */
396 collate(pppdump_t* state, FILE_T fh, int *err, guint8 *pd, int *num_bytes,
397 direction_enum *direction, pkt_id *pid)
401 int n, num_written = 0;
407 state->num_saved_states = 0;
410 pid->num_saved_states = state->num_saved_states;
414 while (state->precs) {
415 num_written = process_data_from_prec_state(state, fh, pd, err, &ss, &pkt);
416 state->num_saved_states++;
418 pid->num_saved_states++;
421 if (num_written < 0) {
424 else if (num_written > 0) {
425 *num_bytes = num_written;
426 *direction = pkt->dir;
428 pid->offset = pkt->id_offset;
435 /* if 0 bytes written, keep processing */
438 while ((id = file_getc(fh)) != EOF) {
443 pkt = id == PPPD_SENT_DATA ? &state->spkt : &state->rpkt;
445 if (pkt->id_offset == 0) {
446 pkt->id_offset = state->offset - 1;
450 n = (n << 8) + file_getc(fh);
453 num_written = process_data(state, fh, pkt, n, pd, err, &ss);
455 if (num_written < 0) {
458 else if (num_written > 0) {
459 *num_bytes = num_written;
460 *direction = pkt->dir;
462 pid->offset = pkt->id_offset;
469 /* if 0 bytes written, keep looping */
473 case PPPD_SEND_DELIM:
474 case PPPD_RECV_DELIM:
475 /* What can we do? */
478 case PPPD_RESET_TIME:
479 wtap_file_read_unknown_bytes(&time_long, sizeof(guint32), fh, err);
480 state->offset += sizeof(guint32);
481 state->timestamp = pntohl(&time_long);
485 case PPPD_TIME_STEP_LONG:
486 wtap_file_read_unknown_bytes(&time_long, sizeof(guint32), fh, err);
487 state->offset += sizeof(guint32);
488 state->tenths += pntohl(&time_long);
490 if (state->tenths >= 10) {
491 state->timestamp += state->tenths / 10;
492 state->tenths = state->tenths % 10;
497 case PPPD_TIME_STEP_SHORT:
498 wtap_file_read_unknown_bytes(&time_short, sizeof(guint8), fh, err);
499 state->offset += sizeof(guint8);
500 state->tenths += time_short;
502 if (state->tenths >= 10) {
503 state->timestamp += state->tenths / 10;
504 state->tenths = state->tenths % 10;
511 g_assert_not_reached();
521 /* Used to read packets in random-access fashion */
523 pppdump_seek_read (wtap *wth,
525 union wtap_pseudo_header *pseudo_header,
531 direction_enum direction;
538 state = wth->capture.generic;
540 pid = g_ptr_array_index(state->pids, seek_off);
545 file_seek(wth->random_fh, pid->offset, SEEK_SET);
547 init_state(state->seek_state);
549 for (i = 0 ; i <= pid->num_saved_states; i++) {
551 retval = collate(state->seek_state, wth->random_fh, &err, pd, &num_bytes,
558 if (direction != pid->dir) {
563 if (len != num_bytes) {
567 pseudo_header->p2p.sent = (pid->dir == DIRECTION_SENT ? TRUE : FALSE);
573 simple_g_free(gpointer data, gpointer dummy _U_)
580 pppdump_close(wtap *wth)
584 state = wth->capture.generic;
587 g_list_foreach(state->precs, simple_g_free, NULL);
588 g_list_free(state->precs);
591 if (state->seek_state) { /* should always be TRUE */
592 g_free(state->seek_state);
595 if (state->pids) { /* should always be TRUE */
597 for (i = 0; i < g_ptr_array_len(state->pids); i++) {
598 g_free(g_ptr_array_index(state->pids, i));
600 g_ptr_array_free(state->pids, TRUE);