Ethereal-dev: [ethereal-dev] [patch] pipe capture

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

From: Olivier Abad <oabad@xxxxxxxxxxxxx>
Date: Tue, 25 Jul 2000 00:37:07 +0200
Here is a first patch to read captures from a pipe.

I didn't change anything in the capture window : if you enter a pipe
name (e.g. /tmp/fifo) instead of an interface name, this name is passed
to the capture() function, which first tries a pcap_open_live() on it,
and if it fails, performs a pipe_open_live()

I have created two functions : pipe_open_live() to initialize the
capture from the pipe, and pipe_dispatch() to read one record from the
pipe and write it in the capture file.

Reading from stdin seems to work also. You can do something like this :
$ wget -O - http://ethereal.zing.org/sample/v6.pcap | ./ethereal -i - -k

Please tell me if you find problems. I'll check the code in CVS later.

Olivier

P.S. : I guess it will work only on Unix platforms. I'll have to put
#ifdefs for win32.
-- 
When you have 200 programmers trying to write code for one
product, like Win95 or NT, what you get is a multipule personality
program.  By definition, the real problem is that these programs are
psychotic by nature and make people crazy when they use them.
	-- Joan Brewer on alt.destroy.microsoft
diff -uNr ethereal/capture.c ethereal.pipe/capture.c
--- ethereal/capture.c	Fri Jul 21 23:19:05 2000
+++ ethereal.pipe/capture.c	Mon Jul 24 23:29:54 2000
@@ -99,6 +99,8 @@
 #include "simple_dialog.h"
 #include "prefs.h"
 #include "globals.h"
+#include "wiretap/libpcap.h"
+#include "wiretap/wtap-int.h"
 
 #include "packet-clip.h"
 #include "packet-eth.h"
@@ -134,6 +136,9 @@
   gint           max;
   gint           linktype;
   gint           sync_packets;
+  gboolean       from_pipe;    /* TRUE if we are capturing data from a pipe */
+  gboolean       modified;     /* TRUE if data in the pipe uses modified pcap headers */
+  gboolean       byte_swapped; /* TRUE if data in the pipe is byte swapped */
   packet_counts  counts;
   wtap_dumper   *pdh;
 } loop_data;
@@ -707,6 +712,204 @@
  */
 #define	CAP_READ_TIMEOUT	250
 
+static void
+adjust_header(loop_data *ld, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
+{
+  if (ld->byte_swapped) {
+    /* Byte-swap the record header fields. */
+    rechdr->ts_sec = BSWAP32(rechdr->ts_sec);
+    rechdr->ts_usec = BSWAP32(rechdr->ts_usec);
+    rechdr->incl_len = BSWAP32(rechdr->incl_len);
+    rechdr->orig_len = BSWAP32(rechdr->orig_len);
+  }
+  if (hdr->version_major == 2 &&
+      (hdr->version_minor < 3 ||
+       (hdr->version_minor == 3 && rechdr->incl_len > rechdr->orig_len))) {
+    guint32 temp;
+
+    temp = rechdr->orig_len;
+    rechdr->orig_len = rechdr->incl_len;
+    rechdr->incl_len = temp;
+  }
+}
+
+/* mimic pcap_open_live() for pipe captures */
+static int
+pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, char *ebuf)
+{
+  struct stat pipe_stat;
+  int         fd;
+  guint32     magic;
+  int         bytes_read, b;
+
+  if (strcmp(pipename, "-") == 0) fd = 0; /* read from stdin */
+  else if (stat(pipename, &pipe_stat) == 0 && S_ISFIFO(pipe_stat.st_mode)) {
+    if ((fd = open(pipename, O_RDONLY)) == -1) return -1;
+  } else return -1;
+
+  ld->from_pipe = TRUE;
+  /* read the pcap header */
+  if (read(fd, &magic, sizeof magic) != sizeof magic) {
+    close(fd);
+    return -1;
+  }
+
+  switch (magic) {
+  case PCAP_MAGIC:
+    /* Host that wrote it has our byte order. */
+    ld->byte_swapped = FALSE;
+    ld->modified = FALSE;
+    break;
+  case PCAP_MODIFIED_MAGIC:
+    /* Host that wrote it has our byte order, but was running
+       a program using the patched "libpcap". */
+    ld->byte_swapped = FALSE;
+    ld->modified = TRUE;
+    break;
+  case PCAP_SWAPPED_MAGIC:
+    /* Host that wrote it has a byte order opposite to ours. */
+    ld->byte_swapped = TRUE;
+    ld->modified = FALSE;
+    break;
+  case PCAP_SWAPPED_MODIFIED_MAGIC:
+    /* Host that wrote it out has a byte order opposite to
+       ours, and was running a program using the patched
+       "libpcap". */
+    ld->byte_swapped = TRUE;
+    ld->modified = TRUE;
+    break;
+  default:
+    /* Not a "libpcap" type we know about. */
+    close(fd);
+    return -1;
+  }
+
+  bytes_read = read(fd, hdr, sizeof(struct pcap_hdr));
+  if (bytes_read <= 0) {
+    close(fd);
+    return -1;
+  }
+  while (bytes_read < sizeof(struct pcap_hdr))
+  {
+    b = read(fd, ((char *)&hdr)+bytes_read, sizeof(struct pcap_hdr) - bytes_read);
+    if (b <= 0) {
+      close(fd);
+      return -1;
+    }
+    bytes_read += b;
+  }
+  if (ld->byte_swapped) {
+    /* Byte-swap the header fields about which we care. */
+    hdr->version_major = BSWAP16(hdr->version_major);
+    hdr->version_minor = BSWAP16(hdr->version_minor);
+    hdr->snaplen = BSWAP32(hdr->snaplen);
+    hdr->network = BSWAP32(hdr->network);
+  }
+  if (hdr->version_major < 2) {
+    close(fd);
+    return -1;
+  }
+
+  return fd;
+}
+
+static int
+pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr)
+{
+  struct wtap_pkthdr whdr;
+  struct pcaprec_modified_hdr rechdr;
+  int bytes_to_read, bytes_read, b;
+  u_char pd[WTAP_MAX_PACKET_SIZE];
+  int err;
+
+  bytes_to_read = ld->modified ? sizeof rechdr : sizeof rechdr.hdr;
+  bytes_read = read(fd, &rechdr, bytes_to_read);
+  if (bytes_read <= 0) {
+    close(fd);
+    ld->go = FALSE;
+    fprintf(stderr, "record header read error : %d, errno = %d\n", bytes_read, errno);
+    return 0;
+  }
+  while (bytes_read < bytes_to_read)
+  {
+    b = read(fd, ((char *)&rechdr)+bytes_read, bytes_to_read - bytes_read);
+    if (b <= 0) {
+      close(fd);
+      ld->go = FALSE;
+      fprintf(stderr, "record header read error : %d, errno = %d\n", bytes_read, errno);
+      return 0;
+    }
+    bytes_read += b;
+  }
+  adjust_header(ld, hdr, &rechdr.hdr);
+  if (rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
+    close(fd);
+    ld->go = FALSE;
+    fprintf(stderr, "invalid record length : %d\n", rechdr.hdr.incl_len);
+    return 0;
+  }
+  bytes_read = read(fd, pd, rechdr.hdr.incl_len);
+  if (bytes_read <= 0) {
+    close(fd);
+    ld->go = FALSE;
+    fprintf(stderr, "record read error : %d, errno = %d\n", bytes_read, errno);
+    return 0;
+  }
+  while (bytes_read < rechdr.hdr.incl_len)
+  {
+    b = read(fd, pd+bytes_read, rechdr.hdr.incl_len - bytes_read);
+    if (b <= 0) {
+      close(fd);
+      ld->go = FALSE;
+      fprintf(stderr, "record read error : %d, errno = %d\n", bytes_read, errno);
+      return 0;
+    }
+    bytes_read += b;
+  }
+  whdr.ts.tv_sec = rechdr.hdr.ts_sec;
+  whdr.ts.tv_usec = rechdr.hdr.ts_usec;
+  whdr.caplen = rechdr.hdr.incl_len;
+  whdr.len = rechdr.hdr.orig_len;
+  whdr.pkt_encap = ld->linktype;
+  wtap_dump(ld->pdh, &whdr, NULL, pd, &err);
+
+  /* Set the initial payload to the packet length, and the initial
+     captured payload to the capture length (other protocols may
+     reduce them if their headers say they're less). */
+  pi.len = whdr.len;
+  pi.captured_len = whdr.caplen;
+    
+  switch (ld->linktype) {
+    case WTAP_ENCAP_ETHERNET:
+      capture_eth(pd, 0, &ld->counts);
+      break;
+    case WTAP_ENCAP_FDDI:
+    case WTAP_ENCAP_FDDI_BITSWAPPED:
+      capture_fddi(pd, &ld->counts);
+      break;
+    case WTAP_ENCAP_TR:
+      capture_tr(pd, 0, &ld->counts);
+      break;
+    case WTAP_ENCAP_NULL:
+      capture_null(pd, &ld->counts);
+      break;
+    case WTAP_ENCAP_PPP:
+      capture_ppp(pd, 0, &ld->counts);
+      break;
+    case WTAP_ENCAP_RAW_IP:
+      capture_raw(pd, &ld->counts);
+      break;
+    case WTAP_ENCAP_LINUX_ATM_CLIP:
+      capture_clip(pd, &ld->counts);
+      break;
+    /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
+       with LLC header following; we should implement it at some
+       point. */
+  }
+
+  return 1;
+}
+
 /* Do the low-level work of a capture.
    Returns TRUE if it succeeds, FALSE otherwise. */
 int
@@ -724,12 +927,14 @@
 #ifdef linux
   fd_set      set1;
   struct timeval timeout;
-  int         pcap_fd;
+  int         pcap_fd = 0;
 #endif
 #ifdef _WIN32 
   WORD wVersionRequested; 
   WSADATA wsaData; 
 #endif
+  int         pipe_fd = -1;
+  struct pcap_hdr hdr;
 
   /* Initialize Windows Socket if we are in a WIN32 OS 
      This needs to be done before querying the interface for network/netmask */
@@ -748,6 +953,7 @@
   ld.counts.total   = 0;
   ld.max            = cfile.count;
   ld.linktype       = WTAP_ENCAP_UNKNOWN;
+  ld.from_pipe      = FALSE;
   ld.sync_packets   = 0;
   ld.counts.sctp    = 0;
   ld.counts.tcp     = 0;
@@ -765,22 +971,28 @@
   pch = pcap_open_live(cfile.iface, cfile.snap, 1, CAP_READ_TIMEOUT, err_str);
 
   if (pch == NULL) {
-    /* Well, we couldn't start the capture.
-       If this is a child process that does the capturing in sync
-       mode or fork mode, it shouldn't do any UI stuff until we pop up the
-       capture-progress window, and, since we couldn't start the
-       capture, we haven't popped it up. */
-    if (!capture_child) {
-      while (gtk_events_pending()) gtk_main_iteration();
-    }
-    snprintf(errmsg, sizeof errmsg,
-      "The capture session could not be initiated (%s).\n"
-      "Please check to make sure you have sufficient permissions, and that\n"
-      "you have the proper interface specified.", err_str);
-    goto error;
+    /* try to open cfile.iface as a pipe */
+    pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld, err_str);
+
+    if (pipe_fd == -1) {
+      /* Well, we couldn't start the capture.
+	 If this is a child process that does the capturing in sync
+	 mode or fork mode, it shouldn't do any UI stuff until we pop up the
+	 capture-progress window, and, since we couldn't start the
+	 capture, we haven't popped it up. */
+      if (!capture_child) {
+	while (gtk_events_pending()) gtk_main_iteration();
+      }
+      snprintf(errmsg, sizeof errmsg,
+	  "The capture session could not be initiated (%s).\n"
+	  "Please check to make sure you have sufficient permissions, and that\n"
+	  "you have the proper interface or pipe specified.", err_str);
+      goto error;
+    }
   }
 
-  if (cfile.cfilter) {
+  /* capture filters only work on real interfaces */
+  if (cfile.cfilter && !ld.from_pipe) {
     /* A capture filter was specified; set it up. */
     if (pcap_lookupnet (cfile.iface, &netnum, &netmask, err_str) < 0) {
       snprintf(errmsg, sizeof errmsg,
@@ -800,14 +1012,15 @@
   }
 
   /* Set up to write to the capture file. */
-  ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(pch));
+  ld.linktype = wtap_pcap_encap_to_wtap_encap(ld.from_pipe ? hdr.network
+	                                                   : pcap_datalink(pch));
   if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
     strcpy(errmsg, "The network you're capturing from is of a type"
              " that Ethereal doesn't support.");
     goto error;
   }
   ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP,
-		ld.linktype, pcap_snapshot(pch), &err);
+      ld.linktype, ld.from_pipe ? hdr.snaplen : pcap_snapshot(pch), &err);
 
   if (ld.pdh == NULL) {
     /* We couldn't set up to write to the capture file. */
@@ -929,41 +1142,60 @@
 
   upd_time = time(NULL);
 #ifdef linux
-  pcap_fd = pcap_fileno(pch);
+  if (!ld.from_pipe) pcap_fd = pcap_fileno(pch);
 #endif
   while (ld.go) {
     while (gtk_events_pending()) gtk_main_iteration();
+
+    if (ld.from_pipe) {
+      FD_ZERO(&set1);
+      FD_SET(pipe_fd, &set1);
+      timeout.tv_sec = 0;
+      timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+      if (select(pipe_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+	/*
+	 * "select()" says we can read from the pipe without blocking; go for
+	 * it. We are not sure we can read a whole record, but at least the
+	 * begninning of one. pipe_dispatch() will block reading the whole
+	 * record.
+	 */
+	inpkts = pipe_dispatch(pipe_fd, &ld, &hdr);
+      } else
+	inpkts = 0;
+    }
+    else {
 #ifdef linux
-    /*
-     * Sigh.  The semantics of the read timeout argument to
-     * "pcap_open_live()" aren't particularly well specified by
-     * the "pcap" man page - at least with the BSD BPF code, the
-     * intent appears to be, at least in part, a way of cutting
-     * down the number of reads done on a capture, by blocking
-     * until the buffer fills or a timer expires - and the Linux
-     * libpcap doesn't actually support it, so we can't use it
-     * to break out of the "pcap_dispatch()" every 1/4 of a second
-     * or so.
-     *
-     * Thus, on Linux, we do a "select()" on the file descriptor for the
-     * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or
-     * CAP_READ_TIMEOUT*1000 microseconds.
-     */
-    FD_ZERO(&set1);
-    FD_SET(pcap_fd, &set1);
-    timeout.tv_sec = 0;
-    timeout.tv_usec = CAP_READ_TIMEOUT*1000;
-    if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
       /*
-       * "select()" says we can read from it without blocking; go for
-       * it.
+       * Sigh.  The semantics of the read timeout argument to
+       * "pcap_open_live()" aren't particularly well specified by
+       * the "pcap" man page - at least with the BSD BPF code, the
+       * intent appears to be, at least in part, a way of cutting
+       * down the number of reads done on a capture, by blocking
+       * until the buffer fills or a timer expires - and the Linux
+       * libpcap doesn't actually support it, so we can't use it
+       * to break out of the "pcap_dispatch()" every 1/4 of a second
+       * or so.
+       *
+       * Thus, on Linux, we do a "select()" on the file descriptor for the
+       * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or
+       * CAP_READ_TIMEOUT*1000 microseconds.
        */
-      inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
-    } else
-      inpkts = 0;
+      FD_ZERO(&set1);
+      FD_SET(pcap_fd, &set1);
+      timeout.tv_sec = 0;
+      timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+      if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+	/*
+	 * "select()" says we can read from it without blocking; go for
+	 * it.
+	 */
+	inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+      } else
+	inpkts = 0;
 #else
-    inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+      inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
 #endif
+    }
     if (inpkts > 0)
       ld.sync_packets += inpkts;
     /* Only update once a second so as not to overload slow displays */
@@ -1057,7 +1289,7 @@
       break;
     }
   }
-  pcap_close(pch);
+  if (!ld.from_pipe) pcap_close(pch);
 
   gtk_grab_remove(GTK_WIDGET(cap_w));
   gtk_widget_destroy(GTK_WIDGET(cap_w));
diff -uNr ethereal/wiretap/libpcap.c ethereal.pipe/wiretap/libpcap.c
--- ethereal/wiretap/libpcap.c	Sat May 20 01:06:53 2000
+++ ethereal.pipe/wiretap/libpcap.c	Sat Jul 22 15:03:23 2000
@@ -33,58 +33,10 @@
 /* See source to the "libpcap" library for information on the "libpcap"
    file format. */
 
-/* Magic numbers in "libpcap" files.
-
-   "libpcap" file records are written in the byte order of the host that
-   writes them, and the reader is expected to fix this up.
-
-   PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC
-   is a byte-swapped version of that.
-
-   PCAP_MODIFIED_MAGIC is for Alexey Kuznetsov's modified "libpcap"
-   format, as generated on Linux systems that have a "libpcap" with
-   his patches, at
-   
-	http://ftp.sunet.se/pub/os/Linux/ip-routing/lbl-tools/
-
-   applied; PCAP_SWAPPED_MODIFIED_MAGIC is the byte-swapped version. */
-#define	PCAP_MAGIC			0xa1b2c3d4
-#define	PCAP_SWAPPED_MAGIC		0xd4c3b2a1
-#define	PCAP_MODIFIED_MAGIC		0xa1b2cd34
-#define	PCAP_SWAPPED_MODIFIED_MAGIC	0x34cdb2a1
-
 /* On some systems, the FDDI MAC addresses are bit-swapped. */
 #if !defined(ultrix) && !defined(__alpha) && !defined(__bsdi__)
 #define BIT_SWAPPED_MAC_ADDRS
 #endif
-
-/* "libpcap" file header (minus magic number). */
-struct pcap_hdr {
-	guint16	version_major;	/* major version number */
-	guint16	version_minor;	/* minor version number */
-	gint32	thiszone;	/* GMT to local correction */
-	guint32	sigfigs;	/* accuracy of timestamps */
-	guint32	snaplen;	/* max length of captured packets, in octets */
-	guint32	network;	/* data link type */
-};
-
-/* "libpcap" record header. */
-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 */
-};
-
-/* "libpcap" record header for Alexey's patched version. */
-struct pcaprec_modified_hdr {
-	struct pcaprec_hdr hdr;	/* the regular header */
-	guint32 ifindex;	/* index, in *capturing* machine's list of
-				   interfaces, of the interface on which this
-				   packet came in. */
-	guint16 protocol;	/* Ethernet packet type */
-	guint8 pkt_type;	/* broadcast/multicast/etc. indication */
-};
 
 static int libpcap_read(wtap *wth, int *err);
 static void adjust_header(wtap *wth, struct pcaprec_hdr *hdr);
diff -uNr ethereal/wiretap/libpcap.h ethereal.pipe/wiretap/libpcap.h
--- ethereal/wiretap/libpcap.h	Sat Jan 22 07:22:39 2000
+++ ethereal.pipe/wiretap/libpcap.h	Sat Jul 22 15:03:25 2000
@@ -21,6 +21,54 @@
  *
  */
 
+/* Magic numbers in "libpcap" files.
+
+   "libpcap" file records are written in the byte order of the host that
+   writes them, and the reader is expected to fix this up.
+
+   PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC
+   is a byte-swapped version of that.
+
+   PCAP_MODIFIED_MAGIC is for Alexey Kuznetsov's modified "libpcap"
+   format, as generated on Linux systems that have a "libpcap" with
+   his patches, at
+   
+	http://ftp.sunet.se/pub/os/Linux/ip-routing/lbl-tools/
+
+   applied; PCAP_SWAPPED_MODIFIED_MAGIC is the byte-swapped version. */
+#define	PCAP_MAGIC			0xa1b2c3d4
+#define	PCAP_SWAPPED_MAGIC		0xd4c3b2a1
+#define	PCAP_MODIFIED_MAGIC		0xa1b2cd34
+#define	PCAP_SWAPPED_MODIFIED_MAGIC	0x34cdb2a1
+
+/* "libpcap" file header (minus magic number). */
+struct pcap_hdr {
+	guint16	version_major;	/* major version number */
+	guint16	version_minor;	/* minor version number */
+	gint32	thiszone;	/* GMT to local correction */
+	guint32	sigfigs;	/* accuracy of timestamps */
+	guint32	snaplen;	/* max length of captured packets, in octets */
+	guint32	network;	/* data link type */
+};
+
+/* "libpcap" record header. */
+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 */
+};
+
+/* "libpcap" record header for Alexey's patched version. */
+struct pcaprec_modified_hdr {
+	struct pcaprec_hdr hdr;	/* the regular header */
+	guint32 ifindex;	/* index, in *capturing* machine's list of
+				   interfaces, of the interface on which this
+				   packet came in. */
+	guint16 protocol;	/* Ethernet packet type */
+	guint8 pkt_type;	/* broadcast/multicast/etc. indication */
+};
+
 int libpcap_open(wtap *wth, int *err);
 gboolean libpcap_dump_open(wtap_dumper *wdh, int *err);
 int libpcap_dump_can_write_encap(int filetype, int encap);