Ethereal-dev: Re: [Ethereal-dev] tetheral and -i - (stdin) option ...
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: rmkml <rmkml@xxxxxxxxxx>
Date: Wed, 25 Dec 2002 06:09:40 +0100
Hello Guy, I submit this patch, this patch work but Im not a C developper ... I found a new pbs, after option "-i -", is not possible add tcpdump/libpcap expression ! example: tethereal -t a -nri - 'ip and not tcp port 80' But this not work on ethereal 0.9.8 (gtk) !!! Next patch ... Please send me any comments ... Regards Guy Harris wrote: > rmkml said: > > Hello, > > > > I found option for pipe stdin in ethereal, > > (ethereal -i -) > > > > but this option is not in tethereal ! > > > > Is it possible include this options in next version of tethereal ? > > Not without either > > 1) changing the library Ethereal and Tethereal use to read captures to be > able to, in some cases, read from a pipe > > or > > 2) having Tethereal, if told to read from a pipe, use the less-capable > code that Ethereal uses for the "-i" option. ("Less-capable" means that > it assumes that the capture file is a standard libpcap file; it cannot > automatically detect the type of file, because the code that does > automatically detect the file currently does so by reading some of the > file's contents multiple times - that requires that it be able to seek > back to the beginning of the file and try reading again, and that doesn't > work on a pipe.) > > It is possible to include that option if somebody writes code to do so and > contributes it to Ethereal. I have enough things I'm *already* working on > for Ethereal that *I* will almost certainly not be doing it.
*** tethereal.c Wed Dec 25 03:49:52 2002 --- tethereal.c.pipestdin Wed Dec 25 05:42:03 2002 *************** *** 1,6 **** /* tethereal.c * ! * $Id: tethereal.c,v 1.171 2002/12/02 23:43:30 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@xxxxxxxxxxxx> --- 1,6 ---- /* tethereal.c * ! * $Id: tethereal.c,v 1.171a 2002/12/24 15:09:11 rmkml Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@xxxxxxxxxxxx> *************** *** 52,57 **** --- 52,61 ---- #include <setjmp.h> #endif + #ifdef HAVE_SYS_STAT_H + # include <sys/stat.h> + #endif + #ifdef HAVE_LIBZ #include <zlib.h> /* to get the libz version number */ #endif *************** *** 107,118 **** --- 111,125 ---- #ifdef HAVE_LIBPCAP #include <wiretap/wtap-capture.h> + #include <wiretap/libpcap.h> #endif #ifdef WIN32 #include "capture-wpcap.h" #endif + gboolean capture_child; /* if this is the child for "-S" */ + static guint32 firstsec, firstusec; static guint32 prevsec, prevusec; static GString *comp_info_str; *************** *** 125,136 **** --- 132,156 ---- #ifdef HAVE_LIBPCAP typedef struct _loop_data { gboolean go; /* TRUE as long as we're supposed to keep capturing */ + gint max; /* Number of packets we're supposed to capture - 0 means infinite */ + int err; /* if non-zero, error seen while capturing */ gint linktype; + gboolean from_pipe; /* TRUE if we are capturing data from a pipe */ pcap_t *pch; + packet_counts counts; wtap_dumper *pdh; jmp_buf stopenv; gboolean output_to_pipe; int packet_count; + gboolean modified; /* TRUE if data in the pipe uses modified pcap headers */ + gboolean byte_swapped; /* TRUE if data in the pipe is byte swapped */ + unsigned int bytes_to_read, bytes_read; /* Used by pipe_dispatch */ + enum { + STATE_EXPECT_REC_HDR, STATE_READ_REC_HDR, + STATE_EXPECT_DATA, STATE_READ_DATA + } pipe_state; + + enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } pipe_err; } loop_data; static loop_data ld; *************** *** 160,165 **** --- 180,189 ---- static void show_capture_file_io_error(const char *, int, gboolean); static void wtap_dispatch_cb_print(guchar *, const struct wtap_pkthdr *, long, union wtap_pseudo_header *, const guchar *); + static void adjust_header(loop_data *, struct pcap_hdr *, struct pcaprec_hdr *); + static int pipe_open_live(char *, struct pcap_hdr *, loop_data *, char *, int); + static int pipe_dispatch(int, loop_data *, struct pcap_hdr *, \ + struct pcaprec_modified_hdr *, guchar *, char *, int); capture_file cfile; ts_type timestamp_type = RELATIVE; *************** *** 944,949 **** --- 968,975 ---- static int capture(int out_file_type) { + int pcap_encap; + int file_snaplen; gchar open_err_str[PCAP_ERRBUF_SIZE]; gchar lookup_net_err_str[PCAP_ERRBUF_SIZE]; bpf_u_int32 netnum, netmask; *************** *** 952,968 **** int err = 0; int volatile volatile_err = 0; int volatile inpkts = 0; - int pcap_cnt; char errmsg[1024+1]; condition *volatile cnd_stop_capturesize = NULL; condition *volatile cnd_stop_timeout = NULL; #ifndef _WIN32 static const char ppamsg[] = "can't find PPA for "; char *libpcap_warn; #endif struct pcap_stat stats; gboolean write_err; gboolean dump_ok; /* Initialize all data structures used for dissection. */ init_dissection(); --- 978,1004 ---- int err = 0; int volatile volatile_err = 0; int volatile inpkts = 0; char errmsg[1024+1]; condition *volatile cnd_stop_capturesize = NULL; condition *volatile cnd_stop_timeout = NULL; + fd_set set1; + struct timeval timeout; #ifndef _WIN32 + int pcap_cnt; static const char ppamsg[] = "can't find PPA for "; char *libpcap_warn; + int sel_ret; + int pipe_fd = -1; + struct pcap_hdr hdr; + struct pcaprec_modified_hdr rechdr; + guchar pcap_data[WTAP_MAX_PACKET_SIZE]; #endif struct pcap_stat stats; gboolean write_err; gboolean dump_ok; + #ifdef MUST_DO_SELECT + int pcap_fd = 0; + #endif /* Initialize all data structures used for dissection. */ init_dissection(); *************** *** 994,999 **** --- 1030,1043 ---- "support capturing on PPP/WAN interfaces in Windows NT/2000.\n", open_err_str); #else + /* try to open cfile.iface as a pipe */ + pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld, errmsg, sizeof errmsg); + + if (pipe_fd == -1) { + + if (ld.pipe_err == PIPNEXIST) { + /* Pipe doesn't exist, so output message for interface */ + /* If we got a "can't find PPA for XXX" message, warn the user (who is running Ethereal on HP-UX) that they don't have a version of libpcap that properly handles HP-UX (libpcap 0.6.x and later *************** *** 1018,1027 **** "Please check to make sure you have sufficient permissions, and that\n" "you have the proper interface specified.%s", open_err_str, libpcap_warn); #endif goto error; } ! if (cfile.cfilter) { /* A capture filter was specified; set it up. */ if (pcap_lookupnet(cfile.iface, &netnum, &netmask, lookup_net_err_str) < 0) { /* --- 1062,1081 ---- "Please check to make sure you have sufficient permissions, and that\n" "you have the proper interface specified.%s", open_err_str, libpcap_warn); #endif + } + /* + * Else pipe (or file) does exist and pipe_open_live() has + * filled in errmsg + */ goto error; + } else + /* pipe_open_live() succeeded; don't want + error message from pcap_open_live() */ + open_err_str[0] = '\0'; } ! ! if (cfile.cfilter && !ld.from_pipe) { /* A capture filter was specified; set it up. */ if (pcap_lookupnet(cfile.iface, &netnum, &netmask, lookup_net_err_str) < 0) { /* *************** *** 1045,1052 **** } } ! ld.linktype = wtap_pcap_encap_to_wtap_encap(get_pcap_linktype(ld.pch, ! cfile.iface)); if (cfile.save_file != NULL) { /* Set up to write to the capture file. */ if (ld.linktype == WTAP_ENCAP_UNKNOWN) { --- 1099,1116 ---- } } ! /* Set up to write to the capture file. */ ! #ifndef _WIN32 ! if (ld.from_pipe) { ! pcap_encap = hdr.network; ! file_snaplen = hdr.snaplen; ! } else ! #endif ! { ! pcap_encap = get_pcap_linktype(ld.pch, cfile.iface); ! file_snaplen = pcap_snapshot(ld.pch); ! } ! ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_encap); if (cfile.save_file != NULL) { /* Set up to write to the capture file. */ if (ld.linktype == WTAP_ENCAP_UNKNOWN) { *************** *** 1121,1126 **** --- 1185,1221 ---- } else ld.go = FALSE; while (ld.go) { + + #ifndef _WIN32 + if (ld.from_pipe) { + FD_ZERO(&set1); + FD_SET(pipe_fd, &set1); + timeout.tv_sec = 0; + /* timeout.tv_usec = CAP_READ_TIMEOUT*1000; */ + sel_ret = select(pipe_fd+1, &set1, NULL, NULL, &timeout); + if (sel_ret <= 0) { + inpkts = 0; + if (sel_ret < 0 && errno != EINTR) { + snprintf(errmsg, sizeof(errmsg), + "Unexpected error from select: %s", strerror(errno)); + /* popup_errmsg(errmsg); */ + ld.go = FALSE; + } + } else { + /* + * "select()" says we can read from the pipe without blocking + */ + inpkts = pipe_dispatch(pipe_fd, &ld, &hdr, &rechdr, pcap_data, + errmsg, sizeof errmsg); + if (inpkts < 0) { + ld.go = FALSE; + } + } + } + else + #endif + { + /* We need to be careful with automatic variables defined in the outer scope which are changed inside the loop. Most compilers don't try to roll them back to their original values after the *************** *** 1145,1152 **** that it might be clobbered, and we'd need to give it the volatile attribute to suppress the warning. */ - int loop_err = 0; - int packet_count_prev = 0; if (cnd_stop_capturesize == NULL && cnd_stop_timeout == NULL) { /* We're not stopping at a particular capture file size, and we're --- 1240,1245 ---- *************** *** 1186,1192 **** } else if (cnd_stop_timeout != NULL && cnd_eval(cnd_stop_timeout)) { /* The specified capture time has elapsed; stop the capture. */ ld.go = FALSE; ! } else if (inpkts > 0) { if (capture_opts.autostop_count != 0 && ld.packet_count >= capture_opts.autostop_count) { /* The specified number of packets have been captured and have --- 1279,1287 ---- } else if (cnd_stop_timeout != NULL && cnd_eval(cnd_stop_timeout)) { /* The specified capture time has elapsed; stop the capture. */ ld.go = FALSE; ! } ! } ! if (inpkts > 0) { if (capture_opts.autostop_count != 0 && ld.packet_count >= capture_opts.autostop_count) { /* The specified number of packets have been captured and have *************** *** 1200,1205 **** --- 1295,1301 ---- its maximum size. */ if (capture_opts.ringbuffer_on) { /* Switch to the next ringbuffer file */ + int loop_err = 0; if (ringbuf_switch_file(&cfile, &ld.pdh, &loop_err)) { /* File switch succeeded: reset the condition */ cnd_reset(cnd_stop_capturesize); *************** *** 1214,1219 **** --- 1310,1316 ---- } } if (ld.output_to_pipe) { + int packet_count_prev = 0; if (ld.packet_count > packet_count_prev) { if (fflush(wtap_dump_file(ld.pdh))) { volatile_err = errno; *************** *** 1264,1269 **** --- 1361,1380 ---- show_capture_file_io_error(cfile.save_file, err, TRUE); } + #ifndef _WIN32 + /* + * XXX We exhibit different behaviour between normal mode and sync mode + * when the pipe is stdin and not already at EOF. If we're a child, the + * parent's stdin isn't closed, so if the user starts another capture, + * pipe_open_live() will very likely not see the expected magic bytes and + * will say "Unrecognized libpcap format". On the other hand, in normal + * mode, pipe_open_live() will say "End of file on pipe during open". + */ + if (ld.from_pipe && pipe_fd >= 0) + close(pipe_fd); + else + #endif + { /* Get the capture statistics, and, if any packets were dropped, report that. */ if (pcap_stats(ld.pch, &stats) >= 0) { *************** *** 1279,1285 **** report_counts(); pcap_close(ld.pch); ! return TRUE; error: --- 1390,1396 ---- report_counts(); pcap_close(ld.pch); ! } return TRUE; error: *************** *** 1289,1296 **** --- 1400,1415 ---- g_free(cfile.save_file); cfile.save_file = NULL; fprintf(stderr, "tethereal: %s\n", errmsg); + #ifndef WIN32 + if (ld.from_pipe) { + if (pipe_fd >= 0) + close(pipe_fd); + } else + #endif + { if (ld.pch != NULL) pcap_close(ld.pch); + } return FALSE; } *************** *** 2139,2142 **** --- 2258,2573 ---- fname); fprintf(stderr, "tethereal: %s\n", err_msg); return (err); + } + + /* + * Timeout, in milliseconds, for reads from the stream of captured packets. + */ + #define CAP_READ_TIMEOUT 250 + + /* Take carre of byte order in the libpcap headers read from pipes. + * (function taken from wiretap/libpcap.c) */ + 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); + } + + /* In file format version 2.3, the "incl_len" and "orig_len" fields were + swapped, in order to match the BPF header layout. + + Unfortunately, some files were, according to a comment in the "libpcap" + source, written with version 2.3 in their headers but without the + interchanged fields, so if "incl_len" is greater than "orig_len" - which + would make no sense - we assume that we need to swap them. */ + 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 + * We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the + * header. + * N.B. : we can't read the libpcap formats used in RedHat 6.1 or SuSE 6.3 + * because we can't seek on pipes (see wiretap/libpcap.c for details) */ + static int + pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, + char *errmsg, int errmsgl) + { + struct stat pipe_stat; + int fd; + guint32 magic; + int b, sel_ret; + unsigned int bytes_read; + fd_set rfds; + struct timeval timeout; + + /* + * XXX Ethereal blocks until we return + */ + if (strcmp(pipename, "-") == 0) + fd = 0; /* read from stdin */ + else { + if (stat(pipename, &pipe_stat) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + ld->pipe_err = PIPNEXIST; + else { + snprintf(errmsg, errmsgl, + "The capture session could not be initiated " + "due to error on pipe: %s", strerror(errno)); + ld->pipe_err = PIPERR; + } + return -1; + } + if (! S_ISFIFO(pipe_stat.st_mode)) { + if (S_ISCHR(pipe_stat.st_mode)) { + /* + * Assume the user specified an interface on a system where + * interfaces are in /dev. Pretend we haven't seen it. + */ + ld->pipe_err = PIPNEXIST; + } else { + snprintf(errmsg, errmsgl, + "The capture session could not be initiated because\n" + "\"%s\" is neither an interface nor a pipe", pipename); + ld->pipe_err = PIPERR; + } + return -1; + } + fd = open(pipename, O_RDONLY | O_NONBLOCK); + if (fd == -1) { + snprintf(errmsg, errmsgl, + "The capture session could not be initiated " + "due to error on pipe open: %s", strerror(errno)); + ld->pipe_err = PIPERR; + return -1; + } + } + + ld->from_pipe = TRUE; + + /* read the pcap header */ + FD_ZERO(&rfds); + bytes_read = 0; + while (bytes_read < sizeof magic) { + FD_SET(fd, &rfds); + timeout.tv_sec = 0; + timeout.tv_usec = CAP_READ_TIMEOUT*1000; + sel_ret = select(fd+1, &rfds, NULL, NULL, &timeout); + if (sel_ret < 0) { + snprintf(errmsg, errmsgl, + "Unexpected error from select: %s", strerror(errno)); + goto error; + } else if (sel_ret > 0) { + b = read(fd, &magic+bytes_read, sizeof magic-bytes_read); + if (b <= 0) { + if (b == 0) + snprintf(errmsg, errmsgl, "End of file on pipe during open"); + else + snprintf(errmsg, errmsgl, "Error on pipe during open: %s", + strerror(errno)); + goto error; + } + bytes_read += b; + } + } + + switch (magic) { + case PCAP_MAGIC: + /* Host that wrote it has our byte order, and was running + a program using either standard or ss990417 libpcap. */ + 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 either ss990915 or ss991029 libpcap. */ + ld->byte_swapped = FALSE; + ld->modified = TRUE; + break; + case PCAP_SWAPPED_MAGIC: + /* Host that wrote it has a byte order opposite to ours, + and was running a program using either standard or + ss990417 libpcap. */ + 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 either ss990915 + or ss991029 libpcap. */ + ld->byte_swapped = TRUE; + ld->modified = TRUE; + break; + default: + /* Not a "libpcap" type we know about. */ + snprintf(errmsg, errmsgl, "Unrecognized libpcap format"); + goto error; + } + + /* Read the rest of the header */ + bytes_read = 0; + while (bytes_read < sizeof(struct pcap_hdr)) { + FD_SET(fd, &rfds); + timeout.tv_sec = 0; + timeout.tv_usec = CAP_READ_TIMEOUT*1000; + sel_ret = select(fd+1, &rfds, NULL, NULL, &timeout); + if (sel_ret < 0) { + snprintf(errmsg, errmsgl, + "Unexpected error from select: %s", strerror(errno)); + goto error; + } else if (sel_ret > 0) { + b = read(fd, ((char *)hdr)+bytes_read, + sizeof(struct pcap_hdr) - bytes_read); + if (b <= 0) { + if (b == 0) + snprintf(errmsg, errmsgl, "End of file on pipe during open"); + else + snprintf(errmsg, errmsgl, "Error on pipe during open: %s", + strerror(errno)); + goto error; + } + 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) { + snprintf(errmsg, errmsgl, "Unable to read old libpcap format"); + goto error; + } + + ld->pipe_state = STATE_EXPECT_REC_HDR; + ld->pipe_err = PIPOK; + return fd; + + error: + ld->pipe_err = PIPERR; + close(fd); + return -1; + + } + /* We read one record from the pipe, take care of byte order in the record + * header, write the record in the capture file, and update capture statistics. */ + + static int + pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr, + struct pcaprec_modified_hdr *rechdr, guchar *data, + char *errmsg, int errmsgl) + { + struct pcap_pkthdr phdr; + int b; + enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR, + PD_ERR } result; + + switch (ld->pipe_state) { + + case STATE_EXPECT_REC_HDR: + ld->bytes_to_read = ld->modified ? + sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr); + ld->bytes_read = 0; + ld->pipe_state = STATE_READ_REC_HDR; + /* Fall through */ + + case STATE_READ_REC_HDR: + b = read(fd, ((char *)rechdr)+ld->bytes_read, + ld->bytes_to_read - ld->bytes_read); + if (b <= 0) { + if (b == 0) + result = PD_PIPE_EOF; + else + result = PD_PIPE_ERR; + break; + } + if ((ld->bytes_read += b) < ld->bytes_to_read) + return 0; + result = PD_REC_HDR_READ; + break; + + case STATE_EXPECT_DATA: + ld->bytes_read = 0; + ld->pipe_state = STATE_READ_DATA; + /* Fall through */ + + case STATE_READ_DATA: + b = read(fd, data+ld->bytes_read, rechdr->hdr.incl_len - ld->bytes_read); + if (b <= 0) { + if (b == 0) + result = PD_PIPE_EOF; + else + result = PD_PIPE_ERR; + break; + } + if ((ld->bytes_read += b) < rechdr->hdr.incl_len) + return 0; + result = PD_DATA_READ; + break; + + default: + snprintf(errmsg, errmsgl, "pipe_dispatch: invalid state"); + result = PD_ERR; + + } /* switch (ld->pipe_state) */ + + /* + * We've now read as much data as we were expecting, so process it. + */ + switch (result) { + + case PD_REC_HDR_READ: + /* We've read the header. Take care of byte order. */ + adjust_header(ld, hdr, &rechdr->hdr); + if (rechdr->hdr.incl_len > WTAP_MAX_PACKET_SIZE) { + snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)", + ld->counts.total+1, rechdr->hdr.incl_len); + break; + } + ld->pipe_state = STATE_EXPECT_DATA; + return 0; + + case PD_DATA_READ: + /* Fill in a "struct pcap_pkthdr", and process the packet. */ + phdr.ts.tv_sec = rechdr->hdr.ts_sec; + phdr.ts.tv_usec = rechdr->hdr.ts_usec; + phdr.caplen = rechdr->hdr.incl_len; + phdr.len = rechdr->hdr.orig_len; + + capture_pcap_cb((guchar *)ld, &phdr, data); + + ld->pipe_state = STATE_EXPECT_REC_HDR; + return 1; + + case PD_PIPE_EOF: + ld->pipe_err = PIPEOF; + return -1; + + case PD_PIPE_ERR: + snprintf(errmsg, errmsgl, "Error reading from pipe: %s", + strerror(errno)); + /* Fall through */ + case PD_ERR: + break; + } + + ld->pipe_err = PIPERR; + /* Return here rather than inside the switch to prevent GCC warning */ + return -1; }
- Follow-Ups:
- Re: [Ethereal-dev] tetheral and -i - (stdin) option ...
- From: Guy Harris
- Re: [Ethereal-dev] tetheral and -i - (stdin) option ...
- References:
- [Ethereal-dev] tetheral and -i - (stdin) option ...
- From: rmkml
- Re: [Ethereal-dev] tetheral and -i - (stdin) option ...
- From: Guy Harris
- [Ethereal-dev] tetheral and -i - (stdin) option ...
- Prev by Date: [Ethereal-dev] Administrivia: Out of the office mails
- Next by Date: [Ethereal-dev] Question about stdin on ethereal win32.
- Previous by thread: Re: [Ethereal-dev] tetheral and -i - (stdin) option ...
- Next by thread: Re: [Ethereal-dev] tetheral and -i - (stdin) option ...
- Index(es):