1 /* $OpenBSD: savefile.c,v 1.17 2020/05/27 04:24:01 dlg 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 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 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 * 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 * 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 p->linktype = hdr.linktype; 164 p->sf.rfile = fp; 165 p->bufsize = hdr.snaplen; 166 167 /* Align link header as required for proper data alignment */ 168 /* XXX should handle all types */ 169 switch (p->linktype) { 170 171 case DLT_EN10MB: 172 linklen = 14; 173 break; 174 175 case DLT_FDDI: 176 linklen = 13 + 8; /* fddi_header + llc */ 177 break; 178 179 case DLT_NULL: 180 default: 181 linklen = 0; 182 break; 183 } 184 185 if (p->bufsize < 0) 186 p->bufsize = BPF_MAXBUFSIZE; 187 p->sf.base = malloc(p->bufsize + BPF_ALIGNMENT); 188 if (p->sf.base == NULL) { 189 strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE); 190 goto bad; 191 } 192 p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT); 193 p->sf.version_major = hdr.version_major; 194 p->sf.version_minor = hdr.version_minor; 195 #ifdef PCAP_FDDIPAD 196 /* XXX padding only needed for kernel fcode */ 197 pcap_fddipad = 0; 198 #endif 199 200 return (p); 201 bad: 202 free(p); 203 return (NULL); 204 } 205 206 /* 207 * Read sf_readfile and return the next packet. Return the header in hdr 208 * and the contents in buf. Return 0 on success, SFERR_EOF if there were 209 * no more packets, and SFERR_TRUNC if a partial packet was encountered. 210 */ 211 static int 212 sf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen) 213 { 214 FILE *fp = p->sf.rfile; 215 216 /* read the stamp */ 217 if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) { 218 /* probably an EOF, though could be a truncated packet */ 219 return (1); 220 } 221 222 if (p->sf.swapped) { 223 /* these were written in opposite byte order */ 224 hdr->caplen = SWAPLONG(hdr->caplen); 225 hdr->len = SWAPLONG(hdr->len); 226 hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec); 227 hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec); 228 } 229 /* 230 * We interchanged the caplen and len fields at version 2.3, 231 * in order to match the bpf header layout. But unfortunately 232 * some files were written with version 2.3 in their headers 233 * but without the interchanged fields. 234 */ 235 if (p->sf.version_minor < 3 || 236 (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) { 237 int t = hdr->caplen; 238 hdr->caplen = hdr->len; 239 hdr->len = t; 240 } 241 242 if (hdr->caplen > buflen) { 243 /* 244 * This can happen due to Solaris 2.3 systems tripping 245 * over the BUFMOD problem and not setting the snapshot 246 * correctly in the savefile header. If the caplen isn't 247 * grossly wrong, try to salvage. 248 */ 249 static u_char *tp = NULL; 250 static int tsize = 0; 251 252 if (hdr->caplen > 65535) { 253 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 254 "bogus savefile header"); 255 return (-1); 256 } 257 258 if (tsize < hdr->caplen) { 259 tsize = ((hdr->caplen + 1023) / 1024) * 1024; 260 free(tp); 261 tp = malloc(tsize); 262 if (tp == NULL) { 263 tsize = 0; 264 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 265 "BUFMOD hack malloc"); 266 return (-1); 267 } 268 } 269 if (fread((char *)tp, hdr->caplen, 1, fp) != 1) { 270 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 271 "truncated dump file"); 272 return (-1); 273 } 274 /* 275 * We can only keep up to buflen bytes. Since caplen > buflen 276 * is exactly how we got here, we know we can only keep the 277 * first buflen bytes and must drop the remainder. Adjust 278 * caplen accordingly, so we don't get confused later as 279 * to how many bytes we have to play with. 280 */ 281 hdr->caplen = buflen; 282 memcpy((char *)buf, (char *)tp, buflen); 283 284 } else { 285 /* read the packet itself */ 286 287 if (fread((char *)buf, hdr->caplen, 1, fp) != 1) { 288 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 289 "truncated dump file"); 290 return (-1); 291 } 292 } 293 return (0); 294 } 295 296 /* 297 * Print out packets stored in the file initialized by sf_read_init(). 298 * If cnt > 0, return after 'cnt' packets, otherwise continue until eof. 299 */ 300 int 301 pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 302 { 303 struct bpf_insn *fcode = p->fcode.bf_insns; 304 int status = 0; 305 int n = 0; 306 307 while (status == 0) { 308 struct pcap_pkthdr h; 309 310 /* 311 * Has "pcap_breakloop()" been called? 312 * If so, return immediately - if we haven't read any 313 * packets, clear the flag and return -2 to indicate 314 * that we were told to break out of the loop, otherwise 315 * leave the flag set, so that the *next* call will break 316 * out of the loop without having read any packets, and 317 * return the number of packets we've processed so far. 318 */ 319 if (p->break_loop) { 320 if (n == 0) { 321 p->break_loop = 0; 322 return (PCAP_ERROR_BREAK); 323 } else 324 return (n); 325 } 326 327 status = sf_next_packet(p, &h, p->buffer, p->bufsize); 328 if (status) { 329 if (status == 1) 330 return (0); 331 return (status); 332 } 333 334 if (fcode == NULL || 335 bpf_filter(fcode, p->buffer, h.len, h.caplen)) { 336 (*callback)(user, &h, p->buffer); 337 if (++n >= cnt && cnt > 0) 338 break; 339 } 340 } 341 /*XXX this breaks semantics tcpslice expects */ 342 return (n); 343 } 344 345 /* 346 * Output a packet to the initialized dump file. 347 */ 348 void 349 pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 350 { 351 FILE *f; 352 353 f = (FILE *)user; 354 /* XXX we should check the return status */ 355 (void)fwrite((char *)h, sizeof(*h), 1, f); 356 (void)fwrite((char *)sp, h->caplen, 1, f); 357 } 358 359 static pcap_dumper_t * 360 pcap_setup_dump(pcap_t *p, FILE *f, const char *fname) 361 { 362 if (sf_write_header(f, p->linktype, p->tzoff, p->snapshot) == -1) { 363 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s", 364 fname, pcap_strerror(errno)); 365 if (f != stdout) 366 (void)fclose(f); 367 return (NULL); 368 } 369 return ((pcap_dumper_t *)f); 370 } 371 372 /* 373 * Initialize so that sf_write() will output to the file named 'fname'. 374 */ 375 pcap_dumper_t * 376 pcap_dump_open(pcap_t *p, const char *fname) 377 { 378 FILE *f; 379 if (fname[0] == '-' && fname[1] == '\0') 380 f = stdout; 381 else { 382 f = fopen(fname, "w"); 383 if (f == NULL) { 384 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 385 fname, pcap_strerror(errno)); 386 return (NULL); 387 } 388 } 389 return (pcap_setup_dump(p, f, fname)); 390 } 391 392 /* 393 * Initialize so that sf_write() will output to the given stream. 394 */ 395 pcap_dumper_t * 396 pcap_dump_fopen(pcap_t *p, FILE *f) 397 { 398 return (pcap_setup_dump(p, f, "stream")); 399 } 400 401 FILE * 402 pcap_dump_file(pcap_dumper_t *p) 403 { 404 return ((FILE *)p); 405 } 406 407 long 408 pcap_dump_ftell(pcap_dumper_t *p) 409 { 410 return (ftell((FILE *)p)); 411 } 412 413 int 414 pcap_dump_flush(pcap_dumper_t *p) 415 { 416 417 if (fflush((FILE *)p) == EOF) 418 return (-1); 419 else 420 return (0); 421 } 422 423 void 424 pcap_dump_close(pcap_dumper_t *p) 425 { 426 427 #ifdef notyet 428 if (ferror((FILE *)p)) 429 return-an-error; 430 /* XXX should check return from fclose() too */ 431 #endif 432 (void)fclose((FILE *)p); 433 } 434