1 /* $OpenBSD: savefile.c,v 1.16 2015/12/22 19:51:04 mmcc 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 status = sf_next_packet(p, &h, p->buffer, p->bufsize); 311 if (status) { 312 if (status == 1) 313 return (0); 314 return (status); 315 } 316 317 if (fcode == NULL || 318 bpf_filter(fcode, p->buffer, h.len, h.caplen)) { 319 (*callback)(user, &h, p->buffer); 320 if (++n >= cnt && cnt > 0) 321 break; 322 } 323 } 324 /*XXX this breaks semantics tcpslice expects */ 325 return (n); 326 } 327 328 /* 329 * Output a packet to the initialized dump file. 330 */ 331 void 332 pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 333 { 334 FILE *f; 335 336 f = (FILE *)user; 337 /* XXX we should check the return status */ 338 (void)fwrite((char *)h, sizeof(*h), 1, f); 339 (void)fwrite((char *)sp, h->caplen, 1, f); 340 } 341 342 static pcap_dumper_t * 343 pcap_setup_dump(pcap_t *p, FILE *f, const char *fname) 344 { 345 if (sf_write_header(f, p->linktype, p->tzoff, p->snapshot) == -1) { 346 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s", 347 fname, pcap_strerror(errno)); 348 if (f != stdout) 349 (void)fclose(f); 350 return (NULL); 351 } 352 return ((pcap_dumper_t *)f); 353 } 354 355 /* 356 * Initialize so that sf_write() will output to the file named 'fname'. 357 */ 358 pcap_dumper_t * 359 pcap_dump_open(pcap_t *p, const char *fname) 360 { 361 FILE *f; 362 if (fname[0] == '-' && fname[1] == '\0') 363 f = stdout; 364 else { 365 f = fopen(fname, "w"); 366 if (f == NULL) { 367 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 368 fname, pcap_strerror(errno)); 369 return (NULL); 370 } 371 } 372 return (pcap_setup_dump(p, f, fname)); 373 } 374 375 /* 376 * Initialize so that sf_write() will output to the given stream. 377 */ 378 pcap_dumper_t * 379 pcap_dump_fopen(pcap_t *p, FILE *f) 380 { 381 return (pcap_setup_dump(p, f, "stream")); 382 } 383 384 FILE * 385 pcap_dump_file(pcap_dumper_t *p) 386 { 387 return ((FILE *)p); 388 } 389 390 long 391 pcap_dump_ftell(pcap_dumper_t *p) 392 { 393 return (ftell((FILE *)p)); 394 } 395 396 int 397 pcap_dump_flush(pcap_dumper_t *p) 398 { 399 400 if (fflush((FILE *)p) == EOF) 401 return (-1); 402 else 403 return (0); 404 } 405 406 void 407 pcap_dump_close(pcap_dumper_t *p) 408 { 409 410 #ifdef notyet 411 if (ferror((FILE *)p)) 412 return-an-error; 413 /* XXX should check return from fclose() too */ 414 #endif 415 (void)fclose((FILE *)p); 416 } 417