ftp.nice.ch/pub/next/unix/security/tcpdump.3.0.s.tar.gz#/tcpdump/libpcap-0.0/savefile.c

This is savefile.c in view mode; [Download] [Up]

/*
 * Copyright (c) 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by the University of California,
 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
 * the University nor the names of its contributors may be used to endorse
 * or promote products derived from this software without specific prior
 * written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
#ifndef lint
static char rcsid[] =
    "@(#)$Header: savefile.c,v 1.16 94/06/20 19:07:56 leres Exp $ (LBL)";
#endif

/*
 * savefile.c - supports offline use of tcpdump
 *	Extraction/creation by Jeffrey Mogul, DECWRL
 *	Modified by Steve McCanne, LBL.
 *
 * Used to save the received packet headers, after filtering, to
 * a file, and then read them later.
 * The first record in the file contains saved values for the machine
 * dependent values so we can print the dump file on any architecture.
 */

#include <sys/types.h>
#include <sys/time.h>
#ifdef SJP_MODS
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#endif
#include <net/bpf.h>


#include <errno.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "pcap-int.h"

#define TCPDUMP_MAGIC 0xa1b2c3d4

/*
 * We use the "receiver-makes-right" approach to byte order,
 * because time is at a premium when we are writing the file.
 * In other words, the pcap_file_header and pcap_pkthdr,
 * records are written in host byte order.
 * Note that the packets are always written in network byte order.
 *
 * ntoh[ls] aren't sufficient because we might need to swap on a big-endian
 * machine (if the file was written in little-end order).
 */
#define	SWAPLONG(y) \
((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
#define	SWAPSHORT(y) \
	( (((y)&0xff)<<8) | (((y)&0xff00)>>8) )

#define SFERR_TRUNC		1
#define SFERR_BADVERSION	2
#define SFERR_BADF		3
#define SFERR_EOF		4 /* not really an error, just a status */

#ifdef SJP_MODS
/*
 * We must snapshot original pppstat values and take
 * the difference to get valid readings.
 */
struct ifpppstatsreq pre_stats;
struct ifpppcstatsreq pre_comp_stats;
int stats_sock;
#define SIOCGPPPSTATS	_IOWR('i', 123, struct ifpppstatsreq)
#define SIOCGPPPCSTATS	_IOWR('i', 122, struct ifpppcstatsreq)

static inline char *strdup(char *str)
{
  char *tmp;

  if (str == NULL)
    return NULL;
  
  if ((tmp = (char *) malloc(strlen(str) + 1)) == NULL)
    {
      fprintf(stderr, "Out of memory at %s %d\n", __FILE__, __LINE__);
      return NULL;
    }

  return strcpy(tmp, str);
}


#endif


static int
sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
{
	struct pcap_file_header hdr;

#ifdef SJP_MODS
	int iodes[2];
	char sysconf[4096];
	char *email_name = NULL;
	char *modem_speed = NULL;
	time_t thetime;
#endif

#ifdef SJP_MODS
	bzero((char *) &hdr, sizeof (struct pcap_file_header));
#endif

	hdr.magic = TCPDUMP_MAGIC;
	hdr.version_major = PCAP_VERSION_MAJOR;
#ifndef SJP_MODS
	hdr.version_minor = PCAP_VERSION_MINOR;
#else
	hdr.version_minor = SJP_PCAP_VERSION_MINOR;
#endif

	hdr.thiszone = thiszone;
	hdr.snaplen = snaplen;
	hdr.sigfigs = 0;
	hdr.linktype = linktype;

#ifdef SJP_MODS

	if (pipe(iodes) == -1)
	  {
	    perror("Error Creating Pipes: ");
	    exit(1);
	  }

	if (fork() == 0)
	  {
	    /*
	     * child
	     */
	    dup2(iodes[1], 1);
	    if (execl("/usr/bin/hostinfo", "hostinfo", 0) == -1)
	      {
		perror("Error getting hostinfo: ");
		close(iodes[1]);
		exit(1);
	      }
	  }
	else
	  { /*
	     * parent
	     */
	    read(iodes[0], sysconf, 4096);
	    sysconf[4095] = (char) NULL;
	    close(iodes[0], iodes[1]);
	  }
	       
	hdr.hinfo_len = strlen(sysconf);
	
	email_name = getenv("PPPMAILTO");
	if (email_name)
	    hdr.email_len = strlen(email_name);
	else
	  {
	    fprintf(stderr, "Please define the environment variable PPPMAILTO\n");
	    hdr.email_len = 0;
	  }

	modem_speed = getenv("PPPMODEMSPEED");
	if (modem_speed)
	  {
	    hdr.m_speed = atoi(modem_speed);
	    fprintf(stderr, "Setting modem speed to %d\n", hdr.m_speed);
	  }
	else
	  {
	    fprintf(stderr, "Please define the environment variable PPPMODEMSPEED\n");
	    hdr.m_speed = 0;
	  }

	thetime = time(NULL);
	strncpy(hdr.date, ctime(&thetime), 26);

	if (gettimeofday(&hdr.session_start, NULL) == -1)
	  perror("Setting time in libpcap/savefile.c");

#endif

	if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
		return (-1);

#ifdef SJP_MODS
	if (fwrite(sysconf, hdr.hinfo_len, 1, fp) != 1)
	  return (-1);

	if (hdr.email_len != 0)
	  if (fwrite(email_name, hdr.email_len, 1, fp) != 1)
	    return (-1);

#endif
	return (0);
}

static void
swap_hdr(struct pcap_file_header *hp)
{
  fprintf(stderr, "Inside swap_hdr %s %d\n", __FILE__, __LINE__);

	hp->version_major = SWAPSHORT(hp->version_major);
	hp->version_minor = SWAPSHORT(hp->version_minor);
	hp->thiszone = SWAPLONG(hp->thiszone);
	hp->sigfigs = SWAPLONG(hp->sigfigs);
	hp->snaplen = SWAPLONG(hp->snaplen);
	hp->linktype = SWAPLONG(hp->linktype);

#ifdef SJP_MODS
  hp->session_start.tv_sec = SWAPLONG(hp->session_start.tv_sec);
  hp->session_start.tv_usec = SWAPLONG(hp->session_start.tv_usec);
  hp->session_end.tv_sec = SWAPLONG(hp->session_end.tv_sec);
  hp->session_end.tv_usec = SWAPLONG(hp->session_end.tv_usec);
  hp->m_speed = SWAPLONG(hp->m_speed);
  hp->ps_recv = SWAPLONG(hp->ps_recv);
  hp->ps_drop = SWAPLONG(hp->ps_drop);
#endif

}

pcap_t *
pcap_open_offline(char *fname, char *errbuf)
{
	register pcap_t *p;
	register FILE *fp;
	struct pcap_file_header hdr;
	int linklen;

	p = (pcap_t *)malloc(sizeof(*p));
	if (p == NULL) {
		strcpy(errbuf, "out of swap");
		return (NULL);
	}

#ifdef notdef
	bzero(p, sizeof(*p));
#else
	memset(p, 0, sizeof(*p));
#endif
	/*
	 * Set this field so we don't close stdin in pcap_close!
	 */
	p->fd = -1;

	if (fname[0] == '-' && fname[1] == '\0')
		fp = stdin;
	else {
		fp = fopen(fname, "r");
		if (fp == NULL) {
			sprintf(errbuf, "%s: %s", fname, pcap_strerror(errno));
			goto bad;
		}
	}
	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
		sprintf(errbuf, "fread: %s", pcap_strerror(errno));
		goto bad;
	}
	if (hdr.magic != TCPDUMP_MAGIC) {
		if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) {
			sprintf(errbuf, "bad dump file format");
			goto bad;
		}
		p->sf.swapped = 1;
		swap_hdr(&hdr);
	}
	if (hdr.version_major < PCAP_VERSION_MAJOR) {
		sprintf(errbuf, "archaic file format");
		goto bad;
	}

	if (hdr.version_minor == PCAP_VERSION_MINOR)
	  {
	    p->tzoff = hdr.thiszone;
	    p->snapshot = hdr.snaplen;
	    p->linktype = hdr.linktype;
	    p->sf.rfile = fp;
	    p->bufsize = hdr.snaplen;
	    /* Align link header as required for proper data alignment */
	    linklen = 14;					/* XXX */
	    p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT);
	    p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
	    p->sf.version_major = hdr.version_major;
	    p->sf.version_minor = hdr.version_minor;
	  }
#ifdef SJP_MODS
	else if (hdr.version_minor == SJP_PCAP_VERSION_MINOR)
	  {
	    struct timeval tmp;
	    char hostinfobuf[1000],
	         email_buf[100];

/* 	    fprintf(stderr, "Reading a Modified TCPDUMP file\n"); */
	    p->tzoff = hdr.thiszone;
	    p->snapshot = hdr.snaplen;
	    p->linktype = hdr.linktype;
	    p->sf.rfile = fp;
	    p->bufsize = hdr.snaplen;
	    /* Align link header as required for proper data alignment */
	    linklen = 14;					/* XXX */
	    p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT);
	    p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
	    p->sf.version_major = hdr.version_major;
	    p->sf.version_minor = hdr.version_minor;


	    if (fread(hostinfobuf, hdr.hinfo_len, 1, fp) != 1)
	      {
		sprintf(errbuf, "Error reading Hostinfo: ");
		goto bad;
	      }
	    hostinfobuf[hdr.hinfo_len] = (char) NULL;
	    hdr.hinfo = strdup(hostinfobuf);

	    if(hdr.email_len != 0)
	      {
		if (fread(email_buf, hdr.email_len, 1, fp) != 1)
		  {
		    sprintf(errbuf, "Error reading Mail address: ");
		    goto bad;
		  }
	      }
	    email_buf[hdr.email_len] = (char) NULL;
	    hdr.email_addr = strdup(email_buf);

	    p->sf.hdr = (struct pcap_file_header *) malloc(sizeof(struct pcap_file_header));
	    if (p->sf.hdr == NULL)
	      {
		sprintf(errbuf, "Out of memory");
		return NULL;
	      }

	    bcopy(&hdr, p->sf.hdr, sizeof(struct pcap_file_header));
	    
	    
#if 0
	    printf("Dump taken on %s  by  %s\n\n", hdr.date, email_buf);
	    
	    printf("Dump started at: ");
	    ts_print_copy(&hdr.session_start, 1);
	    printf("\nDump ended at: ");
	    ts_print_copy(&hdr.session_end, 1);
	    tmp.tv_sec = hdr.session_end.tv_sec - hdr.session_start.tv_sec;
	    tmp.tv_usec = 0;
	    printf("\nDump Duration: ");
	    sjpts_print(&tmp, 1);
	    printf("\n\n");

	    printf("Session Stats:\n");
	    printf("%6.6s %6.6s %6.6s %6.6s %6.6s %6.6s\n",
		   "in", "pack", "comp", "uncomp", "err", "toss");
#define PIN(val) hdr.stats.p.val
#define PVIN(val) hdr.stats.vj.val
	    printf("%6d %6d %6d %6d %6d %6d\n\n",
		   PIN(ppp_ibytes), PIN(ppp_ipackets), PVIN(vjs_compressedin),
		   PVIN(vjs_uncompressedin), PVIN(vjs_errorin), PVIN(vjs_tossed));

	    printf("%6.6s %6.6s %6.6s %6.6s %6.6s %6.6s\n",
		   "out", "pack", "comp", "uncomp", "search", "miss");

	    printf("%6d %6d %6d %6d %6d %6d\n\n",
		   PIN(ppp_obytes), PIN(ppp_opackets), PVIN(vjs_compressed),
		   PVIN(vjs_packets), PVIN(vjs_searches), PVIN(vjs_misses));


	    printf("%s\n", hostinfobuf);
#endif
	  }

#endif /* SJP_MODS */
	else
	  {
	    fprintf(stderr, "Reading an unknown MINOR version (%d) of TCPDUMP file\n",
		   hdr.version_minor);
	    printf("Currently not supported\n");
	  }

	return (p);
 bad:
	free(p);
	return (NULL);
}

/*
 * Read sf_readfile and return the next packet.  Return the header in hdr
 * and the contents in buf.  Return 0 on success, SFERR_EOF if there were
 * no more packets, and SFERR_TRUNC if a partial packet was encountered.
 */
int
sf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen)
{
	FILE *fp = p->sf.rfile;

	/* read the stamp */
	if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) {
		/* probably an EOF, though could be a truncated packet */
		return (1);
	}

	if (p->sf.swapped) {
		/* these were written in opposite byte order */
		hdr->caplen = SWAPLONG(hdr->caplen);
		hdr->len = SWAPLONG(hdr->len);
		hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
		hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
	}
	/*
	 * We interchanged the caplen and len fields at version 2.3,
	 * in order to match the bpf header layout.  But unfortunately
	 * some files were written with version 2.3 in their headers
	 * but without the interchanged fields.
	 */
	if (p->sf.version_minor < 3 ||
	    (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) {
		int t = hdr->caplen;
		hdr->caplen = hdr->len;
		hdr->len = t;
	}

	if (hdr->caplen > buflen) {
		sprintf(p->errbuf, "bad dump file format");
		return (-1);
	}

	/* read the packet itself */

	if (fread((char *)buf, hdr->caplen, 1, fp) != 1) {
		sprintf(p->errbuf, "truncated dump file");
		return (-1);
	}
	return (0);
}

/*
 * Print out packets stored in the file initialized by sf_read_init().
 * If cnt > 0, return after 'cnt' packets, otherwise continue until eof.
 */
int
pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
{
	struct bpf_insn *fcode = p->fcode.bf_insns;
	int status = 0;
	int n = 0;

	while (status == 0) {
		struct pcap_pkthdr h;

		status = sf_next_packet(p, &h, p->buffer, p->bufsize);
		if (status)
			return (-1);

		if (fcode == NULL ||
		    bpf_filter(fcode, p->buffer, h.len, h.caplen)) {
			(*callback)(user, &h, p->buffer);
			if (++n >= cnt && cnt > 0)
				break;
		}
	}
	/*XXX this breaks semantics tcpslice expects */
	return (n);
}

/*
 * Output a packet to the initialized dump file.
 */
void
pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
	FILE * f = (FILE *)user;
	(void)fwrite((char *)h, sizeof(*h), 1, f);
	(void)fwrite((char *)sp, h->caplen, 1, f);
}

/*
 * Initialize so that sf_write() will output to the file named 'fname'.
 */
pcap_dumper_t *
pcap_dump_open(pcap_t *p, char *fname)
{
	FILE *f;
	if (fname[0] == '-' && fname[1] == '\0')
		f = stdout;
	else {
#ifdef SJP_MODS
	  /*
	   * Lets collect PPP stats snapshot
	   */
	  
	  if ((stats_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	    {
	      perror("couldn't create IP socket");
	      exit(1);
	    }

	  bzero((char *)&pre_stats, sizeof(pre_stats));
	  bzero((char *)&pre_comp_stats, sizeof(pre_comp_stats));

	  sprintf(pre_stats.ifr_name, "ppp%d", 0);   /* This needs to be fixed  +++ */
	  sprintf(pre_comp_stats.ifr_name, "ppp%d", 0);

	  if (ioctl(stats_sock, SIOCGPPPSTATS, &pre_stats) < 0) {
	    if (errno == ENOTTY)
	      fprintf(stderr, "pppstats: kernel support missing\n");
	    else
	      perror("ioctl(SIOCGPPPSTATS)");
	    exit(1);
	  }
	  if (ioctl(stats_sock, SIOCGPPPCSTATS, &pre_comp_stats) < 0) {
	    if (errno == ENOTTY)
	      fprintf(stderr, "pppcstats: kernel compression support missing\n");
	    else
	      perror("ioctl(SIOCGPPPCSTATS)");
	    exit(1);
	  }


	  f = fopen(fname, "w+");
#else
		f = fopen(fname, "w");
#endif
		if (f == NULL) {
			sprintf(p->errbuf, "%s: %s",
			    fname, pcap_strerror(errno));
			return (NULL);
		}
	}
	(void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot);
	return ((pcap_dumper_t *)f);
}

void
pcap_dump_close(pcap_dumper_t *p, pcap_t *pd)
{
#ifdef SJP_MODS

  FILE *file = (FILE *) p;
  struct pcap_file_header hdr;
  struct ifpppstatsreq stats;
  struct ifpppcstatsreq comp_stats;
  struct pcap_stat stat;

  rewind(file);
  fread((void *)&hdr, sizeof(struct pcap_file_header), 1, file);
  if (gettimeofday(&hdr.session_end, NULL) == -1)
    perror("Setting end time in libpcap/savefile.c");

  /*
   * Set the session stats as the difference from the original
   * snapshot.
   */

  bzero((char *)&stats, sizeof(stats));
  bzero((char *)&comp_stats, sizeof(comp_stats));
  
  sprintf(stats.ifr_name, "ppp%d", 0);   /* This needs to be fixed  +++ */
  sprintf(comp_stats.ifr_name, "ppp%d", 0);
  
  if (ioctl(stats_sock, SIOCGPPPSTATS, &stats) < 0) {
    if (errno == ENOTTY)
      fprintf(stderr, "pppstats: kernel support missing\n");
    else
      perror("ioctl(SIOCGPPPSTATS)");
    exit(1);
  }
  if (ioctl(stats_sock, SIOCGPPPCSTATS, &comp_stats) < 0) {
    if (errno == ENOTTY)
      fprintf(stderr, "pppcstats: kernel compression support missing\n");
    else
      perror("ioctl(SIOCGPPPCSTATS)");
    exit(1);
  }

#define STATS_DIFF(val) hdr.stats.p.val = stats.stats.p.val - pre_stats.stats.p.val
  STATS_DIFF(ppp_ibytes);
  STATS_DIFF(ppp_ipackets);
  STATS_DIFF(ppp_ierrors);
  STATS_DIFF(ppp_obytes);
  STATS_DIFF(ppp_opackets);
  STATS_DIFF(ppp_oerrors);

#define VJ_DIFF(val) hdr.stats.vj.val = stats.stats.vj.val - pre_stats.stats.vj.val
  VJ_DIFF(vjs_packets);
  VJ_DIFF(vjs_compressed);
  VJ_DIFF(vjs_searches);
  VJ_DIFF(vjs_misses);
  VJ_DIFF(vjs_uncompressedin);
  VJ_DIFF(vjs_compressedin);
  VJ_DIFF(vjs_errorin);
  VJ_DIFF(vjs_tossed);

#define RECV_COMP_DIFF(val) hdr.comp_stats.c.val = comp_stats.stats.c.val - pre_comp_stats.stats.c.val
  RECV_COMP_DIFF(unc_bytes);
  RECV_COMP_DIFF(unc_packets);
  RECV_COMP_DIFF(comp_bytes);
  RECV_COMP_DIFF(comp_packets);
  RECV_COMP_DIFF(inc_bytes);
  RECV_COMP_DIFF(inc_packets);
  RECV_COMP_DIFF(ratio);

#define SEND_COMP_DIFF(val) hdr.comp_stats.d.val = comp_stats.stats.d.val - pre_comp_stats.stats.d.val
  SEND_COMP_DIFF(unc_bytes);
  SEND_COMP_DIFF(unc_packets);
  SEND_COMP_DIFF(comp_bytes);
  SEND_COMP_DIFF(comp_packets);
  SEND_COMP_DIFF(inc_bytes);
  SEND_COMP_DIFF(inc_packets);
  SEND_COMP_DIFF(ratio);


  if (pcap_stats(pd, &stat) < 0)
    (void)fprintf(stderr, "pcap_stats: Error getting stats\n");
  else {
    hdr.ps_recv = stat.ps_recv;
    hdr.ps_drop = stat.ps_drop;
  }


  /*
   * Now write out the new header
   */

  rewind(file);
  fwrite((void *)&hdr, sizeof(struct pcap_file_header), 1, file);


  /*
   * There is some garbage being generated here...
   * The hdr stored in the pcap dynamically allocated
   * the hinfo and email address.  We don't get passed
   * the pcap_t to clean up with... *sigh*.
   */

#endif  
  fclose((FILE *)p);

}

#ifdef SJP_MODS
void
sjpts_print(register const struct timeval *tvp, int tflag)
{
	register int s;

	if (tflag > 0) {
		/* Default */
		s = (tvp->tv_sec) % 86400;
		(void)printf("%02d:%02d:%02d",
		    s / 3600, (s % 3600) / 60, s % 60);
	} else if (tflag < 0) {
		/* Unix timeval style */
		(void)printf("%d.%06d ", tvp->tv_sec, tvp->tv_usec);
	}
}

#endif

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.