Ethereal-dev: Re: [ethereal-dev] Security race in ethereal leading to root access

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Guy Harris <gharris@xxxxxxxxxxxx>
Date: Sun, 15 Aug 1999 23:30:52 -0700
> Well, I have some code to do that...
> 
> ...unfortunately, it works only if the process doing the capture is the
> same process as the process that calls "mkstemp()" to create the capture
> file...
> 
> ...and that's not the case if you run Ethereal with "-F" or "-S".
> 
> One possible fix would be to pass a file descriptor number, rather than
> a file name, in the arguments to the instance of Ethereal doing the
> capture.  This is probably a relatively simple change, but, unless the
> ability to write the capture to a specified file descriptor is useful if
> you're *not* a child Ethereal run in a "-F" or "-S" capture, it kludges
> up the command-line interface a bit, and in any case it kludges up the
> implementation a bit.

Not as much as I'd feared.

Long term, we might want to consider some the fancier solution I
suggested, but, in the short term, passing the file descriptor number
seemed to work with "-F" and "-S".

It also adds to Wiretap the ability to write out capture files; the API
I put in lets you specify the format in which to write the file out,
which could come in handy for the "save in formats other than 'libpcap'"
feature somebody asked for.  (That's why I split up the capture file
formats for NetXRay and NetMon - when writing the file out, you might
want to select an older format, if you have nothing that reads the newer
format, or a newer format.)

Here's a patch for that; absent any objections, I'll check it in.

Index: capture.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/capture.c,v
retrieving revision 1.52
diff -c -r1.52 capture.c
*** capture.c	1999/08/15 22:31:22	1.52
--- capture.c	1999/08/16 06:25:33
***************
*** 309,316 ****
  static void
  capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
    GtkWidget *if_cb, *filter_te, *count_cb, *snap_sb;
- 
    gchar *filter_text;
  
    if_cb     = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
    filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
--- 309,317 ----
  static void
  capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
    GtkWidget *if_cb, *filter_te, *count_cb, *snap_sb;
    gchar *filter_text;
+   char *tmpdir;
+   static const char tmpname[] = "/ether.XXXXXX";
  
    if_cb     = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
    filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
***************
*** 341,363 ****
  	unlink(cf.save_file); /* silently ignore error */
  	g_free(cf.save_file);
    }
!   cf.save_file = tempnam(NULL, "ether");
    cf.user_saved = 0;
    
    if( fork_mode ){	/*  use fork() for capture */
      int  fork_child;
      char ssnap[24];
      char scount[24];	/* need a constant for len of numbers */
      int err;
  
!     sprintf(ssnap,"%d",cf.snap); /* in liu of itoa */
      sprintf(scount,"%d",cf.count);
      signal(SIGCHLD, SIG_IGN);
      if (sync_mode) pipe(sync_pipe);
      if((fork_child = fork()) == 0){
        /* args: -k -- capture
         * -i interface specification
         * -w file to write
         * -c count to capture
         * -Q quit after capture (forces -k)
         * -s snaplen
--- 342,373 ----
  	unlink(cf.save_file); /* silently ignore error */
  	g_free(cf.save_file);
    }
!   tmpdir = getenv("TMPDIR");
!   if (tmpdir == NULL)
!     tmpdir = "/var/tmp";
!   cf.save_file = malloc(strlen(tmpdir) + sizeof tmpname);
!   strcpy(cf.save_file, tmpdir);
!   strcat(cf.save_file, tmpname);
!   cf.save_file_fd = mkstemp(cf.save_file);
    cf.user_saved = 0;
    
    if( fork_mode ){	/*  use fork() for capture */
      int  fork_child;
      char ssnap[24];
      char scount[24];	/* need a constant for len of numbers */
+     char save_file_fd[24];
      int err;
  
!     sprintf(ssnap,"%d",cf.snap); /* in lieu of itoa */
      sprintf(scount,"%d",cf.count);
+     sprintf(save_file_fd,"%d",cf.save_file_fd);
      signal(SIGCHLD, SIG_IGN);
      if (sync_mode) pipe(sync_pipe);
      if((fork_child = fork()) == 0){
        /* args: -k -- capture
         * -i interface specification
         * -w file to write
+        * -W file descriptor to write
         * -c count to capture
         * -Q quit after capture (forces -k)
         * -s snaplen
***************
*** 369,386 ****
  	 close(1);
  	 dup(sync_pipe[1]);
  	 close(sync_pipe[0]);
! 	 execlp(ethereal_path,"ethereal","-k","-Q","-i",cf.iface,"-w",cf.save_file,
  		"-c", scount, "-s", ssnap, "-S", 
  		"-m", medium_font, "-b", bold_font,
! 		(cf.cfilter == NULL)? 0 : "-f", (cf.cfilter == NULL)? 0 : cf.cfilter, 
! 		0);	
         }
         else {
! 	 execlp(ethereal_path,"ethereal","-k","-Q","-i",cf.iface,"-w",cf.save_file,
  		"-c", scount, "-s", ssnap,
  		"-m", medium_font, "-b", bold_font,
! 		(cf.cfilter == NULL)? 0 : "-f", (cf.cfilter == NULL)? 0 : cf.cfilter,
! 		0);
         }
      }
      else {
--- 379,400 ----
  	 close(1);
  	 dup(sync_pipe[1]);
  	 close(sync_pipe[0]);
! 	 execlp(ethereal_path, "ethereal", "-k", "-Q", "-i", cf.iface,
! 		"-w", cf.save_file, "-W", save_file_fd,
  		"-c", scount, "-s", ssnap, "-S", 
  		"-m", medium_font, "-b", bold_font,
! 		(cf.cfilter == NULL)? 0 : "-f",
! 		(cf.cfilter == NULL)? 0 : cf.cfilter,
! 		(const char *)NULL);	
         }
         else {
! 	 execlp(ethereal_path, "ethereal", "-k", "-Q", "-i", cf.iface,
! 		"-w", cf.save_file, "-W", save_file_fd,
  		"-c", scount, "-s", ssnap,
  		"-m", medium_font, "-b", bold_font,
! 		(cf.cfilter == NULL)? 0 : "-f",
! 		(cf.cfilter == NULL)? 0 : cf.cfilter,
! 		(const char *)NULL);
         }
      }
      else {
***************
*** 419,427 ****
    gint           go;
    gint           max;
    gint           linktype;
    gint           sync_packets;
    packet_counts  counts;
!   pcap_dumper_t *pdh;
  } loop_data;
  
  void
--- 433,442 ----
    gint           go;
    gint           max;
    gint           linktype;
+   gint           wtap_linktype;
    gint           sync_packets;
    packet_counts  counts;
!   wtap_dumper   *pdh;
  } loop_data;
  
  void
***************
*** 434,439 ****
--- 449,456 ----
    bpf_u_int32 netnum, netmask;
    time_t      upd_time, cur_time;
    int         err, inpkts;
+   char       *errmsg;
+   char        errmsg_errno[1024+1];
  
    ld.go             = TRUE;
    ld.counts.total   = 0;
***************
*** 454,495 ****
    pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
  
    if (pch) {
!     /* save the old new umask and set the new one to readable only by the user */
!     mode_t old_umask = umask(0066);
! 
!     /* Have libpcap create the empty dumpfile */
!     ld.pdh = pcap_dump_open(pch, cf.save_file);
! 
!     /* reset the umask to the original value */
!     (void) umask(old_umask); 
  
      if (ld.pdh == NULL) {  /* We have an error */
!       snprintf(err_str, PCAP_ERRBUF_SIZE, "Error trying to save capture to "
!         "file:\n%s", pcap_geterr(pch));
        simple_dialog(ESD_TYPE_WARN, NULL, err_str);
        pcap_close(pch);
        return;
      }
  
-     ld.linktype = pcap_datalink(pch);
- 
      if (cf.cfilter) {
        if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
          simple_dialog(ESD_TYPE_WARN, NULL,
            "Can't use filter:  Couldn't obtain netmask info.");
!         pcap_dump_close(ld.pdh);
          unlink(cf.save_file); /* silently ignore error */
          pcap_close(pch);
          return;
        } else if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
          simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
!         pcap_dump_close(ld.pdh);
          unlink(cf.save_file); /* silently ignore error */
          pcap_close(pch);
          return;
        } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
          simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
!         pcap_dump_close(ld.pdh);
          unlink(cf.save_file); /* silently ignore error */
          pcap_close(pch);
          return;
--- 471,530 ----
    pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
  
    if (pch) {
!     ld.linktype = pcap_datalink(pch);
!     ld.wtap_linktype = wtap_pcap_encap_to_wtap_encap(ld.linktype);
!     ld.pdh = wtap_dump_fdopen(cf.save_file_fd, WTAP_FILE_PCAP,
! 		ld.wtap_linktype, pcap_snapshot(pch), &err);
  
      if (ld.pdh == NULL) {  /* We have an error */
!       switch (err) {
! 
!       case WTAP_ERR_CANT_OPEN:
!         errmsg = "The file to which the capture would be saved"
!                  " couldn't be created for some unknown reason.";
!         break;
! 
!       case WTAP_ERR_SHORT_WRITE:
!         errmsg = "A full header couldn't be written to the file"
!                  " to which the capture would be saved.";
!         break;
! 
!       default:
!         if (err < 0) {
!           sprintf(errmsg_errno, "The file to which the capture would be"
! 	                      " saved (\"%%s\") could not be opened: Error %d.",
! 	  			err);
!         } else {
!           sprintf(errmsg_errno, "The file to which the capture would be"
! 	                      " saved (\"%%s\") could not be opened: %s.",
! 	  			strerror(err));
! 	}
! 	errmsg = errmsg_errno;
! 	break;
!       }
!       snprintf(err_str, PCAP_ERRBUF_SIZE, errmsg, cf.save_file);
        simple_dialog(ESD_TYPE_WARN, NULL, err_str);
        pcap_close(pch);
        return;
      }
  
      if (cf.cfilter) {
        if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
          simple_dialog(ESD_TYPE_WARN, NULL,
            "Can't use filter:  Couldn't obtain netmask info.");
!         wtap_dump_close(ld.pdh);
          unlink(cf.save_file); /* silently ignore error */
          pcap_close(pch);
          return;
        } else if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
          simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
!         wtap_dump_close(ld.pdh);
          unlink(cf.save_file); /* silently ignore error */
          pcap_close(pch);
          return;
        } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
          simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
!         wtap_dump_close(ld.pdh);
          unlink(cf.save_file); /* silently ignore error */
          pcap_close(pch);
          return;
***************
*** 501,507 ****
           system, and signal our parent so that they'll open the capture
  	 file and update its windows to indicate that we have a live
  	 capture in progress. */
!       fflush((FILE *)ld.pdh);
        kill(getppid(), SIGUSR2);
      }
  
--- 536,542 ----
           system, and signal our parent so that they'll open the capture
  	 file and update its windows to indicate that we have a live
  	 capture in progress. */
!       fflush(wtap_dump_file(ld.pdh));
        kill(getppid(), SIGUSR2);
      }
  
***************
*** 603,609 ****
          gtk_label_set(GTK_LABEL(other_lb), label_str);
  
  	/* do sync here, too */
! 	fflush((FILE *)ld.pdh);
  	if (sync_mode && ld.sync_packets) {
  	  char tmp[20];
  	  sprintf(tmp, "%d*", ld.sync_packets);
--- 638,644 ----
          gtk_label_set(GTK_LABEL(other_lb), label_str);
  
  	/* do sync here, too */
! 	fflush(wtap_dump_file(ld.pdh));
  	if (sync_mode && ld.sync_packets) {
  	  char tmp[20];
  	  sprintf(tmp, "%d*", ld.sync_packets);
***************
*** 613,619 ****
        }
      }
      
!     if (ld.pdh) pcap_dump_close(ld.pdh);
      pcap_close(pch);
  
      gtk_grab_remove(GTK_WIDGET(cap_w));
--- 648,654 ----
        }
      }
      
!     if (ld.pdh) wtap_dump_close(ld.pdh);
      pcap_close(pch);
  
      gtk_grab_remove(GTK_WIDGET(cap_w));
***************
*** 664,678 ****
  static void
  capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
    const u_char *pd) {
!   
    loop_data *ld = (loop_data *) user;
    
    if ((++ld->counts.total >= ld->max) && (ld->max > 0)) 
    {
       ld->go = FALSE;
    }
-   /* Currently, pcap_dumper_t is a FILE *.  Let's hope that doesn't change. */
-   if (ld->pdh) pcap_dump((u_char *) ld->pdh, phdr, pd);
      
    switch (ld->linktype) {
      case DLT_EN10MB :
--- 699,721 ----
  static void
  capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
    const u_char *pd) {
!   struct wtap_pkthdr whdr;
! 
    loop_data *ld = (loop_data *) user;
    
    if ((++ld->counts.total >= ld->max) && (ld->max > 0)) 
    {
       ld->go = FALSE;
+   }
+   if (ld->pdh) {
+      whdr.ts = phdr->ts;
+      whdr.caplen = phdr->caplen;
+      whdr.len = phdr->len;
+      whdr.pkt_encap = ld->wtap_linktype;
+ 
+      /* XXX - check for errors */
+      wtap_dump(ld->pdh, &whdr, pd);
    }
      
    switch (ld->linktype) {
      case DLT_EN10MB :
Index: ethereal.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/ethereal.c,v
retrieving revision 1.90
diff -c -r1.90 ethereal.c
*** ethereal.c	1999/08/15 19:18:44	1.90
--- ethereal.c	1999/08/16 06:25:39
***************
*** 1094,1099 ****
--- 1094,1100 ----
  #endif
    cf.iface		= NULL;
    cf.save_file		= NULL;
+   cf.save_file_fd	= -1;
    cf.user_saved		= 0;
    cf.snap		= MAX_PACKET_SIZE;
    cf.count		= 0;
***************
*** 1121,1127 ****
  
  #ifndef WIN32
    /* Now get our args */
!   while ((opt = getopt(argc, argv, "b:B:c:f:Fhi:km:nP:Qr:R:Ss:t:T:w:v")) != EOF) {
      switch (opt) {
        case 'b':	       /* Bold font */
  	bold_font = g_strdup(optarg);
--- 1122,1128 ----
  
  #ifndef WIN32
    /* Now get our args */
!   while ((opt = getopt(argc, argv, "b:B:c:f:Fhi:km:nP:Qr:R:Ss:t:T:w:W:v")) != EOF) {
      switch (opt) {
        case 'b':	       /* Bold font */
  	bold_font = g_strdup(optarg);
***************
*** 1205,1213 ****
          exit(0);
          break;
  #ifdef HAVE_LIBPCAP
!       case 'w':        /* Write capture file xxx */
          cf.save_file = g_strdup(optarg);
  	break;
  #endif
      }
    }
--- 1206,1217 ----
          exit(0);
          break;
  #ifdef HAVE_LIBPCAP
!       case 'w':        /* Write to capture file xxx */
          cf.save_file = g_strdup(optarg);
  	break;
+       case 'W':        /* Write to capture file FD xxx */
+         cf.save_file_fd = atoi(optarg);
+ 	break;
  #endif
      }
    }
***************
*** 1221,1226 ****
--- 1225,1237 ----
      if (cf.save_file == NULL) {
        fprintf(stderr, "ethereal: \"-k\" flag was specified without \"-w\" flag\n");
        exit(1);
+     }
+     if (fork_mode) {
+       if (cf.save_file_fd == -1) {
+         fprintf(stderr, "ethereal: \"-k\" flag was specified with \"-%c\" flag but without \"-W\" flag\n",
+             (sync_mode ? 'S' : 'F'));
+         exit(1);
+       }
      }
    }
  
Index: file.h
===================================================================
RCS file: /usr/local/cvsroot/ethereal/file.h,v
retrieving revision 1.36
diff -c -r1.36 file.h
*** file.h	1999/08/15 19:18:46	1.36
--- file.h	1999/08/16 06:25:39
***************
*** 65,70 ****
--- 65,71 ----
    guint32      snap;      /* Captured packet length */
    gchar       *iface;     /* Interface */
    gchar       *save_file; /* File that user saved capture to */
+   int          save_file_fd; /* File descriptor for saved file */
    gint         user_saved;/* Was capture file saved by user yet? */
    wtap        *wth;       /* Wiretap session */
    dfilter     *rfcode;    /* Compiled read filter program */ 
Index: wiretap/libpcap.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/libpcap.c,v
retrieving revision 1.6
diff -c -r1.6 libpcap.c
*** libpcap.c	1999/07/28 23:16:42	1.6
--- libpcap.c	1999/08/16 06:25:40
***************
*** 23,28 ****
--- 23,30 ----
  #ifdef HAVE_CONFIG_H
  #include "config.h"
  #endif
+ #include <stdlib.h>
+ #include <errno.h>
  #include "wtap.h"
  #include "buffer.h"
  #include "libpcap.h"
***************
*** 64,102 ****
  struct pcaprec_hdr {
  	guint32	ts_sec;		/* timestamp seconds */
  	guint32	ts_usec;	/* timestamp microseconds */
! 	guint32	incl_len;	/* number of octets captured in file */
  	guint32	orig_len;	/* actual length of packet */
  };
  
  /* Returns WTAP_FILE_PCAP on success, WTAP_FILE_UNKNOWN on failure */
  int libpcap_open(wtap *wth)
  {
  	int bytes_read;
  	guint32 magic;
  	struct pcap_hdr hdr;
- 	static const int pcap_encap[] = {
- 		WTAP_ENCAP_NONE,	/* no encapsulation */
- 		WTAP_ENCAP_ETHERNET,
- 		WTAP_ENCAP_NONE,	/* 3Mb experimental Ethernet */
- 		WTAP_ENCAP_NONE,	/* Amateur Radio AX.25 */
- 		WTAP_ENCAP_NONE,	/* Proteon ProNET Token Ring */
- 		WTAP_ENCAP_NONE,	/* Chaos */
- 		WTAP_ENCAP_TR,		/* IEEE 802 Networks - assume token ring */
- 		WTAP_ENCAP_ARCNET,
- 		WTAP_ENCAP_SLIP,
- 		WTAP_ENCAP_PPP,
- 		WTAP_ENCAP_FDDI,
- 		WTAP_ENCAP_ATM_RFC1483,
- 		WTAP_ENCAP_RAW_IP,
- 		WTAP_ENCAP_NONE,
- 		WTAP_ENCAP_NONE,
- 		WTAP_ENCAP_NONE,
- 		WTAP_ENCAP_NONE,
- 		WTAP_ENCAP_NONE,
- 		WTAP_ENCAP_NONE,
- 		WTAP_ENCAP_LINUX_ATM_CLIP
- 	};
- 	#define NUM_PCAP_ENCAPS (sizeof pcap_encap / sizeof pcap_encap[0])
  	int byte_swapped = 0;
  
  	/* Read in the number that should be at the start of a "libpcap" file */
--- 66,110 ----
  struct pcaprec_hdr {
  	guint32	ts_sec;		/* timestamp seconds */
  	guint32	ts_usec;	/* timestamp microseconds */
! 	guint32	incl_len;	/* number of octets of packet saved in file */
  	guint32	orig_len;	/* actual length of packet */
  };
  
+ static int libpcap_read(wtap *wth);
+ static int libpcap_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
+     const u_char *pd);
+ static int libpcap_dump_close(wtap_dumper *wdh);
+ 
+ static const int pcap_encap[] = {
+ 	WTAP_ENCAP_NONE,	/* no encapsulation */
+ 	WTAP_ENCAP_ETHERNET,
+ 	WTAP_ENCAP_NONE,	/* 3Mb experimental Ethernet */
+ 	WTAP_ENCAP_NONE,	/* Amateur Radio AX.25 */
+ 	WTAP_ENCAP_NONE,	/* Proteon ProNET Token Ring */
+ 	WTAP_ENCAP_NONE,	/* Chaos */
+ 	WTAP_ENCAP_TR,		/* IEEE 802 Networks - assume token ring */
+ 	WTAP_ENCAP_ARCNET,
+ 	WTAP_ENCAP_SLIP,
+ 	WTAP_ENCAP_PPP,
+ 	WTAP_ENCAP_FDDI,
+ 	WTAP_ENCAP_ATM_RFC1483,
+ 	WTAP_ENCAP_RAW_IP,
+ 	WTAP_ENCAP_NONE,
+ 	WTAP_ENCAP_NONE,
+ 	WTAP_ENCAP_NONE,
+ 	WTAP_ENCAP_NONE,
+ 	WTAP_ENCAP_NONE,
+ 	WTAP_ENCAP_NONE,
+ 	WTAP_ENCAP_LINUX_ATM_CLIP
+ };
+ #define NUM_PCAP_ENCAPS (sizeof pcap_encap / sizeof pcap_encap[0])
+ 
  /* Returns WTAP_FILE_PCAP on success, WTAP_FILE_UNKNOWN on failure */
  int libpcap_open(wtap *wth)
  {
  	int bytes_read;
  	guint32 magic;
  	struct pcap_hdr hdr;
  	int byte_swapped = 0;
  
  	/* Read in the number that should be at the start of a "libpcap" file */
***************
*** 151,157 ****
  }
  
  /* Read the next packet */
! int libpcap_read(wtap *wth)
  {
  	int	packet_size;
  	int	bytes_read;
--- 159,165 ----
  }
  
  /* Read the next packet */
! static int libpcap_read(wtap *wth)
  {
  	int	packet_size;
  	int	bytes_read;
***************
*** 219,222 ****
--- 227,324 ----
  	wth->phdr.pkt_encap = wth->file_encap;
  
  	return data_offset;
+ }
+ 
+ int wtap_pcap_encap_to_wtap_encap(int encap)
+ {
+ 	if (encap < 0 || encap >= NUM_PCAP_ENCAPS)
+ 		return WTAP_FILE_UNKNOWN;
+ 	return pcap_encap[encap];
+ }
+ 
+ /* Returns 1 on success, 0 on failure; sets "*err" to an error code on
+    failure */
+ int libpcap_dump_open(wtap_dumper *wdh, int *err)
+ {
+ 	static const guint32 pcap_magic = PCAP_MAGIC;
+ 	struct pcap_hdr file_hdr;
+ 	static const int wtap_encap[] = {
+ 		0,		/* WTAP_ENCAP_NONE */
+ 		1,		/* WTAP_ENCAP_ETHERNET */
+ 		6,		/* WTAP_ENCAP_TR */
+ 		8,		/* WTAP_ENCAP_SLIP */
+ 		9,		/* WTAP_ENCAP_PPP */
+ 		10,		/* WTAP_ENCAP_FDDI */
+ 		12,		/* WTAP_ENCAP_RAW_IP */
+ 		7,		/* WTAP_ENCAP_ARCNET */
+ 		11,		/* WTAP_ENCAP_ATM_RFC1483 */
+ 		19		/* WTAP_ENCAP_LINUX_ATM_CLIP */
+ 	};
+ 	int nwritten;
+ 
+ 	/* Per-packet encapsulations aren't supported. */
+ 	if (wdh->encap == WTAP_ENCAP_PER_PACKET) {
+ 		*err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ 		return 0;
+ 	}
+ 
+ 	/* This is a libpcap file */
+ 	wdh->subtype_write = libpcap_dump;
+ 	wdh->subtype_close = libpcap_dump_close;
+ 
+ 	/* Write the file header. */
+ 	nwritten = fwrite(&pcap_magic, 1, sizeof pcap_magic, wdh->fh);
+ 	if (nwritten != sizeof pcap_magic) {
+ 		if (nwritten < 0)
+ 			*err = errno;
+ 		else
+ 			*err = WTAP_ERR_SHORT_WRITE;
+ 		return 0;
+ 	}
+ 
+ 	/* current "libpcap" format is 2.4 */
+ 	file_hdr.version_major = 2;
+ 	file_hdr.version_minor = 4;
+ 	file_hdr.thiszone = 0;	/* XXX - current offset? */
+ 	file_hdr.sigfigs = 0;	/* unknown, but also apparently unused */
+ 	file_hdr.snaplen = wdh->snaplen;
+ 	file_hdr.network = wtap_encap[wdh->encap];
+ 	nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
+ 	if (nwritten != sizeof file_hdr) {
+ 		if (nwritten < 0)
+ 			*err = errno;
+ 		else
+ 			*err = WTAP_ERR_SHORT_WRITE;
+ 		return 0;
+ 	}
+ 
+ 	return 1;
+ }
+ 
+ /* Write a record for a packet to a dump file.
+    Returns 1 on success, 0 on failure. */
+ static int libpcap_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
+     const u_char *pd)
+ {
+ 	struct pcaprec_hdr rec_hdr;
+ 	int nwritten;
+ 
+ 	rec_hdr.ts_sec = phdr->ts.tv_sec;
+ 	rec_hdr.ts_usec = phdr->ts.tv_usec;
+ 	rec_hdr.incl_len = phdr->caplen;
+ 	rec_hdr.orig_len = phdr->len;
+ 	nwritten = fwrite(&rec_hdr, 1, sizeof rec_hdr, wdh->fh);
+ 	if (nwritten != sizeof rec_hdr)
+ 		return 0;	/* failed (XXX - save reason why) */
+ 	nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
+ 	if (nwritten != phdr->caplen)
+ 		return 0;	/* failed (XXX - save reason why) */
+ 	return 1;
+ }
+ 
+ /* Close a dump file.
+    Returns 1 on success, 0 on failure. */
+ static int libpcap_dump_close(wtap_dumper *wdh)
+ {
+ 	return 1;
  }
Index: wiretap/wtap.h
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/wtap.h,v
retrieving revision 1.23
diff -c -r1.23 wtap.h
*** wtap.h	1999/08/15 06:59:13	1.23
--- wtap.h	1999/08/16 06:25:40
***************
*** 32,38 ****
   * that code adds a DLT_ATM_CLIP DLT_ code of 19, and that
   * encapsulation isn't the same as the DLT_ATM_RFC1483 encapsulation
   * presumably used on some BSD systems, which we turn into
!  * WTAP_ENCAP_ATM_RFC1483. */
  #define WTAP_ENCAP_NONE				0
  #define WTAP_ENCAP_ETHERNET			1
  #define WTAP_ENCAP_TR				2
--- 32,44 ----
   * that code adds a DLT_ATM_CLIP DLT_ code of 19, and that
   * encapsulation isn't the same as the DLT_ATM_RFC1483 encapsulation
   * presumably used on some BSD systems, which we turn into
!  * WTAP_ENCAP_ATM_RFC1483.
!  *
!  * WTAP_ENCAP_PER_PACKET is a value passed to "wtap_dump_open()" or
!  * "wtap_dump_fdopen()" to indicate that there is no single encapsulation
!  * type for all packets in the file; this may cause those routines to
!  * fail if the capture file format being written can't support that. */
! #define WTAP_ENCAP_PER_PACKET			-1
  #define WTAP_ENCAP_NONE				0
  #define WTAP_ENCAP_ETHERNET			1
  #define WTAP_ENCAP_TR				2
***************
*** 48,54 ****
  /* last WTAP_ENCAP_ value + 1 */
  #define WTAP_NUM_ENCAP_TYPES			11
  
! /* File types that can be read by wiretap */
  #define WTAP_FILE_UNKNOWN			0
  #define WTAP_FILE_WTAP				1
  #define WTAP_FILE_PCAP				2
--- 54,62 ----
  /* last WTAP_ENCAP_ value + 1 */
  #define WTAP_NUM_ENCAP_TYPES			11
  
! /* File types that can be read by wiretap.
!    We may eventually support writing some or all of these file types,
!    too, so we distinguish between different versions of them. */
  #define WTAP_FILE_UNKNOWN			0
  #define WTAP_FILE_WTAP				1
  #define WTAP_FILE_PCAP				2
***************
*** 56,64 ****
  #define WTAP_FILE_NGSNIFFER			4
  #define WTAP_FILE_SNOOP				6
  #define WTAP_FILE_IPTRACE			7
! #define WTAP_FILE_NETMON			8
! #define WTAP_FILE_NETXRAY			9
! #define WTAP_FILE_RADCOM			10
  
  /* Filter types that wiretap can create. An 'offline' filter is really
   * a BPF filter, but it is treated specially because wiretap might not know
--- 64,75 ----
  #define WTAP_FILE_NGSNIFFER			4
  #define WTAP_FILE_SNOOP				6
  #define WTAP_FILE_IPTRACE			7
! #define WTAP_FILE_NETMON_1_x			8
! #define WTAP_FILE_NETMON_2_x			9
! #define WTAP_FILE_NETXRAY_1_0			10
! #define WTAP_FILE_NETXRAY_1_1			11
! #define WTAP_FILE_NETXRAY_2_001			12
! #define WTAP_FILE_RADCOM			13
  
  /* Filter types that wiretap can create. An 'offline' filter is really
   * a BPF filter, but it is treated specially because wiretap might not know
***************
*** 162,173 ****
  						   types */
  } wtap;
  
  /*
   * On failure, "wtap_open_offline()" returns NULL, and puts into the
   * "int" pointed to by its second argument:
   *
-  * 0 on success;
-  *
   * a positive "errno" value if the capture file can't be opened;
   *
   * a negative number, indicating the type of error, on other failures.
--- 173,197 ----
  						   types */
  } wtap;
  
+ struct wtap_dumper;
+ 
+ typedef int (*subtype_write_func)(struct wtap_dumper*,
+ 		const struct wtap_pkthdr*, const u_char*);
+ typedef int (*subtype_close_func)(struct wtap_dumper *);
+ typedef struct wtap_dumper {
+ 	FILE*			fh;
+ 	int			file_type;
+ 	int			snaplen;
+ 	int			encap;
+ 
+ 	subtype_write_func	subtype_write;
+ 	subtype_close_func	subtype_close;
+ } wtap_dumper;
+ 
  /*
   * On failure, "wtap_open_offline()" returns NULL, and puts into the
   * "int" pointed to by its second argument:
   *
   * a positive "errno" value if the capture file can't be opened;
   *
   * a negative number, indicating the type of error, on other failures.
***************
*** 183,188 ****
--- 207,241 ----
  int wtap_file_type(wtap *wth);
  const char *wtap_file_type_string(wtap *wth);
  void wtap_close(wtap *wth);
+ 
+ /*
+  * On failure, "wtap_dump_open()" and "wtap_dump_fdopen()" return NULL,
+  * and put into the "int" pointed to by its second argument:
+  *
+  * a positive "errno" value if the capture file can't be created, or
+  * some other failure that sets "errno" occurs;
+  *
+  * a negative number, indicating the type of error, on other failures.
+  */
+ #define	WTAP_ERR_CANT_OPEN			-1
+ 		/* couldn't open, reason unknown */
+ #define	WTAP_ERR_UNSUPPORTED_FILE_TYPE		-2
+ 		/* can't save files in that format */
+ #define	WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED	-3
+ 		/* that format doesn't support per-packet encapsulations */
+ #define	WTAP_ERR_SHORT_WRITE			-4
+ 		/* write wrote less data than it should have */
+ 
+ wtap_dumper* wtap_dump_open(const char *filename, int filetype, int encap,
+ 	int snaplen, int *err);
+ wtap_dumper* wtap_dump_fdopen(int fd, int filetype, int encap, int snaplen,
+ 	int *err);
+ int wtap_dump(wtap_dumper *, const struct wtap_pkthdr *, const u_char *);
+ FILE* wtap_dump_file(wtap_dumper *);
+ int wtap_dump_close(wtap_dumper *);
+ 
+ /* XXX - needed until "wiretap" can do live packet captures */
+ int wtap_pcap_encap_to_wtap_encap(int encap);
  
  /* Pointer versions of ntohs and ntohl.  Given a pointer to a member of a
   * byte array, returns the value of the two or four bytes at the pointer.
Index: wiretap/libpcap.h
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/libpcap.h,v
retrieving revision 1.1
diff -c -r1.1 libpcap.h
*** libpcap.h	1998/11/15 05:29:12	1.1
--- libpcap.h	1999/08/16 06:25:40
***************
*** 22,25 ****
   */
  
  int libpcap_open(wtap *wth);
! int libpcap_read(wtap *wth);
--- 22,25 ----
   */
  
  int libpcap_open(wtap *wth);
! int libpcap_dump_open(wtap_dumper *wdh, int *err);
Index: wiretap/file.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/file.c,v
retrieving revision 1.13
diff -c -r1.13 file.c
*** file.c	1999/08/15 06:59:13	1.13
--- file.c	1999/08/16 06:25:41
***************
*** 100,115 ****
  	if ((wth->file_type = iptrace_open(wth)) != WTAP_FILE_UNKNOWN) {
  		goto success;
  	}
! 	/* WTAP_FILE_NETMON */
  	if ((wth->file_type = netmon_open(wth)) != WTAP_FILE_UNKNOWN) {
  		goto success;
  	}
! 	/* WTAP_FILE_NETXRAY */
  	if ((wth->file_type = netxray_open(wth)) != WTAP_FILE_UNKNOWN) {
  		goto success;
  	}
  
- 
  /* failure: */
  	fclose(wth->fh);
  	free(wth);
--- 100,114 ----
  	if ((wth->file_type = iptrace_open(wth)) != WTAP_FILE_UNKNOWN) {
  		goto success;
  	}
! 	/* WTAP_FILE_NETMON_xxx */
  	if ((wth->file_type = netmon_open(wth)) != WTAP_FILE_UNKNOWN) {
  		goto success;
  	}
! 	/* WTAP_FILE_NETXRAY_xxx */
  	if ((wth->file_type = netxray_open(wth)) != WTAP_FILE_UNKNOWN) {
  		goto success;
  	}
  
  /* failure: */
  	fclose(wth->fh);
  	free(wth);
***************
*** 121,123 ****
--- 120,221 ----
  	buffer_init(wth->frame_buffer, 1500);
  	return wth;
  }
+ 
+ 
+ static wtap_dumper* wtap_dump_open_common(FILE *fh, int filetype,
+     int encap, int snaplen, int *err);
+ 
+ wtap_dumper* wtap_dump_open(const char *filename, int filetype, int encap,
+ 				int snaplen, int *err)
+ {
+ 	FILE *fh;
+ 
+ 	/* In case "fopen()" fails but doesn't set "errno", set "errno"
+ 	   to a generic "the open failed" error. */
+ 	errno = WTAP_ERR_CANT_OPEN;
+ 	fh = fopen(filename, "w");
+ 	if (fh == NULL) {
+ 		*err = errno;
+ 		return NULL;	/* can't create file */
+ 	}
+ 	return wtap_dump_open_common(fh, filetype, encap, snaplen, err);
+ }
+ 
+ wtap_dumper* wtap_dump_fdopen(int fd, int filetype, int encap, int snaplen,
+ 				int *err)
+ {
+ 	FILE *fh;
+ 
+ 	/* In case "fopen()" fails but doesn't set "errno", set "errno"
+ 	   to a generic "the open failed" error. */
+ 	errno = WTAP_ERR_CANT_OPEN;
+ 	fh = fdopen(fd, "w");
+ 	if (fh == NULL) {
+ 		*err = errno;
+ 		return NULL;	/* can't create standard I/O stream */
+ 	}
+ 	return wtap_dump_open_common(fh, filetype, encap, snaplen, err);
+ }
+ 
+ static wtap_dumper* wtap_dump_open_common(FILE *fh, int filetype, int encap,
+ 					int snaplen, int *err)
+ {
+ 	wtap_dumper *wdh;
+ 
+ 	wdh = malloc(sizeof (wtap_dumper));
+ 	if (wdh == NULL) {
+ 		*err = errno;
+ 		/* NOTE: this means the FD handed to "wtap_dump_fdopen()"
+ 		   will be closed if the malloc fails. */
+ 		fclose(fh);
+ 		return NULL;
+ 	}
+ 	wdh->fh = fh;
+ 	wdh->file_type = filetype;
+ 	wdh->snaplen = snaplen;
+ 	wdh->encap = encap;
+ 
+ 	switch (filetype) {
+ 
+ 	case WTAP_FILE_PCAP:
+ 		if (!libpcap_dump_open(wdh, err))
+ 			goto fail;
+ 		break;
+ 
+ 	default:
+ 		/* We currently only support dumping "libpcap" files */
+ 		*err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+ 		goto fail;
+ 	}
+ 	return wdh;
+ 
+ fail:
+ 	free(wdh);
+ 	fclose(fh);
+ 	return NULL;	/* XXX - provide a reason why we failed */
+ }
+ 
+ FILE* wtap_dump_file(wtap_dumper *wdh)
+ {
+ 	return wdh->fh;
+ }
+ 
+ int wtap_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
+     const u_char *pd)
+ {
+ 	return (wdh->subtype_write)(wdh, phdr, pd);
+ }
+ 
+ int wtap_dump_close(wtap_dumper *wdh)
+ {
+ 	int ret = 1;
+ 
+ 	if (!(wdh->subtype_close)(wdh))
+ 		ret = 0;
+ 	ret = fclose(wdh->fh);
+ 	if (ret == EOF)
+ 		ret = 0;
+ 	free(wdh);
+ 	return ret;
+ }
+ 
Index: wiretap/netxray.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/netxray.c,v
retrieving revision 1.8
diff -c -r1.8 netxray.c
*** netxray.c	1999/07/13 02:53:25	1.8
--- netxray.c	1999/08/16 06:25:41
***************
*** 94,99 ****
--- 94,100 ----
  	struct netxray_hdr hdr;
  	double timeunit;
  	int version_major;
+ 	int file_type;
  	double t;
  	static const int netxray_encap[] = {
  		WTAP_ENCAP_ETHERNET,
***************
*** 139,150 ****
--- 140,154 ----
  	if (memcmp(hdr.version, vers_1_0, sizeof vers_1_0) == 0) {
  		timeunit = 1000.0;
  		version_major = 1;
+ 		file_type = WTAP_FILE_NETXRAY_1_0;
  	} else if (memcmp(hdr.version, vers_1_1, sizeof vers_1_1) == 0) {
  		timeunit = 1000000.0;
  		version_major = 1;
+ 		file_type = WTAP_FILE_NETXRAY_1_1;
  	} else if (memcmp(hdr.version, vers_2_001, sizeof vers_2_001) == 0) {
  		timeunit = 1000000.0;
  		version_major = 2;
+ 		file_type = WTAP_FILE_NETXRAY_2_001;
  	} else {
  		return WTAP_FILE_UNKNOWN;
  	}
***************
*** 179,185 ****
  	/* Seek to the beginning of the data records. */
  	fseek(wth->fh, pletohl(&hdr.start_offset), SEEK_SET);
  
! 	return WTAP_FILE_NETXRAY;
  }
  
  /* Read the next packet */
--- 183,189 ----
  	/* Seek to the beginning of the data records. */
  	fseek(wth->fh, pletohl(&hdr.start_offset), SEEK_SET);
  
! 	return file_type;
  }
  
  /* Read the next packet */
Index: wiretap/wtap.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/wtap.c,v
retrieving revision 1.13
diff -c -r1.13 wtap.c
*** wtap.c	1999/08/02 02:35:57	1.13
--- wtap.c	1999/08/16 06:25:41
***************
*** 62,73 ****
  		case WTAP_FILE_IPTRACE:
  			return "iptrace";
  
! 		case WTAP_FILE_NETMON:
! 			return "Microsoft Network Monitor";
  
! 		case WTAP_FILE_NETXRAY:
! 			return "Cinco Networks NetXRay/Network Associates Sniffer (Windows-based)";
  
  		case WTAP_FILE_RADCOM:
  			return "RADCOM WAN/LAN analyzer";
  
--- 62,82 ----
  		case WTAP_FILE_IPTRACE:
  			return "iptrace";
  
! 		case WTAP_FILE_NETMON_1_x:
! 			return "Microsoft Network Monitor 1.x";
  
! 		case WTAP_FILE_NETMON_2_x:
! 			return "Microsoft Network Monitor 2.x";
  
+ 		case WTAP_FILE_NETXRAY_1_0:
+ 			return "Cinco Networks NetXRay";
+ 
+ 		case WTAP_FILE_NETXRAY_1_1:
+ 			return "Network Associates Sniffer (Windows-based) 1.1";
+ 
+ 		case WTAP_FILE_NETXRAY_2_001:
+ 			return "Network Associates Sniffer (Windows-based) 2.001";
+ 
  		case WTAP_FILE_RADCOM:
  			return "RADCOM WAN/LAN analyzer";
  
***************
*** 100,110 ****
  			g_free(wth->capture.radcom);
  			break;
  
! 		case WTAP_FILE_NETMON:
  			g_free(wth->capture.netmon);
  			break;
  
! 		case WTAP_FILE_NETXRAY:
  			g_free(wth->capture.netxray);
  			break;
  
--- 109,122 ----
  			g_free(wth->capture.radcom);
  			break;
  
! 		case WTAP_FILE_NETMON_1_x:
! 		case WTAP_FILE_NETMON_2_x:
  			g_free(wth->capture.netmon);
  			break;
  
! 		case WTAP_FILE_NETXRAY_1_0:
! 		case WTAP_FILE_NETXRAY_1_1:
! 		case WTAP_FILE_NETXRAY_2_001:
  			g_free(wth->capture.netxray);
  			break;
  
Index: wiretap/netmon.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/netmon.c,v
retrieving revision 1.8
diff -c -r1.8 netmon.c
*** netmon.c	1999/07/13 02:53:24	1.8
--- netmon.c	1999/08/16 06:25:42
***************
*** 95,100 ****
--- 95,101 ----
  	int bytes_read;
  	char magic[sizeof netmon_1_x_magic];
  	struct netmon_hdr hdr;
+ 	int file_type;
  	static const int netmon_encap[] = {
  		WTAP_ENCAP_NONE,
  		WTAP_ENCAP_ETHERNET,
***************
*** 135,143 ****
--- 136,146 ----
  	switch (hdr.ver_major) {
  
  	case 1:
+ 		file_type = WTAP_FILE_NETMON_1_x;
  		break;
  
  	case 2:
+ 		file_type = WTAP_FILE_NETMON_2_x;
  		break;
  
  	default:
***************
*** 195,201 ****
  	/* Seek to the beginning of the data records. */
  	fseek(wth->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
  
! 	return WTAP_FILE_NETMON;
  }
  
  /* Read the next packet */
--- 198,204 ----
  	/* Seek to the beginning of the data records. */
  	fseek(wth->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
  
! 	return file_type;
  }
  
  /* Read the next packet */