savefile.c revision 17683
173134Ssobomax/* 273134Ssobomax * Copyright (c) 1993, 1994, 1995, 1996 373134Ssobomax * The Regents of the University of California. All rights reserved. 473134Ssobomax * 573134Ssobomax * Redistribution and use in source and binary forms, with or without 673134Ssobomax * modification, are permitted provided that: (1) source code distributions 773134Ssobomax * retain the above copyright notice and this paragraph in its entirety, (2) 873134Ssobomax * distributions including binary code include the above copyright notice and 973134Ssobomax * this paragraph in its entirety in the documentation or other materials 1073134Ssobomax * provided with the distribution, and (3) all advertising materials mentioning 1173134Ssobomax * features or use of this software display the following acknowledgement: 1273134Ssobomax * ``This product includes software developed by the University of California, 1373134Ssobomax * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 1473134Ssobomax * the University nor the names of its contributors may be used to endorse 1573134Ssobomax * or promote products derived from this software without specific prior 1673134Ssobomax * written permission. 1773134Ssobomax * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 1873134Ssobomax * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 1973134Ssobomax * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 2073134Ssobomax */ 2173134Ssobomax#ifndef lint 2273134Ssobomaxstatic char rcsid[] = 2373134Ssobomax "@(#)$Header: savefile.c,v 1.30 96/07/15 00:48:52 leres Exp $ (LBL)"; 2473134Ssobomax#endif 2573134Ssobomax 2673134Ssobomax/* 2773134Ssobomax * savefile.c - supports offline use of tcpdump 2873134Ssobomax * Extraction/creation by Jeffrey Mogul, DECWRL 2973134Ssobomax * Modified by Steve McCanne, LBL. 3073134Ssobomax * 3173134Ssobomax * Used to save the received packet headers, after filtering, to 3273134Ssobomax * a file, and then read them later. 3373134Ssobomax * The first record in the file contains saved values for the machine 3473134Ssobomax * dependent values so we can print the dump file on any architecture. 3573134Ssobomax */ 3673134Ssobomax 3773134Ssobomax#include <sys/types.h> 3873134Ssobomax#include <sys/time.h> 3973134Ssobomax 4073134Ssobomax#include <errno.h> 4173134Ssobomax#include <memory.h> 4273134Ssobomax#include <stdio.h> 4373134Ssobomax#include <stdlib.h> 4473144Ssobomax#include <unistd.h> 4573134Ssobomax 4673134Ssobomax#include "pcap-int.h" 4773134Ssobomax 4873134Ssobomax#include "gnuc.h" 4973134Ssobomax#ifdef HAVE_OS_PROTO_H 5073134Ssobomax#include "os-proto.h" 5173134Ssobomax#endif 5273134Ssobomax 5373134Ssobomax#define TCPDUMP_MAGIC 0xa1b2c3d4 5473134Ssobomax 5573134Ssobomax/* 5673134Ssobomax * We use the "receiver-makes-right" approach to byte order, 5773134Ssobomax * because time is at a premium when we are writing the file. 5873134Ssobomax * In other words, the pcap_file_header and pcap_pkthdr, 5973134Ssobomax * records are written in host byte order. 6073134Ssobomax * Note that the packets are always written in network byte order. 6174258Ssobomax * 6273144Ssobomax * ntoh[ls] aren't sufficient because we might need to swap on a big-endian 6373134Ssobomax * machine (if the file was written in little-end order). 6473134Ssobomax */ 6573134Ssobomax#define SWAPLONG(y) \ 6673134Ssobomax((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff)) 6774258Ssobomax#define SWAPSHORT(y) \ 6873134Ssobomax ( (((y)&0xff)<<8) | (((y)&0xff00)>>8) ) 6973134Ssobomax 7073134Ssobomax#define SFERR_TRUNC 1 7173144Ssobomax#define SFERR_BADVERSION 2 7273144Ssobomax#define SFERR_BADF 3 7373144Ssobomax#define SFERR_EOF 4 /* not really an error, just a status */ 7473144Ssobomax 7573144Ssobomaxstatic int 7673144Ssobomaxsf_write_header(FILE *fp, int linktype, int thiszone, int snaplen) 7773134Ssobomax{ 7873134Ssobomax struct pcap_file_header hdr; 7973144Ssobomax 8073144Ssobomax hdr.magic = TCPDUMP_MAGIC; 8173134Ssobomax hdr.version_major = PCAP_VERSION_MAJOR; 8273134Ssobomax hdr.version_minor = PCAP_VERSION_MINOR; 8373134Ssobomax 8473134Ssobomax hdr.thiszone = thiszone; 8573134Ssobomax hdr.snaplen = snaplen; 8673134Ssobomax hdr.sigfigs = 0; 8773134Ssobomax hdr.linktype = linktype; 8873134Ssobomax 8973134Ssobomax if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) 9073134Ssobomax return (-1); 9173134Ssobomax 9273134Ssobomax return (0); 9373134Ssobomax} 9473134Ssobomax 9573134Ssobomaxstatic void 9673134Ssobomaxswap_hdr(struct pcap_file_header *hp) 9773134Ssobomax{ 9873134Ssobomax hp->version_major = SWAPSHORT(hp->version_major); 9974258Ssobomax hp->version_minor = SWAPSHORT(hp->version_minor); 10074258Ssobomax hp->thiszone = SWAPLONG(hp->thiszone); 10174258Ssobomax hp->sigfigs = SWAPLONG(hp->sigfigs); 10274258Ssobomax hp->snaplen = SWAPLONG(hp->snaplen); 10374258Ssobomax hp->linktype = SWAPLONG(hp->linktype); 10474258Ssobomax} 10574258Ssobomax 10674258Ssobomaxpcap_t * 10774258Ssobomaxpcap_open_offline(char *fname, char *errbuf) 10874258Ssobomax{ 10974258Ssobomax register pcap_t *p; 11074258Ssobomax register FILE *fp; 11173134Ssobomax struct pcap_file_header hdr; 11273134Ssobomax int linklen; 11373134Ssobomax 11473134Ssobomax p = (pcap_t *)malloc(sizeof(*p)); 11573134Ssobomax if (p == NULL) { 11673134Ssobomax strcpy(errbuf, "out of swap"); 11773134Ssobomax return (NULL); 11873144Ssobomax } 11973144Ssobomax 12073144Ssobomax memset((char *)p, 0, sizeof(*p)); 12173144Ssobomax /* 12273144Ssobomax * Set this field so we don't close stdin in pcap_close! 12373144Ssobomax */ 12473144Ssobomax p->fd = -1; 12573144Ssobomax 12673144Ssobomax if (fname[0] == '-' && fname[1] == '\0') 12773144Ssobomax fp = stdin; 12873144Ssobomax else { 12973144Ssobomax fp = fopen(fname, "r"); 13073144Ssobomax if (fp == NULL) { 13173144Ssobomax sprintf(errbuf, "%s: %s", fname, pcap_strerror(errno)); 13273144Ssobomax goto bad; 13374258Ssobomax } 13473144Ssobomax } 13574258Ssobomax if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 13674258Ssobomax sprintf(errbuf, "fread: %s", pcap_strerror(errno)); 13773144Ssobomax goto bad; 13873144Ssobomax } 13973144Ssobomax if (hdr.magic != TCPDUMP_MAGIC) { 14073134Ssobomax if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) { 14173144Ssobomax sprintf(errbuf, "bad dump file format"); 14273144Ssobomax goto bad; 14373134Ssobomax } 14473144Ssobomax p->sf.swapped = 1; 14573144Ssobomax swap_hdr(&hdr); 14673144Ssobomax } 14773144Ssobomax if (hdr.version_major < PCAP_VERSION_MAJOR) { 14873144Ssobomax sprintf(errbuf, "archaic file format"); 14973144Ssobomax goto bad; 15073144Ssobomax } 15173134Ssobomax p->tzoff = hdr.thiszone; 15273134Ssobomax p->snapshot = hdr.snaplen; 15373134Ssobomax p->linktype = hdr.linktype; 15473134Ssobomax p->sf.rfile = fp; 15573134Ssobomax p->bufsize = hdr.snaplen; 15673134Ssobomax 15774258Ssobomax /* Align link header as required for proper data alignment */ 15874258Ssobomax /* XXX should handle all types */ 15974258Ssobomax switch (p->linktype) { 16074258Ssobomax 16174258Ssobomax case DLT_EN10MB: 16274258Ssobomax linklen = 14; 16373134Ssobomax break; 16473134Ssobomax 16573134Ssobomax case DLT_FDDI: 16673134Ssobomax linklen = 13 + 8; /* fddi_header + llc */ 16773134Ssobomax break; 16873134Ssobomax 16973134Ssobomax case DLT_NULL: 17073134Ssobomax default: 17173134Ssobomax linklen = 0; 17273134Ssobomax break; 17373134Ssobomax } 17473134Ssobomax 17573134Ssobomax p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT); 17673134Ssobomax p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT); 17773134Ssobomax p->sf.version_major = hdr.version_major; 17873134Ssobomax p->sf.version_minor = hdr.version_minor; 17973134Ssobomax#ifdef PCAP_FDDIPAD 18073134Ssobomax /* XXX padding only needed for kernel fcode */ 18173134Ssobomax pcap_fddipad = 0; 18273134Ssobomax#endif 18373134Ssobomax 18473134Ssobomax return (p); 18573134Ssobomax bad: 18673134Ssobomax free(p); 18773134Ssobomax return (NULL); 18873134Ssobomax} 18973134Ssobomax 19073134Ssobomax/* 19173134Ssobomax * Read sf_readfile and return the next packet. Return the header in hdr 19273134Ssobomax * and the contents in buf. Return 0 on success, SFERR_EOF if there were 19373134Ssobomax * no more packets, and SFERR_TRUNC if a partial packet was encountered. 19473134Ssobomax */ 19573134Ssobomaxstatic int 19673134Ssobomaxsf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen) 19773134Ssobomax{ 19873134Ssobomax FILE *fp = p->sf.rfile; 19973134Ssobomax 20073134Ssobomax /* read the stamp */ 20173144Ssobomax if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) { 20273134Ssobomax /* probably an EOF, though could be a truncated packet */ 20373134Ssobomax return (1); 20473134Ssobomax } 20573134Ssobomax 20673134Ssobomax if (p->sf.swapped) { 20773134Ssobomax /* these were written in opposite byte order */ 20873134Ssobomax hdr->caplen = SWAPLONG(hdr->caplen); 20973134Ssobomax hdr->len = SWAPLONG(hdr->len); 21073144Ssobomax hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec); 21173144Ssobomax hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec); 21273144Ssobomax } 21373144Ssobomax /* 21473134Ssobomax * We interchanged the caplen and len fields at version 2.3, 21573134Ssobomax * in order to match the bpf header layout. But unfortunately 21673134Ssobomax * some files were written with version 2.3 in their headers 21773134Ssobomax * but without the interchanged fields. 21873134Ssobomax */ 21973144Ssobomax if (p->sf.version_minor < 3 || 22073144Ssobomax (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) { 22173144Ssobomax int t = hdr->caplen; 22273144Ssobomax hdr->caplen = hdr->len; 22373134Ssobomax hdr->len = t; 22473134Ssobomax } 22573144Ssobomax 22673144Ssobomax if (hdr->caplen > buflen) { 22773134Ssobomax /* 22873134Ssobomax * This can happen due to Solaris 2.3 systems tripping 22973134Ssobomax * over the BUFMOD problem and not setting the snapshot 23073134Ssobomax * correctly in the savefile header. If the caplen isn't 23173134Ssobomax * grossly wrong, try to salvage. 23273134Ssobomax */ 23373134Ssobomax static u_char *tp = NULL; 234 static int tsize = 0; 235 236 if (tsize < hdr->caplen) { 237 tsize = ((hdr->caplen + 1023) / 1024) * 1024; 238 if (tp != NULL) 239 free((u_char *)tp); 240 tp = (u_char *)malloc(tsize); 241 if (tp == NULL) { 242 sprintf(p->errbuf, "BUFMOD hack malloc"); 243 return (-1); 244 } 245 } 246 if (fread((char *)tp, hdr->caplen, 1, fp) != 1) { 247 sprintf(p->errbuf, "truncated dump file"); 248 return (-1); 249 } 250 memcpy((char *)buf, (char *)tp, buflen); 251 252 } else { 253 /* read the packet itself */ 254 255 if (fread((char *)buf, hdr->caplen, 1, fp) != 1) { 256 sprintf(p->errbuf, "truncated dump file"); 257 return (-1); 258 } 259 } 260 return (0); 261} 262 263/* 264 * Print out packets stored in the file initialized by sf_read_init(). 265 * If cnt > 0, return after 'cnt' packets, otherwise continue until eof. 266 */ 267int 268pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 269{ 270 struct bpf_insn *fcode = p->fcode.bf_insns; 271 int status = 0; 272 int n = 0; 273 274 while (status == 0) { 275 struct pcap_pkthdr h; 276 277 status = sf_next_packet(p, &h, p->buffer, p->bufsize); 278 if (status) { 279 if (status == 1) 280 return (0); 281 return (status); 282 } 283 284 if (fcode == NULL || 285 bpf_filter(fcode, p->buffer, h.len, h.caplen)) { 286 (*callback)(user, &h, p->buffer); 287 if (++n >= cnt && cnt > 0) 288 break; 289 } 290 } 291 /*XXX this breaks semantics tcpslice expects */ 292 return (n); 293} 294 295/* 296 * Output a packet to the initialized dump file. 297 */ 298void 299pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 300{ 301 register FILE *f; 302 303 f = (FILE *)user; 304 /* XXX we should check the return status */ 305 (void)fwrite((char *)h, sizeof(*h), 1, f); 306 (void)fwrite((char *)sp, h->caplen, 1, f); 307} 308 309/* 310 * Initialize so that sf_write() will output to the file named 'fname'. 311 */ 312pcap_dumper_t * 313pcap_dump_open(pcap_t *p, char *fname) 314{ 315 FILE *f; 316 if (fname[0] == '-' && fname[1] == '\0') 317 f = stdout; 318 else { 319 f = fopen(fname, "w"); 320 if (f == NULL) { 321 sprintf(p->errbuf, "%s: %s", 322 fname, pcap_strerror(errno)); 323 return (NULL); 324 } 325 } 326 (void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot); 327 return ((pcap_dumper_t *)f); 328} 329 330void 331pcap_dump_close(pcap_dumper_t *p) 332{ 333 334#ifdef notyet 335 if (ferror((FILE *)p)) 336 return-an-error; 337 /* XXX should check return from fclose() too */ 338#endif 339 (void)fclose((FILE *)p); 340} 341