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