1 /* $OpenBSD: savefile.c,v 1.18 2023/08/10 15:47:05 sashan Exp $ */
2
3 /*
4 * Copyright (c) 1993, 1994, 1995, 1996, 1997
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that: (1) source code distributions
9 * retain the above copyright notice and this paragraph in its entirety, (2)
10 * distributions including binary code include the above copyright notice and
11 * this paragraph in its entirety in the documentation or other materials
12 * provided with the distribution, and (3) all advertising materials mentioning
13 * features or use of this software display the following acknowledgement:
14 * ``This product includes software developed by the University of California,
15 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
16 * the University nor the names of its contributors may be used to endorse
17 * or promote products derived from this software without specific prior
18 * written permission.
19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 *
23 * savefile.c - supports offline use of tcpdump
24 * Extraction/creation by Jeffrey Mogul, DECWRL
25 * Modified by Steve McCanne, LBL.
26 *
27 * Used to save the received packet headers, after filtering, to
28 * a file, and then read them later.
29 * The first record in the file contains saved values for the machine
30 * dependent values so we can print the dump file on any architecture.
31 */
32
33 #include <sys/types.h>
34 #include <sys/time.h>
35
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #ifdef HAVE_OS_PROTO_H
43 #include "os-proto.h"
44 #endif
45
46 #include "pcap-int.h"
47
48 #define TCPDUMP_MAGIC 0xa1b2c3d4
49
50 /*
51 * We use the "receiver-makes-right" approach to byte order,
52 * because time is at a premium when we are writing the file.
53 * In other words, the pcap_file_header and pcap_pkthdr,
54 * records are written in host byte order.
55 * Note that the packets are always written in network byte order.
56 *
57 * ntoh[ls] aren't sufficient because we might need to swap on a big-endian
58 * machine (if the file was written in little-end order).
59 */
60 #define SWAPLONG(y) \
61 ((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
62 #define SWAPSHORT(y) \
63 ( (((y)&0xff)<<8) | ((u_short)((y)&0xff00)>>8) )
64
65 #define SFERR_TRUNC 1
66 #define SFERR_BADVERSION 2
67 #define SFERR_BADF 3
68 #define SFERR_EOF 4 /* not really an error, just a status */
69
70 static int
sf_write_header(FILE * fp,int linktype,int thiszone,int snaplen)71 sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
72 {
73 struct pcap_file_header hdr;
74
75 hdr.magic = TCPDUMP_MAGIC;
76 hdr.version_major = PCAP_VERSION_MAJOR;
77 hdr.version_minor = PCAP_VERSION_MINOR;
78
79 hdr.thiszone = thiszone;
80 hdr.snaplen = snaplen;
81 hdr.sigfigs = 0;
82 hdr.linktype = linktype;
83
84 if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
85 return (-1);
86
87 return (0);
88 }
89
90 static void
swap_hdr(struct pcap_file_header * hp)91 swap_hdr(struct pcap_file_header *hp)
92 {
93 hp->version_major = SWAPSHORT(hp->version_major);
94 hp->version_minor = SWAPSHORT(hp->version_minor);
95 hp->thiszone = SWAPLONG(hp->thiszone);
96 hp->sigfigs = SWAPLONG(hp->sigfigs);
97 hp->snaplen = SWAPLONG(hp->snaplen);
98 hp->linktype = SWAPLONG(hp->linktype);
99 }
100
101 pcap_t *
pcap_open_offline(const char * fname,char * errbuf)102 pcap_open_offline(const char *fname, char *errbuf)
103 {
104 pcap_t *p;
105 FILE *fp;
106
107 if (fname[0] == '-' && fname[1] == '\0')
108 fp = stdin;
109 else {
110 fp = fopen(fname, "r");
111 if (fp == NULL) {
112 snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname,
113 pcap_strerror(errno));
114 return (NULL);
115 }
116 }
117 p = pcap_fopen_offline(fp, errbuf);
118 if (p == NULL) {
119 if (fp != stdin)
120 fclose(fp);
121 }
122 return (p);
123 }
124
125 pcap_t *
pcap_fopen_offline(FILE * fp,char * errbuf)126 pcap_fopen_offline(FILE *fp, char *errbuf)
127 {
128 pcap_t *p;
129 struct pcap_file_header hdr;
130 int linklen;
131
132 p = calloc(1, sizeof(*p));
133 if (p == NULL) {
134 strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
135 return (NULL);
136 }
137
138 /*
139 * Set this field so we don't double-close in pcap_close!
140 */
141 p->fd = -1;
142
143 if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
144 snprintf(errbuf, PCAP_ERRBUF_SIZE, "fread: %s",
145 pcap_strerror(errno));
146 goto bad;
147 }
148 if (hdr.magic != TCPDUMP_MAGIC) {
149 if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) {
150 snprintf(errbuf, PCAP_ERRBUF_SIZE,
151 "bad dump file format");
152 goto bad;
153 }
154 p->sf.swapped = 1;
155 swap_hdr(&hdr);
156 }
157 if (hdr.version_major < PCAP_VERSION_MAJOR) {
158 snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format");
159 goto bad;
160 }
161 p->tzoff = hdr.thiszone;
162 p->snapshot = hdr.snaplen;
163 /*
164 * Handle some LINKTYPE_ values in pcap headers that aren't
165 * the same as the corresponding OpenBSD DLT_ values.
166 *
167 * Those LINKTYPE_ values were assigned for DLT_s whose
168 * numerical values differ between platforms, so that
169 * the link-layer type value in pcap file headers can
170 * be platform-independent. This means that code reading
171 * a pcap file doesn't have to know on which platform a
172 * file was written in order to read it correctly.
173 *
174 * See
175 *
176 * https://www.tcpdump.org/linktypes.html
177 *
178 * for the current list of LINKTYPE_ values and the corresponding
179 * DLT_ values.
180 */
181 switch (hdr.linktype) {
182
183 case 100:
184 /* LINKTYPE_ATM_RFC1483 */
185 p->linktype = DLT_ATM_RFC1483;
186 break;
187
188 case 101:
189 /* LINKTYPE_RAW */
190 p->linktype = DLT_RAW;
191 break;
192
193 case 102:
194 /* LINKTYPE_SLIP_BSDOS */
195 p->linktype = DLT_SLIP_BSDOS;
196 break;
197
198 case 103:
199 /* LINKTYPE_PPP_BSDOS */
200 p->linktype = DLT_PPP_BSDOS;
201 break;
202
203 case 108:
204 /* LINKTYPE_LOOP */
205 p->linktype = DLT_LOOP;
206 break;
207
208 case 109:
209 /* LINKTYPE_ENC */
210 p->linktype = DLT_ENC;
211 break;
212
213 case 256:
214 /* LINKTYPE_PFSYNC */
215 p->linktype = DLT_PFSYNC;
216 break;
217
218 default:
219 p->linktype = hdr.linktype;
220 break;
221 }
222 p->sf.rfile = fp;
223 p->bufsize = hdr.snaplen;
224
225 /* Align link header as required for proper data alignment */
226 /* XXX should handle all types */
227 switch (p->linktype) {
228
229 case DLT_EN10MB:
230 linklen = 14;
231 break;
232
233 case DLT_FDDI:
234 linklen = 13 + 8; /* fddi_header + llc */
235 break;
236
237 case DLT_NULL:
238 default:
239 linklen = 0;
240 break;
241 }
242
243 if (p->bufsize < 0)
244 p->bufsize = BPF_MAXBUFSIZE;
245 p->sf.base = malloc(p->bufsize + BPF_ALIGNMENT);
246 if (p->sf.base == NULL) {
247 strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
248 goto bad;
249 }
250 p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
251 p->sf.version_major = hdr.version_major;
252 p->sf.version_minor = hdr.version_minor;
253 #ifdef PCAP_FDDIPAD
254 /* XXX padding only needed for kernel fcode */
255 pcap_fddipad = 0;
256 #endif
257
258 return (p);
259 bad:
260 free(p);
261 return (NULL);
262 }
263
264 /*
265 * Read sf_readfile and return the next packet. Return the header in hdr
266 * and the contents in buf. Return 0 on success, SFERR_EOF if there were
267 * no more packets, and SFERR_TRUNC if a partial packet was encountered.
268 */
269 static int
sf_next_packet(pcap_t * p,struct pcap_pkthdr * hdr,u_char * buf,int buflen)270 sf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen)
271 {
272 FILE *fp = p->sf.rfile;
273
274 /* read the stamp */
275 if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) {
276 /* probably an EOF, though could be a truncated packet */
277 return (1);
278 }
279
280 if (p->sf.swapped) {
281 /* these were written in opposite byte order */
282 hdr->caplen = SWAPLONG(hdr->caplen);
283 hdr->len = SWAPLONG(hdr->len);
284 hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
285 hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
286 }
287 /*
288 * We interchanged the caplen and len fields at version 2.3,
289 * in order to match the bpf header layout. But unfortunately
290 * some files were written with version 2.3 in their headers
291 * but without the interchanged fields.
292 */
293 if (p->sf.version_minor < 3 ||
294 (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) {
295 int t = hdr->caplen;
296 hdr->caplen = hdr->len;
297 hdr->len = t;
298 }
299
300 if (hdr->caplen > buflen) {
301 /*
302 * This can happen due to Solaris 2.3 systems tripping
303 * over the BUFMOD problem and not setting the snapshot
304 * correctly in the savefile header. If the caplen isn't
305 * grossly wrong, try to salvage.
306 */
307 static u_char *tp = NULL;
308 static int tsize = 0;
309
310 if (hdr->caplen > 65535) {
311 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
312 "bogus savefile header");
313 return (-1);
314 }
315
316 if (tsize < hdr->caplen) {
317 tsize = ((hdr->caplen + 1023) / 1024) * 1024;
318 free(tp);
319 tp = malloc(tsize);
320 if (tp == NULL) {
321 tsize = 0;
322 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
323 "BUFMOD hack malloc");
324 return (-1);
325 }
326 }
327 if (fread((char *)tp, hdr->caplen, 1, fp) != 1) {
328 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
329 "truncated dump file");
330 return (-1);
331 }
332 /*
333 * We can only keep up to buflen bytes. Since caplen > buflen
334 * is exactly how we got here, we know we can only keep the
335 * first buflen bytes and must drop the remainder. Adjust
336 * caplen accordingly, so we don't get confused later as
337 * to how many bytes we have to play with.
338 */
339 hdr->caplen = buflen;
340 memcpy((char *)buf, (char *)tp, buflen);
341
342 } else {
343 /* read the packet itself */
344
345 if (fread((char *)buf, hdr->caplen, 1, fp) != 1) {
346 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
347 "truncated dump file");
348 return (-1);
349 }
350 }
351 return (0);
352 }
353
354 /*
355 * Print out packets stored in the file initialized by sf_read_init().
356 * If cnt > 0, return after 'cnt' packets, otherwise continue until eof.
357 */
358 int
pcap_offline_read(pcap_t * p,int cnt,pcap_handler callback,u_char * user)359 pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
360 {
361 struct bpf_insn *fcode = p->fcode.bf_insns;
362 int status = 0;
363 int n = 0;
364
365 while (status == 0) {
366 struct pcap_pkthdr h;
367
368 /*
369 * Has "pcap_breakloop()" been called?
370 * If so, return immediately - if we haven't read any
371 * packets, clear the flag and return -2 to indicate
372 * that we were told to break out of the loop, otherwise
373 * leave the flag set, so that the *next* call will break
374 * out of the loop without having read any packets, and
375 * return the number of packets we've processed so far.
376 */
377 if (p->break_loop) {
378 if (n == 0) {
379 p->break_loop = 0;
380 return (PCAP_ERROR_BREAK);
381 } else
382 return (n);
383 }
384
385 status = sf_next_packet(p, &h, p->buffer, p->bufsize);
386 if (status) {
387 if (status == 1)
388 return (0);
389 return (status);
390 }
391
392 if (fcode == NULL ||
393 bpf_filter(fcode, p->buffer, h.len, h.caplen)) {
394 (*callback)(user, &h, p->buffer);
395 if (++n >= cnt && cnt > 0)
396 break;
397 }
398 }
399 /*XXX this breaks semantics tcpslice expects */
400 return (n);
401 }
402
403 /*
404 * Output a packet to the initialized dump file.
405 */
406 void
pcap_dump(u_char * user,const struct pcap_pkthdr * h,const u_char * sp)407 pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
408 {
409 FILE *f;
410
411 f = (FILE *)user;
412 /* XXX we should check the return status */
413 (void)fwrite((char *)h, sizeof(*h), 1, f);
414 (void)fwrite((char *)sp, h->caplen, 1, f);
415 }
416
417 static pcap_dumper_t *
pcap_setup_dump(pcap_t * p,FILE * f,const char * fname)418 pcap_setup_dump(pcap_t *p, FILE *f, const char *fname)
419 {
420 if (sf_write_header(f, p->linktype, p->tzoff, p->snapshot) == -1) {
421 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s",
422 fname, pcap_strerror(errno));
423 if (f != stdout)
424 (void)fclose(f);
425 return (NULL);
426 }
427 return ((pcap_dumper_t *)f);
428 }
429
430 /*
431 * Initialize so that sf_write() will output to the file named 'fname'.
432 */
433 pcap_dumper_t *
pcap_dump_open(pcap_t * p,const char * fname)434 pcap_dump_open(pcap_t *p, const char *fname)
435 {
436 FILE *f;
437 if (fname[0] == '-' && fname[1] == '\0')
438 f = stdout;
439 else {
440 f = fopen(fname, "w");
441 if (f == NULL) {
442 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
443 fname, pcap_strerror(errno));
444 return (NULL);
445 }
446 }
447 return (pcap_setup_dump(p, f, fname));
448 }
449
450 /*
451 * Initialize so that sf_write() will output to the given stream.
452 */
453 pcap_dumper_t *
pcap_dump_fopen(pcap_t * p,FILE * f)454 pcap_dump_fopen(pcap_t *p, FILE *f)
455 {
456 return (pcap_setup_dump(p, f, "stream"));
457 }
458
459 FILE *
pcap_dump_file(pcap_dumper_t * p)460 pcap_dump_file(pcap_dumper_t *p)
461 {
462 return ((FILE *)p);
463 }
464
465 long
pcap_dump_ftell(pcap_dumper_t * p)466 pcap_dump_ftell(pcap_dumper_t *p)
467 {
468 return (ftell((FILE *)p));
469 }
470
471 int
pcap_dump_flush(pcap_dumper_t * p)472 pcap_dump_flush(pcap_dumper_t *p)
473 {
474
475 if (fflush((FILE *)p) == EOF)
476 return (-1);
477 else
478 return (0);
479 }
480
481 void
pcap_dump_close(pcap_dumper_t * p)482 pcap_dump_close(pcap_dumper_t *p)
483 {
484
485 #ifdef notyet
486 if (ferror((FILE *)p))
487 return-an-error;
488 /* XXX should check return from fclose() too */
489 #endif
490 (void)fclose((FILE *)p);
491 }
492