/src/wireshark/wiretap/pppdump.c
Line | Count | Source |
1 | | /* pppdump.c |
2 | | * |
3 | | * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu> |
4 | | * |
5 | | * SPDX-License-Identifier: GPL-2.0-or-later |
6 | | */ |
7 | | |
8 | | #include "config.h" |
9 | | #include "pppdump.h" |
10 | | #include "wtap_module.h" |
11 | | #include "file_wrappers.h" |
12 | | |
13 | | #include <stdlib.h> |
14 | | #include <errno.h> |
15 | | #include <string.h> |
16 | | |
17 | | #include <wsutil/array.h> |
18 | | #include <wsutil/ws_assert.h> |
19 | | #include <wsutil/pint.h> |
20 | | |
21 | | /* |
22 | | pppdump records |
23 | | Daniel Thompson (STMicroelectronics) <daniel.thompson@st.com> |
24 | | |
25 | | +------+ |
26 | | | 0x07 | Reset time |
27 | | +------+------+------+------+ |
28 | | | t3 | t2 | t1 | t0 | t = time_t |
29 | | +------+------+------+------+ |
30 | | |
31 | | +------+ |
32 | | | 0x06 | Time step (short) |
33 | | +------+ |
34 | | | ts | ts = time step (tenths of seconds) |
35 | | +------+ |
36 | | |
37 | | +------+ |
38 | | | 0x05 | Time step (long) |
39 | | +------+------+------+------+ |
40 | | | ts3 | ts2 | ts1 | ts0 | ts = time step (tenths of seconds) |
41 | | +------+------+------+------+ |
42 | | |
43 | | +------+ |
44 | | | 0x04 | Receive delimiter (not seen in practice) |
45 | | +------+ |
46 | | |
47 | | +------+ |
48 | | | 0x03 | Send delimiter (not seen in practice) |
49 | | +------+ |
50 | | |
51 | | +------+ |
52 | | | 0x02 | Received data |
53 | | +------+------+ |
54 | | | n1 | n0 | n = number of bytes following |
55 | | +------+------+ |
56 | | | data | |
57 | | | | |
58 | | |
59 | | +------+ |
60 | | | 0x01 | Sent data |
61 | | +------+------+ |
62 | | | n1 | n0 | n = number of bytes following |
63 | | +------+------+ |
64 | | | data | |
65 | | | | |
66 | | */ |
67 | | |
68 | 0 | #define PPPD_SENT_DATA 0x01 |
69 | 0 | #define PPPD_RECV_DATA 0x02 |
70 | 0 | #define PPPD_SEND_DELIM 0x03 |
71 | 0 | #define PPPD_RECV_DELIM 0x04 |
72 | 0 | #define PPPD_TIME_STEP_LONG 0x05 |
73 | 0 | #define PPPD_TIME_STEP_SHORT 0x06 |
74 | 0 | #define PPPD_RESET_TIME 0x07 |
75 | | |
76 | | /* this buffer must be at least (2*PPPD_MTU) + sizeof(ppp_header) + |
77 | | * sizeof(lcp_header) + sizeof(ipcp_header). PPPD_MTU is *very* rarely |
78 | | * larger than 1500 so this value is fine. |
79 | | * |
80 | | * It's less than WTAP_MAX_PACKET_SIZE_STANDARD, so we don't have to worry about |
81 | | * too-large packets. |
82 | | */ |
83 | 0 | #define PPPD_BUF_SIZE 8192 |
84 | | |
85 | | typedef enum { |
86 | | DIRECTION_SENT, |
87 | | DIRECTION_RECV |
88 | | } direction_enum; |
89 | | |
90 | | static bool pppdump_read(wtap *wth, wtap_rec *rec, |
91 | | int *err, char **err_info, int64_t *data_offset); |
92 | | static bool pppdump_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, |
93 | | int *err, char **err_info); |
94 | | |
95 | | static int pppdump_file_type_subtype = -1; |
96 | | |
97 | | void register_pppdump(void); |
98 | | |
99 | | /* |
100 | | * Information saved about a packet, during the initial sequential pass |
101 | | * through the file, to allow us to later re-read it when randomly |
102 | | * reading packets. |
103 | | * |
104 | | * "offset" is the offset in the file of the first data chunk containing data |
105 | | * from that packet; note that it may also contain data from previous |
106 | | * packets. |
107 | | * |
108 | | * "num_bytes_to_skip" is the number of bytes from previous packets in that |
109 | | * first data chunk. |
110 | | * |
111 | | * "dir" is the direction of the packet. |
112 | | */ |
113 | | typedef struct { |
114 | | int64_t offset; |
115 | | int64_t num_bytes_to_skip; |
116 | | direction_enum dir; |
117 | | } pkt_id; |
118 | | |
119 | | /* |
120 | | * Information about a packet currently being processed. There is one of |
121 | | * these for the sent packet being processed and one of these for the |
122 | | * received packet being processed, as we could be in the middle of |
123 | | * processing both a received packet and a sent packet. |
124 | | * |
125 | | * "dir" is the direction of the packet. |
126 | | * |
127 | | * "cnt" is the number of bytes of packet data we've accumulated. |
128 | | * |
129 | | * "esc" is true if the next byte we see is escaped (and thus must be XORed |
130 | | * with 0x20 before saving it), false otherwise. |
131 | | * |
132 | | * "buf" is a buffer containing the packet data we've accumulated. |
133 | | * |
134 | | * "id_offset" is the offset in the file of the first data chunk |
135 | | * containing data from the packet we're processing. |
136 | | * |
137 | | * "sd_offset" is the offset in the file of the first data byte from |
138 | | * the packet we're processing - which isn't necessarily right after |
139 | | * the header of the first data chunk, as we may already have assembled |
140 | | * packets from that chunk. |
141 | | * |
142 | | * "cd_offset" is the offset in the file of the current data chunk we're |
143 | | * processing. |
144 | | */ |
145 | | typedef struct { |
146 | | direction_enum dir; |
147 | | int cnt; |
148 | | bool esc; |
149 | | uint8_t buf[PPPD_BUF_SIZE]; |
150 | | int64_t id_offset; |
151 | | int64_t sd_offset; |
152 | | int64_t cd_offset; |
153 | | } pkt_t; |
154 | | |
155 | | /* |
156 | | * This keeps state used while processing records. |
157 | | * |
158 | | * "timestamp" is the seconds portion of the current time stamp value, |
159 | | * as updated from PPPD_RESET_TIME, PPPD_TIME_STEP_LONG, and |
160 | | * PPPD_TIME_STEP_SHORT records. "tenths" is the tenths-of-seconds |
161 | | * portion. |
162 | | * |
163 | | * "spkt" and "rpkt" are "pkt_t" structures for the sent and received |
164 | | * packets we're currently working on. |
165 | | * |
166 | | * "offset" is the current offset in the file. |
167 | | * |
168 | | * "num_bytes" and "pkt" are information saved when we finish accumulating |
169 | | * the data for a packet, if the data chunk we're working on still has more |
170 | | * data in it: |
171 | | * |
172 | | * "num_bytes" is the number of bytes of additional data remaining |
173 | | * in the chunk after we've finished accumulating the data for the |
174 | | * packet. |
175 | | * |
176 | | * "pkt" is the "pkt_t" for the type of packet the data chunk is for |
177 | | * (sent or received packet). |
178 | | * |
179 | | * "seek_state" is another state structure used while processing records |
180 | | * when doing a seek-and-read. (That structure doesn't itself have a |
181 | | * "seek_state" structure.) |
182 | | * |
183 | | * "pids" is a GPtrArray of pointers to "pkt_id" structures for all the |
184 | | * packets we've seen during the initial sequential pass, to allow us to |
185 | | * later retrieve them with random accesses. |
186 | | * |
187 | | * "pkt_cnt" is the number of packets we've seen up to this point in the |
188 | | * sequential pass. |
189 | | */ |
190 | | typedef struct _pppdump_t { |
191 | | time_t timestamp; |
192 | | unsigned tenths; |
193 | | pkt_t spkt; |
194 | | pkt_t rpkt; |
195 | | int64_t offset; |
196 | | int num_bytes; |
197 | | pkt_t *pkt; |
198 | | struct _pppdump_t *seek_state; |
199 | | GPtrArray *pids; |
200 | | unsigned pkt_cnt; |
201 | | } pppdump_t; |
202 | | |
203 | | static int |
204 | | process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, uint8_t *pd, |
205 | | int *err, char **err_info, pkt_id *pid); |
206 | | |
207 | | static bool |
208 | | collate(pppdump_t *state, FILE_T fh, int *err, char **err_info, uint8_t *pd, |
209 | | int *num_bytes, direction_enum *direction, pkt_id *pid, |
210 | | int64_t num_bytes_to_skip); |
211 | | |
212 | | static void |
213 | | pppdump_close(wtap *wth); |
214 | | |
215 | | static void |
216 | | init_state(pppdump_t *state) |
217 | 0 | { |
218 | |
|
219 | 0 | state->num_bytes = 0; |
220 | 0 | state->pkt = NULL; |
221 | |
|
222 | 0 | state->spkt.dir = DIRECTION_SENT; |
223 | 0 | state->spkt.cnt = 0; |
224 | 0 | state->spkt.esc = false; |
225 | 0 | state->spkt.id_offset = 0; |
226 | 0 | state->spkt.sd_offset = 0; |
227 | 0 | state->spkt.cd_offset = 0; |
228 | |
|
229 | 0 | state->rpkt.dir = DIRECTION_RECV; |
230 | 0 | state->rpkt.cnt = 0; |
231 | 0 | state->rpkt.esc = false; |
232 | 0 | state->rpkt.id_offset = 0; |
233 | 0 | state->rpkt.sd_offset = 0; |
234 | 0 | state->rpkt.cd_offset = 0; |
235 | |
|
236 | 0 | state->seek_state = NULL; |
237 | 0 | state->offset = 0x100000; /* to detect errors during development */ |
238 | 0 | } |
239 | | |
240 | | |
241 | | wtap_open_return_val |
242 | | pppdump_open(wtap *wth, int *err, char **err_info) |
243 | 0 | { |
244 | 0 | uint8_t buffer[6]; /* Looking for: 0x07 t3 t2 t1 t0 ID */ |
245 | 0 | pppdump_t *state; |
246 | | |
247 | | /* There is no file header, only packet records. Fortunately for us, |
248 | | * timestamp records are separated from packet records, so we should |
249 | | * find an "initial time stamp" (i.e., a "reset time" record, or |
250 | | * record type 0x07) at the beginning of the file. We'll check for |
251 | | * that, plus a valid record following the 0x07 and the four bytes |
252 | | * representing the timestamp. |
253 | | */ |
254 | |
|
255 | 0 | if (!wtap_read_bytes(wth->fh, buffer, sizeof(buffer), |
256 | 0 | err, err_info)) { |
257 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
258 | 0 | return WTAP_OPEN_ERROR; |
259 | 0 | return WTAP_OPEN_NOT_MINE; |
260 | 0 | } |
261 | | |
262 | 0 | if (buffer[0] == PPPD_RESET_TIME && |
263 | 0 | (buffer[5] == PPPD_SENT_DATA || |
264 | 0 | buffer[5] == PPPD_RECV_DATA || |
265 | 0 | buffer[5] == PPPD_TIME_STEP_LONG || |
266 | 0 | buffer[5] == PPPD_TIME_STEP_SHORT || |
267 | 0 | buffer[5] == PPPD_RESET_TIME)) { |
268 | |
|
269 | 0 | goto my_file_type; |
270 | 0 | } |
271 | 0 | else { |
272 | 0 | return WTAP_OPEN_NOT_MINE; |
273 | 0 | } |
274 | | |
275 | 0 | my_file_type: |
276 | |
|
277 | 0 | if (file_seek(wth->fh, 5, SEEK_SET, err) == -1) |
278 | 0 | return WTAP_OPEN_ERROR; |
279 | | |
280 | 0 | state = g_new(pppdump_t, 1); |
281 | 0 | wth->priv = (void *)state; |
282 | 0 | state->timestamp = pntohu32(&buffer[1]); |
283 | 0 | state->tenths = 0; |
284 | |
|
285 | 0 | init_state(state); |
286 | |
|
287 | 0 | state->offset = 5; |
288 | 0 | wth->file_encap = WTAP_ENCAP_PPP_WITH_PHDR; |
289 | 0 | wth->file_type_subtype = pppdump_file_type_subtype; |
290 | |
|
291 | 0 | wth->snapshot_length = PPPD_BUF_SIZE; /* just guessing */ |
292 | 0 | wth->subtype_read = pppdump_read; |
293 | 0 | wth->subtype_seek_read = pppdump_seek_read; |
294 | 0 | wth->subtype_close = pppdump_close; |
295 | 0 | wth->file_tsprec = WTAP_TSPREC_100_MSEC; |
296 | |
|
297 | 0 | state->seek_state = g_new(pppdump_t,1); |
298 | | |
299 | | /* If we have a random stream open, we're going to be reading |
300 | | the file randomly; set up a GPtrArray of pointers to |
301 | | information about how to retrieve the data for each packet. */ |
302 | 0 | if (wth->random_fh != NULL) |
303 | 0 | state->pids = g_ptr_array_new(); |
304 | 0 | else |
305 | 0 | state->pids = NULL; |
306 | 0 | state->pkt_cnt = 0; |
307 | | |
308 | | /* |
309 | | * Add an IDB; we don't know how many interfaces were |
310 | | * involved, so we just say one interface, about which |
311 | | * we only know the link-layer type, snapshot length, |
312 | | * and time stamp resolution. |
313 | | */ |
314 | 0 | wtap_add_generated_idb(wth); |
315 | |
|
316 | 0 | return WTAP_OPEN_MINE; |
317 | 0 | } |
318 | | |
319 | | /* Set part of the struct wtap_rec. */ |
320 | | static void |
321 | | pppdump_set_phdr(wtap *wth, wtap_rec *rec, int num_bytes, direction_enum direction) |
322 | 0 | { |
323 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
324 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
325 | 0 | rec->rec_header.packet_header.len = num_bytes; |
326 | 0 | rec->rec_header.packet_header.caplen = num_bytes; |
327 | |
|
328 | 0 | rec->rec_header.packet_header.pseudo_header.p2p.sent = (direction == DIRECTION_SENT ? true : false); |
329 | 0 | } |
330 | | |
331 | | /* Find the next packet and parse it; called from wtap_read(). */ |
332 | | static bool |
333 | | pppdump_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, |
334 | | int64_t *data_offset) |
335 | 0 | { |
336 | 0 | int num_bytes; |
337 | 0 | direction_enum direction; |
338 | 0 | pppdump_t *state; |
339 | 0 | pkt_id *pid; |
340 | |
|
341 | 0 | state = (pppdump_t *)wth->priv; |
342 | | |
343 | | /* If we have a random stream open, allocate a structure to hold |
344 | | the information needed to read this packet's data again. */ |
345 | 0 | if (wth->random_fh != NULL) { |
346 | 0 | pid = g_new(pkt_id, 1); |
347 | 0 | if (!pid) { |
348 | 0 | *err = errno; /* assume a malloc failed and set "errno" */ |
349 | 0 | return false; |
350 | 0 | } |
351 | 0 | pid->offset = 0; |
352 | 0 | } else |
353 | 0 | pid = NULL; /* sequential only */ |
354 | | |
355 | 0 | ws_buffer_assure_space(&rec->data, PPPD_BUF_SIZE); |
356 | 0 | if (!collate(state, wth->fh, err, err_info, ws_buffer_start_ptr(&rec->data), |
357 | 0 | &num_bytes, &direction, pid, 0)) { |
358 | 0 | g_free(pid); |
359 | 0 | return false; |
360 | 0 | } |
361 | | |
362 | 0 | if (pid != NULL) |
363 | 0 | pid->dir = direction; |
364 | |
|
365 | 0 | if (pid != NULL) |
366 | 0 | g_ptr_array_add(state->pids, pid); |
367 | | /* The user's data_offset is not really an offset, but a packet number. */ |
368 | 0 | *data_offset = state->pkt_cnt; |
369 | 0 | state->pkt_cnt++; |
370 | |
|
371 | 0 | pppdump_set_phdr(wth, rec, num_bytes, direction); |
372 | 0 | rec->presence_flags = WTAP_HAS_TS; |
373 | 0 | rec->ts.secs = state->timestamp; |
374 | 0 | rec->ts.nsecs = state->tenths * 100000000; |
375 | |
|
376 | 0 | return true; |
377 | 0 | } |
378 | | |
379 | | /* Returns number of bytes copied for record, -1 if failure. |
380 | | * |
381 | | * This is modeled after pppdump.c, the utility to parse pppd log files; it |
382 | | * comes with the ppp distribution. |
383 | | */ |
384 | | static int |
385 | | process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, uint8_t *pd, |
386 | | int *err, char **err_info, pkt_id *pid) |
387 | 0 | { |
388 | 0 | int c; |
389 | 0 | int num_bytes = n; |
390 | 0 | int num_written; |
391 | |
|
392 | 0 | for (; num_bytes > 0; --num_bytes) { |
393 | 0 | c = file_getc(fh); |
394 | 0 | if (c == EOF) { |
395 | 0 | *err = file_error(fh, err_info); |
396 | 0 | if (*err == 0) { |
397 | 0 | *err = WTAP_ERR_SHORT_READ; |
398 | 0 | } |
399 | 0 | return -1; |
400 | 0 | } |
401 | 0 | state->offset++; |
402 | 0 | switch (c) { |
403 | 0 | case 0x7e: |
404 | | /* |
405 | | * Flag Sequence for RFC 1662 HDLC-like |
406 | | * framing. |
407 | | * |
408 | | * As this is a raw trace of octets going |
409 | | * over the wire, and that might include |
410 | | * the login sequence, there is no |
411 | | * guarantee that *only* PPP traffic |
412 | | * appears in this file, so there is no |
413 | | * guarantee that the first 0x7e we see is |
414 | | * a start flag sequence, and therefore we |
415 | | * cannot safely ignore all characters up |
416 | | * to the first 0x7e, and therefore we |
417 | | * might end up with some bogus PPP |
418 | | * packets. |
419 | | */ |
420 | 0 | if (pkt->cnt > 0) { |
421 | | /* |
422 | | * We've seen stuff before this, |
423 | | * so this is the end of a frame. |
424 | | * Make a frame out of that stuff. |
425 | | */ |
426 | 0 | pkt->esc = false; |
427 | |
|
428 | 0 | num_written = pkt->cnt; |
429 | 0 | pkt->cnt = 0; |
430 | 0 | if (num_written <= 0) { |
431 | 0 | return 0; |
432 | 0 | } |
433 | | |
434 | 0 | if (num_written > PPPD_BUF_SIZE) { |
435 | 0 | *err = WTAP_ERR_BAD_FILE; |
436 | 0 | *err_info = ws_strdup_printf("pppdump: File has %u-byte packet, bigger than maximum of %u", |
437 | 0 | num_written, PPPD_BUF_SIZE); |
438 | 0 | return -1; |
439 | 0 | } |
440 | | |
441 | 0 | memcpy(pd, pkt->buf, num_written); |
442 | | |
443 | | /* |
444 | | * Remember the offset of the |
445 | | * first record containing data |
446 | | * for this packet, and how far |
447 | | * into that record to skip to |
448 | | * get to the beginning of the |
449 | | * data for this packet; the number |
450 | | * of bytes to skip into that record |
451 | | * is the file offset of the first |
452 | | * byte of this packet minus the |
453 | | * file offset of the first byte of |
454 | | * this record, minus 3 bytes for the |
455 | | * header of this record (which, if |
456 | | * we re-read this record, we will |
457 | | * process, not skip). |
458 | | */ |
459 | 0 | if (pid) { |
460 | 0 | pid->offset = pkt->id_offset; |
461 | 0 | pid->num_bytes_to_skip = |
462 | 0 | pkt->sd_offset - pkt->id_offset - 3; |
463 | 0 | ws_assert(pid->num_bytes_to_skip >= 0); |
464 | 0 | } |
465 | |
|
466 | 0 | num_bytes--; |
467 | 0 | if (num_bytes > 0) { |
468 | | /* |
469 | | * There's more data in this |
470 | | * record. |
471 | | * Set the initial data offset |
472 | | * for the next packet. |
473 | | */ |
474 | 0 | pkt->id_offset = pkt->cd_offset; |
475 | 0 | pkt->sd_offset = state->offset; |
476 | 0 | } else { |
477 | | /* |
478 | | * There is no more data in |
479 | | * this record. |
480 | | * Thus, we don't have the |
481 | | * initial data offset for |
482 | | * the next packet. |
483 | | */ |
484 | 0 | pkt->id_offset = 0; |
485 | 0 | pkt->sd_offset = 0; |
486 | 0 | } |
487 | 0 | state->num_bytes = num_bytes; |
488 | 0 | state->pkt = pkt; |
489 | 0 | return num_written; |
490 | 0 | } |
491 | 0 | break; |
492 | | |
493 | 0 | case 0x7d: |
494 | | /* |
495 | | * Control Escape octet for octet-stuffed |
496 | | * RFC 1662 HDLC-like framing. |
497 | | */ |
498 | 0 | if (!pkt->esc) { |
499 | | /* |
500 | | * Control Escape not preceded by |
501 | | * Control Escape; discard it |
502 | | * but XOR the next octet with |
503 | | * 0x20. |
504 | | */ |
505 | 0 | pkt->esc = true; |
506 | 0 | break; |
507 | 0 | } |
508 | | /* |
509 | | * Control Escape preceded by Control Escape; |
510 | | * treat it as an ordinary character, |
511 | | * by falling through. |
512 | | */ |
513 | | |
514 | | /* FALL THROUGH */ |
515 | 0 | default: |
516 | 0 | if (pkt->esc) { |
517 | | /* |
518 | | * This character was preceded by |
519 | | * Control Escape, so XOR it with |
520 | | * 0x20, as per RFC 1662's octet- |
521 | | * stuffed framing, and clear |
522 | | * the flag saying that the |
523 | | * character should be escaped. |
524 | | */ |
525 | 0 | c ^= 0x20; |
526 | 0 | pkt->esc = false; |
527 | 0 | } |
528 | |
|
529 | 0 | if (pkt->cnt >= PPPD_BUF_SIZE) { |
530 | 0 | *err = WTAP_ERR_BAD_FILE; |
531 | 0 | *err_info = ws_strdup_printf("pppdump: File has %u-byte packet, bigger than maximum of %u", |
532 | 0 | pkt->cnt - 1, PPPD_BUF_SIZE); |
533 | 0 | return -1; |
534 | 0 | } |
535 | 0 | pkt->buf[pkt->cnt++] = c; |
536 | 0 | break; |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | | /* we could have run out of bytes to read */ |
541 | 0 | return 0; |
542 | 0 | } |
543 | | |
544 | | /* Returns true if packet data copied, false if error occurred or EOF (no more records). */ |
545 | | static bool |
546 | | collate(pppdump_t* state, FILE_T fh, int *err, char **err_info, uint8_t *pd, |
547 | | int *num_bytes, direction_enum *direction, pkt_id *pid, |
548 | | int64_t num_bytes_to_skip) |
549 | 0 | { |
550 | 0 | int id; |
551 | 0 | pkt_t *pkt = NULL; |
552 | 0 | int byte0, byte1; |
553 | 0 | int n, num_written = 0; |
554 | 0 | int64_t start_offset; |
555 | 0 | uint32_t time_long; |
556 | 0 | uint8_t time_short; |
557 | | |
558 | | /* |
559 | | * Process any data left over in the current record when doing |
560 | | * sequential processing. |
561 | | */ |
562 | 0 | if (state->num_bytes > 0) { |
563 | 0 | ws_assert(num_bytes_to_skip == 0); |
564 | 0 | pkt = state->pkt; |
565 | 0 | num_written = process_data(state, fh, pkt, state->num_bytes, |
566 | 0 | pd, err, err_info, pid); |
567 | |
|
568 | 0 | if (num_written < 0) { |
569 | 0 | return false; |
570 | 0 | } |
571 | 0 | else if (num_written > 0) { |
572 | 0 | *num_bytes = num_written; |
573 | 0 | *direction = pkt->dir; |
574 | 0 | return true; |
575 | 0 | } |
576 | | /* if 0 bytes written, keep processing */ |
577 | 0 | } else { |
578 | | /* |
579 | | * We didn't have any data left over, so the packet will |
580 | | * start at the beginning of a record. |
581 | | */ |
582 | 0 | if (pid) |
583 | 0 | pid->num_bytes_to_skip = 0; |
584 | 0 | } |
585 | | |
586 | | /* |
587 | | * That didn't get all the data for this packet, so process |
588 | | * subsequent records. |
589 | | */ |
590 | 0 | start_offset = state->offset; |
591 | 0 | while ((id = file_getc(fh)) != EOF) { |
592 | 0 | state->offset++; |
593 | 0 | switch (id) { |
594 | 0 | case PPPD_SENT_DATA: |
595 | 0 | case PPPD_RECV_DATA: |
596 | 0 | pkt = id == PPPD_SENT_DATA ? &state->spkt : &state->rpkt; |
597 | | |
598 | | /* |
599 | | * Save the offset of the beginning of |
600 | | * the current record. |
601 | | */ |
602 | 0 | pkt->cd_offset = state->offset - 1; |
603 | | |
604 | | /* |
605 | | * Get the length of the record. |
606 | | */ |
607 | 0 | byte0 = file_getc(fh); |
608 | 0 | if (byte0 == EOF) |
609 | 0 | goto done; |
610 | 0 | state->offset++; |
611 | 0 | byte1 = file_getc(fh); |
612 | 0 | if (byte1 == EOF) |
613 | 0 | goto done; |
614 | 0 | state->offset++; |
615 | 0 | n = (byte0 << 8) | byte1; |
616 | |
|
617 | 0 | if (pkt->id_offset == 0) { |
618 | | /* |
619 | | * We don't have the initial data |
620 | | * offset for this packet, which |
621 | | * means this is the first |
622 | | * data record for that packet. |
623 | | * Save the offset of the |
624 | | * beginning of that record and |
625 | | * the offset of the first data |
626 | | * byte in the packet, which is |
627 | | * the first data byte in the |
628 | | * record. |
629 | | */ |
630 | 0 | pkt->id_offset = pkt->cd_offset; |
631 | 0 | pkt->sd_offset = state->offset; |
632 | 0 | } |
633 | |
|
634 | 0 | if (n == 0) |
635 | 0 | continue; |
636 | | |
637 | 0 | ws_assert(num_bytes_to_skip < n); |
638 | 0 | while (num_bytes_to_skip) { |
639 | 0 | if (file_getc(fh) == EOF) |
640 | 0 | goto done; |
641 | 0 | state->offset++; |
642 | 0 | num_bytes_to_skip--; |
643 | 0 | n--; |
644 | 0 | } |
645 | 0 | num_written = process_data(state, fh, pkt, n, |
646 | 0 | pd, err, err_info, pid); |
647 | |
|
648 | 0 | if (num_written < 0) { |
649 | 0 | return false; |
650 | 0 | } |
651 | 0 | else if (num_written > 0) { |
652 | 0 | *num_bytes = num_written; |
653 | 0 | *direction = pkt->dir; |
654 | 0 | return true; |
655 | 0 | } |
656 | | /* if 0 bytes written, keep looping */ |
657 | 0 | break; |
658 | | |
659 | 0 | case PPPD_SEND_DELIM: |
660 | 0 | case PPPD_RECV_DELIM: |
661 | | /* What can we do? */ |
662 | 0 | break; |
663 | | |
664 | 0 | case PPPD_RESET_TIME: |
665 | 0 | if (!wtap_read_bytes(fh, &time_long, sizeof(uint32_t), err, err_info)) |
666 | 0 | return false; |
667 | 0 | state->offset += sizeof(uint32_t); |
668 | 0 | state->timestamp = pntohu32(&time_long); |
669 | 0 | state->tenths = 0; |
670 | 0 | break; |
671 | | |
672 | 0 | case PPPD_TIME_STEP_LONG: |
673 | 0 | if (!wtap_read_bytes(fh, &time_long, sizeof(uint32_t), err, err_info)) |
674 | 0 | return false; |
675 | 0 | state->offset += sizeof(uint32_t); |
676 | 0 | state->tenths += pntohu32(&time_long); |
677 | |
|
678 | 0 | if (state->tenths >= 10) { |
679 | 0 | state->timestamp += state->tenths / 10; |
680 | 0 | state->tenths = state->tenths % 10; |
681 | 0 | } |
682 | |
|
683 | 0 | break; |
684 | | |
685 | 0 | case PPPD_TIME_STEP_SHORT: |
686 | 0 | if (!wtap_read_bytes(fh, &time_short, sizeof(uint8_t), err, err_info)) |
687 | 0 | return false; |
688 | 0 | state->offset += sizeof(uint8_t); |
689 | 0 | state->tenths += time_short; |
690 | |
|
691 | 0 | if (state->tenths >= 10) { |
692 | 0 | state->timestamp += state->tenths / 10; |
693 | 0 | state->tenths = state->tenths % 10; |
694 | 0 | } |
695 | |
|
696 | 0 | break; |
697 | | |
698 | 0 | default: |
699 | | /* XXX - bad file */ |
700 | 0 | *err = WTAP_ERR_BAD_FILE; |
701 | 0 | *err_info = ws_strdup_printf("pppdump: bad ID byte 0x%02x", id); |
702 | 0 | return false; |
703 | 0 | } |
704 | |
|
705 | 0 | } |
706 | | |
707 | 0 | done: |
708 | 0 | *err = file_error(fh, err_info); |
709 | 0 | if (*err == 0) { |
710 | 0 | if (state->offset != start_offset) { |
711 | | /* |
712 | | * We read at least one byte, so we were working |
713 | | * on a record; an EOF means that record was |
714 | | * cut short. |
715 | | */ |
716 | 0 | *err = WTAP_ERR_SHORT_READ; |
717 | 0 | } |
718 | 0 | } |
719 | 0 | return false; |
720 | 0 | } |
721 | | |
722 | | |
723 | | |
724 | | /* Used to read packets in random-access fashion */ |
725 | | static bool |
726 | | pppdump_seek_read(wtap *wth, |
727 | | int64_t seek_off, |
728 | | wtap_rec *rec, |
729 | | int *err, |
730 | | char **err_info) |
731 | 0 | { |
732 | 0 | int num_bytes; |
733 | 0 | uint8_t *pd; |
734 | 0 | direction_enum direction; |
735 | 0 | pppdump_t *state; |
736 | 0 | pkt_id *pid; |
737 | 0 | int64_t num_bytes_to_skip; |
738 | |
|
739 | 0 | state = (pppdump_t *)wth->priv; |
740 | |
|
741 | 0 | pid = (pkt_id *)g_ptr_array_index(state->pids, seek_off); |
742 | 0 | if (!pid) { |
743 | 0 | *err = WTAP_ERR_BAD_FILE; /* XXX - better error? */ |
744 | 0 | *err_info = g_strdup("pppdump: PID not found for record"); |
745 | 0 | return false; |
746 | 0 | } |
747 | | |
748 | 0 | if (file_seek(wth->random_fh, pid->offset, SEEK_SET, err) == -1) |
749 | 0 | return false; |
750 | | |
751 | 0 | init_state(state->seek_state); |
752 | 0 | state->seek_state->offset = pid->offset; |
753 | |
|
754 | 0 | ws_buffer_assure_space(&rec->data, PPPD_BUF_SIZE); |
755 | 0 | pd = ws_buffer_start_ptr(&rec->data); |
756 | | |
757 | | /* |
758 | | * We'll start reading at the first record containing data from |
759 | | * this packet; however, that doesn't mean "collate()" will |
760 | | * stop only when we've read that packet, as there might be |
761 | | * data for packets going in the other direction as well, and |
762 | | * we might finish processing one of those packets before we |
763 | | * finish processing the packet we're reading. |
764 | | * |
765 | | * Therefore, we keep reading until we get a packet that's |
766 | | * going in the direction we want. |
767 | | */ |
768 | 0 | num_bytes_to_skip = pid->num_bytes_to_skip; |
769 | 0 | do { |
770 | 0 | if (!collate(state->seek_state, wth->random_fh, err, err_info, |
771 | 0 | pd, &num_bytes, &direction, NULL, num_bytes_to_skip)) |
772 | 0 | return false; |
773 | 0 | num_bytes_to_skip = 0; |
774 | 0 | } while (direction != pid->dir); |
775 | | |
776 | 0 | pppdump_set_phdr(wth, rec, num_bytes, pid->dir); |
777 | |
|
778 | 0 | return true; |
779 | 0 | } |
780 | | |
781 | | static void |
782 | | pppdump_close(wtap *wth) |
783 | 0 | { |
784 | 0 | pppdump_t *state; |
785 | |
|
786 | 0 | state = (pppdump_t *)wth->priv; |
787 | |
|
788 | 0 | if (state->seek_state) { /* should always be true */ |
789 | 0 | g_free(state->seek_state); |
790 | 0 | } |
791 | |
|
792 | 0 | if (state->pids) { |
793 | 0 | unsigned int i; |
794 | 0 | for (i = 0; i < g_ptr_array_len(state->pids); i++) { |
795 | 0 | g_free(g_ptr_array_index(state->pids, i)); |
796 | 0 | } |
797 | 0 | g_ptr_array_free(state->pids, true); |
798 | 0 | } |
799 | 0 | } |
800 | | |
801 | | static const struct supported_block_type pppdump_blocks_supported[] = { |
802 | | /* |
803 | | * We support packet blocks, with no comments or other options. |
804 | | */ |
805 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
806 | | }; |
807 | | |
808 | | static const struct file_type_subtype_info pppdump_info = { |
809 | | "pppd log (pppdump format)", "pppd", NULL, NULL, |
810 | | false, BLOCKS_SUPPORTED(pppdump_blocks_supported), |
811 | | NULL, NULL, NULL |
812 | | }; |
813 | | |
814 | | void register_pppdump(void) |
815 | 14 | { |
816 | 14 | pppdump_file_type_subtype = wtap_register_file_type_subtype(&pppdump_info); |
817 | | |
818 | | /* |
819 | | * Register name for backwards compatibility with the |
820 | | * wtap_filetypes table in Lua. |
821 | | */ |
822 | 14 | wtap_register_backwards_compatibility_lua_name("PPPDUMP", |
823 | 14 | pppdump_file_type_subtype); |
824 | 14 | } |
825 | | |
826 | | /* |
827 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
828 | | * |
829 | | * Local variables: |
830 | | * c-basic-offset: 8 |
831 | | * tab-width: 8 |
832 | | * indent-tabs-mode: t |
833 | | * End: |
834 | | * |
835 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
836 | | * :indentSize=8:tabSize=8:noTabs=false: |
837 | | */ |