浏览代码

patch to ngrep introduced

Jiri Kuthan 23 年之前
父节点
当前提交
be0b8a5e84
共有 5 个文件被更改,包括 1490 次插入0 次删除
  1. 3 0
      utils/ngrep/README
  2. 441 0
      utils/ngrep/ngrep.8
  3. 883 0
      utils/ngrep/ngrep.c
  4. 51 0
      utils/ngrep/ngrep.h
  5. 112 0
      utils/ngrep/show_eol.patch

+ 3 - 0
utils/ngrep/README

@@ -0,0 +1,3 @@
+these patches to ngrep allow ngrep to display messages
+in line by line manner, good for postprocessing of textual
+protocols

+ 441 - 0
utils/ngrep/ngrep.8

@@ -0,0 +1,441 @@
+.\" $Id$
+.\" 
+.\" All content, except portions of the bpf filter explanation, are:
+.\"
+.\" Copyright (c) 2001  Jordan Ritter <[email protected]> 
+.\"
+.\" Please refer to the COPYRIGHT file for more information.  
+
+.TH NGREP 8 "December 2001" Linux "User Manuals"
+
+.SH NAME
+
+ngrep \- network grep
+
+.SH SYNOPSIS
+
+.B ngrep <-hXViwqpevxlDtT> <-IO 
+.I pcap_dump
+.B > < -n
+.I num
+.B > < -d 
+.I dev
+.B > < -A 
+.I num
+.B > < -s 
+.I snaplen
+.B > < 
+.I match expression
+.B > < 
+.I bpf filter
+.B >
+
+.SH DESCRIPTION
+
+ngrep strives to provide most of GNU grep's common features, applying
+them to the network layer.  ngrep is a pcap-aware tool that will allow
+you to specify extended regular expressions to match against data
+payloads of packets.  It currently recognizes TCP, UDP and ICMP across
+Ethernet, PPP, SLIP, FDDI and null interfaces, and understands bpf
+filter logic in the same fashion as more common packet sniffing tools,
+such as
+.BR tcpdump (8)
+and 
+.BR snoop (1).
+
+
+.SH OPTIONS
+.IP -h
+Display help/usage information.
+
+.IP -L
+Display captured packets in line-by-line manner. Good for post-processing
+of textual protocols.
+
+.IP -X
+Treat the match expression as a hexadecimal string.  See the
+explanation of \fImatch expression\fP below.
+
+.IP -V
+Display version information.
+
+.IP -i 
+Ignore case for the regex expression. 
+
+.IP -w
+Match the regex expression as a word.
+
+.IP -q 
+Be quiet; don't output any information other than packet headers and
+their payloads (if relevant).
+
+.IP -p
+Don't put the interface into promiscuous mode.
+
+.IP -e 
+Show empty packets.  Normally empty packets are discarded because they
+have no payload to search.  If specified, empty packets will be shown,
+regardless of the specified regex expression.
+
+.IP -v 
+Invert the match; only display packets that don't match.
+
+.IP -x
+Dump packet contents as hexadecimal as well as ASCII.
+
+.IP -l
+Make stdout line buffered.
+
+.IP -D
+When reading pcap_dump files, replay them at their recorded time
+intervals (mimic realtime).  
+
+.IP -t
+Print a timestamp in the form of YYYY/MM/DD HH:MM:SS.UUUUUU everytime
+a packet is matched.
+
+.IP -T 
+Print a timestamp in the form of +S.UUUUUU, indicating the delta 
+between packet matches.
+
+.IP "-s snaplen"
+Set the bpf caplen to snaplen (default 65536).
+
+.IP "-I pcap_dump"
+Input file pcap_dump into ngrep.  Works with any pcap-compatible dump
+file format.  This option is useful for searching for a wide range of
+different patterns over the same packet stream.
+
+.IP "-O pcap_dump"
+Output matched packets to a pcap-compatible dump file.  This feature
+does not interfere with normal output to stdout.
+
+.IP "-n num"
+Match only
+.I \fInum\fP
+packets total, then exit.
+
+.IP "-d dev"
+By default ngrep will select a default interface to listen on.  Use
+this option to force ngrep to listen on interface \fIdev\fP.
+
+.IP "-A num"
+Dump \fInum\fP packets of trailing context after matching a packet. 
+
+.IP "\fI match expression\fP"
+A match expression is either an extended regular expression, or if the
+\fI-X\fP option is specified, a string signifying a hexadecimal value.
+An extended regular expression follows the rules as implemented by the
+.B GNU regex 
+.BR library .
+Hexadecimal expressions can optionally be preceded by `0x'.  E.g.,
+`DEADBEEF', `0xDEADBEEF'.
+
+.IP "\fI bpf filter\fP"
+Selects a filter that specifies what packets will be dumped.  If no
+\fIbpf filter\fP is given, all IP packets seen on the selected
+interface will be dumped.  Otherwise, only packets for which \fIbpf
+filter\fP is `true' will be dumped.
+.LP
+The \fIbpf filter\fP consists of one or more
+.I primitives.
+Primitives usually consist of an
+.I id
+(name or number) preceded by one or more qualifiers.  There are three
+different kinds of qualifier:
+.IP \fItype\fP
+qualifiers say what kind of thing the id name or number refers to.
+Possible types are
+.BR host ,
+.B net
+and
+.BR port .
+E.g., `host blort', `net 1.2.3', `port 80'.  If there is no type
+qualifier,
+.B host
+is assumed.
+.IP \fIdir\fP
+qualifiers specify a particular transfer direction to and/or from
+.I id.
+Possible directions are
+.BR src ,
+.BR dst ,
+.B "src or dst"
+and
+.B "src and"
+.BR dst .
+E.g., `src foo', `dst net 1.2.3', `src or dst port ftp-data'.  If
+there is no dir qualifier,
+.B "src or dst"
+is assumed.
+For `null' link layers (i.e. point to point protocols such as slip) the
+.B inbound
+and
+.B outbound
+qualifiers can be used to specify a desired direction.
+.IP \fIproto\fP
+qualifiers are restricted to ip-only protocols.  Possible protos are:
+.B tcp ,
+.B udp
+and
+.BR icmp .
+e.g., `udp src foo' or `tcp port 21'.  If there is no proto qualifier,
+all protocols consistent with the type are assumed.  E.g., `src foo'
+means `ip and ((tcp or udp) src foo)', `net bar' means `ip and (net
+bar)', and `port 53' means `ip and ((tcp or udp) port 53)'.
+.LP
+In addition to the above, there are some special `primitive' keywords
+that don't follow the pattern:
+.BR gateway ,
+.BR broadcast ,
+.BR less ,
+.B greater
+and arithmetic expressions.  All of these are described below.
+.LP
+More complex filter expressions are built up by using the words
+.BR and ,
+.B or
+and
+.B not
+to combine primitives.  E.g., `host blort and not port ftp and not
+port ftp-data'.  To save typing, identical qualifier lists can be
+omitted.  E.g., `tcp dst port ftp or ftp-data or domain' is exactly
+the same as `tcp dst port ftp or tcp dst port ftp-data or tcp dst port
+domain'.
+.LP
+Allowable primitives are:
+
+.IP "\fBdst host \fIhost\fR"
+True if the IP destination field of the packet is \fIhost\fP,
+which may be either an address or a name.
+
+.IP "\fBsrc host \fIhost\fR"
+True if the IP source field of the packet is \fIhost\fP.
+
+.IP "\fBhost \fIhost\fP"
+True if either the IP source or destination of the packet is \fIhost\fP.
+Any of the above host expressions can be prepended with the keywords,
+\fBip\fP, \fBarp\fP, or \fBrarp\fP as in:
+.in +.5i
+.nf
+\fBip host \fIhost\fR
+.fi
+.in -.5i
+which is equivalent to:
+.in +.5i
+
+
+.IP "\fBether dst \fIehost\fP"
+True if the ethernet destination address is \fIehost\fP.  \fIEhost\fP
+may be either a name from /etc/ethers or a number (see
+.IR ethers (3N)
+for numeric format).
+.IP "\fBether src \fIehost\fP"
+True if the ethernet source address is \fIehost\fP.
+.IP "\fBether host \fIehost\fP"
+True if either the ethernet source or destination address is \fIehost\fP.
+
+.IP "\fBgateway\fP \fIhost\fP"
+True if the packet used \fIhost\fP as a gateway.  I.e., the ethernet
+source or destination address was \fIhost\fP but neither the IP source
+nor the IP destination was \fIhost\fP.  \fIHost\fP must be a name and
+must be found in both /etc/hosts and /etc/ethers.  (An equivalent
+expression is
+.in +.5i
+.nf
+\fBether host \fIehost \fBand not host \fIhost\fR
+.fi
+.in -.5i
+which can be used with either names or numbers for \fIhost / ehost\fP.)
+
+.IP "\fBdst net \fInet\fR"
+True if the IP destination address of the packet has a network
+number of \fInet\fP. \fINet\fP may be either a name from /etc/networks
+or a network number (see \fInetworks(4)\fP for details).
+
+.IP "\fBsrc net \fInet\fR"
+True if the IP source address of the packet has a network
+number of \fInet\fP.
+
+.IP "\fBnet \fInet\fR"
+True if either the IP source or destination address of the packet has a network
+number of \fInet\fP.
+
+.IP "\fBnet \fInet\fR \fBmask \fImask\fR"
+True if the IP address matches \fInet\fR with the specific netmask.
+May be qualified with \fBsrc\fR or \fBdst\fR.
+
+.IP "\fBnet \fInet\fR/\fIlen\fR"
+True if the IP address matches \fInet\fR a netmask \fIlen\fR bits wide.
+May be qualified with \fBsrc\fR or \fBdst\fR.
+
+.IP "\fBdst port \fIport\fR"
+True if the packet is ip/tcp or ip/udp and has a
+destination port value of \fIport\fP.
+The \fIport\fP can be a number or a name used in /etc/services (see
+.IR tcp (4P)
+and
+.IR udp (4P)).
+If a name is used, both the port
+number and protocol are checked.  If a number or ambiguous name is used,
+only the port number is checked (e.g., \fBdst port 513\fR will print both
+tcp/login traffic and udp/who traffic, and \fBport domain\fR will print
+both tcp/domain and udp/domain traffic).
+
+.IP "\fBsrc port \fIport\fR"
+True if the packet has a source port value of \fIport\fP.
+
+.IP "\fBport \fIport\fR"
+True if either the source or destination port of the packet is \fIport\fP.
+Any of the above port expressions can be prepended with the keywords,
+\fBtcp\fP or \fBudp\fP, as in:
+.in +.5i
+.nf
+\fBtcp src port \fIport\fR
+.fi
+.in -.5i
+which matches only tcp packets whose source port is \fIport\fP.
+
+.IP "\fBless \fIlength\fR"
+True if the packet has a length less than or equal to \fIlength\fP.
+This is equivalent to:
+.in +.5i
+.nf
+\fBlen <= \fIlength\fP.
+.fi
+.in -.5i
+
+.IP "\fBgreater \fIlength\fR"
+True if the packet has a length greater than or equal to \fIlength\fP.
+This is equivalent to:
+.in +.5i
+.nf
+\fBlen >= \fIlength\fP.
+.fi
+.in -.5i
+
+.IP "\fBip proto \fIprotocol\fR"
+True if the packet is an ip packet (see
+.IR ip (4P))
+of protocol type \fIprotocol\fP.  \fIProtocol\fP can be a number or
+one of the names \fItcp\fP, \fIudp\fP or \fIicmp\fP.  Note that the
+identifiers \fItcp\fP and \fIudp\fP are also keywords and must be
+escaped via backslash (\\), which is \\\\ in the C-shell.
+
+.IP "\fBip broadcast\fR"
+True if the packet is an IP broadcast packet.  It checks for both
+the all-zeroes and all-ones broadcast conventions, and looks up
+the local subnet mask.
+
+.IP "\fBip multicast\fR"
+True if the packet is an IP multicast packet.
+
+.IP "\fBip\fR"
+Abbreviation for:
+.in +.5i
+.nf
+\fBether proto ip\fR
+.fi
+.IP  "\fBtcp\fR, \fBudp\fR, \fBicmp\fR"
+Abbreviations for:
+.in +.5i
+.nf
+\fBip proto \fIp\fR
+.fi
+.in -.5i
+where \fIp\fR is one of the above protocols.
+.IP  "\fIexpr relop expr\fR"
+True if the relation holds, where \fIrelop\fR is one of >, <, >=, <=, =, !=,
+and \fIexpr\fR is an arithmetic expression composed of integer constants
+(expressed in standard C syntax), the normal binary operators
+[+, -, *, /, &, |], a length operator, and special packet data accessors.
+To access
+data inside the packet, use the following syntax:
+.in +.5i
+.nf
+\fIproto\fB [ \fIexpr\fB : \fIsize\fB ]\fR
+.fi
+.in -.5i
+\fIProto\fR is one of \fBip, tcp, udp \fRor \fBicmp\fR, and
+indicates the protocol layer for the index operation.  The byte
+offset, relative to the indicated protocol layer, is given by
+\fIexpr\fR.  \fISize\fR is optional and indicates the number of bytes
+in the field of interest; it can be either one, two, or four, and
+defaults to one.  The length operator, indicated by the keyword
+\fBlen\fP, gives the length of the packet.
+
+For example, `\fBether[0] & 1 != 0\fP' catches all multicast traffic.
+The expression `\fBip[0] & 0xf != 5\fP'
+catches all IP packets with options. The expression
+`\fBip[6:2] & 0x1fff = 0\fP'
+catches only unfragmented datagrams and frag zero of fragmented datagrams.
+This check is implicitly applied to the \fBtcp\fP and \fBudp\fP
+index operations.
+For instance, \fBtcp[0]\fP always means the first
+byte of the TCP \fIheader\fP, and never means the first byte of an
+intervening fragment.
+.LP
+Primitives may be combined using:
+.IP
+A parenthesized group of primitives and operators
+(parentheses are special to the Shell and must be escaped).
+.IP
+Negation (`\fB!\fP' or `\fBnot\fP').
+.IP
+Concatenation (`\fB&&\fP' or `\fBand\fP').
+.IP
+Alternation (`\fB||\fP' or `\fBor\fP').
+.LP
+Negation has highest precedence.
+Alternation and concatenation have equal precedence and associate
+left to right.  Note that explicit \fBand\fR tokens, not juxtaposition,
+are now required for concatenation.
+.LP
+If an identifier is given without a keyword, the most recent keyword
+is assumed.
+For example,
+.in +.5i
+.nf
+\fBnot host vs and ace\fR
+.fi
+.in -.5i
+is short for
+.in +.5i
+.nf
+\fBnot host vs and host ace\fR
+.fi
+.in -.5i
+which should not be confused with
+.in +.5i
+.nf
+\fBnot ( host vs or ace )\fR
+.fi
+.in -.5i
+.LP
+Expression arguments can be passed to ngrep as either a single
+argument or as multiple arguments, whichever is more convenient.
+Generally, if the expression contains Shell metacharacters, it is
+easier to pass it as a single, quoted argument.  Multiple arguments
+are concatenated with spaces before being parsed.
+
+
+
+.SH DIAGNOSTICS
+
+Errors from 
+.B ngrep, libpcap,
+and the
+.B GNU regex library
+are all output to stderr. 
+
+.SH AUTHOR
+
+Written by Jordan Ritter <[email protected]>.
+
+.SH REPORTING BUGS
+
+Send bug reports to the author.
+
+.SH NOTES
+
+ALL YOUR BASE ARE BELONG TO US.

+ 883 - 0
utils/ngrep/ngrep.c

@@ -0,0 +1,883 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 2001  Jordan Ritter <[email protected]>
+ *
+ * Please refer to the COPYRIGHT file for more information. 
+ * 
+ */
+
+#if defined(BSD) || defined(SOLARIS) || defined(MACOSX)
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <net/if.h>
+#include <sys/tty.h>
+#endif
+
+#if defined(OSF1)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <net/route.h>
+#include <sys/mbuf.h>
+#endif
+
+#if defined(LINUX)
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <time.h>
+#endif
+
+#if defined(AIX)
+#include <sys/machine.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <time.h>
+#endif
+
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+#include <pcap.h>
+#include <net/bpf.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+
+#include <locale.h>
+
+#ifdef USE_PCRE
+#include "pcre-3.4/pcre.h"
+#else
+#include "regex-0.12/regex.h"
+#endif
+
+#include "ngrep.h"
+
+#define dump(_a,_b) ( show_eol ? \
+	dump_line_by_line((_a),(_b)) : _dump((_a),(_b)) )
+
+
+static char rcsver[] = "$Revision$";
+
+int snaplen = 65535, promisc = 1, to = 1000;
+int show_empty = 0, show_hex = 0, quiet = 0;
+int match_after = 0, keep_matching = 0;
+int invert_match = 0, bin_match = 0;
+int matches = 0, max_matches = 0;
+int live_read = 1, want_delay = 0;
+
+int show_eol=0;
+
+char pc_err[PCAP_ERRBUF_SIZE];
+#ifdef USE_PCRE
+int err_offset;
+char *re_err = NULL;
+#else
+const char *re_err = NULL;
+#endif
+
+int re_match_word = 0, re_ignore_case = 0;
+
+#ifdef USE_PCRE
+pcre *pattern = NULL;
+pcre_extra *pattern_extra = NULL;
+#else 
+struct re_pattern_buffer pattern;
+#endif
+
+char *match_data = NULL, *bin_data = NULL, *filter = NULL;
+int (*match_func)() = &blank_match_func;
+int match_len = 0;
+
+struct bpf_program pcapfilter;
+struct in_addr net, mask;
+pcap_t *pd = NULL;
+char *dev = NULL;
+int link_offset;
+
+char *read_file = NULL, *dump_file = NULL;
+pcap_dumper_t *pd_dump = NULL;
+
+struct timeval prev_ts = {0, 0}, prev_delay_ts = {0,0};
+void (*print_time)() = NULL, (*dump_delay)() = dump_delay_proc_init;
+
+unsigned ws_row, ws_col;
+
+
+int main(int argc, char **argv) {
+  int c;
+
+  signal(SIGINT,  clean_exit);
+  signal(SIGQUIT, clean_exit);
+  signal(SIGABRT, clean_exit);
+  signal(SIGPIPE, clean_exit);
+  signal(SIGWINCH, update_windowsize);
+
+  setlocale(LC_ALL, "");
+
+  while ((c = getopt(argc, argv, "LhXViwqpevxlDtTs:n:d:A:I:O:")) != EOF) {
+    switch (c) {
+	case 'L':
+		show_eol=1;
+		break;
+    case 'I':  
+      read_file = optarg;
+      break;
+    case 'O':
+      dump_file = optarg;
+      break;
+    case 'A': 
+      match_after = atoi(optarg) + 1;
+      break;
+    case 'd': 
+      dev = optarg;
+      break;
+    case 'n':
+      max_matches = atoi(optarg);
+      break;
+    case 's':
+      snaplen = atoi(optarg);
+      break;
+    case 'T':
+      print_time = &print_time_diff_init;
+      break;
+    case 't':
+      print_time = &print_time_absolute;
+      break;
+    case 'D':
+      want_delay = 1;
+      break;
+    case 'l':
+      setvbuf(stdout, NULL, _IOLBF, 0);
+      break;
+    case 'x':
+      show_hex++;
+      break;
+    case 'v':
+      invert_match++;
+      break;
+    case 'e':
+      show_empty++;
+      break;
+    case 'p':
+      promisc = 0;
+      break;
+    case 'q':
+      quiet++;
+      break;
+    case 'w':
+      re_match_word++;
+      break;
+    case 'i':
+      re_ignore_case++;
+      break;
+    case 'V':
+      version();
+    case 'X':
+      bin_match++;
+      break;
+    case 'h':
+      usage(0);
+    default:
+      usage(-1);
+    }
+  }
+
+
+  if (argv[optind]) 
+    match_data = argv[optind++];
+
+  if (read_file) {
+    if (!(pd = pcap_open_offline(read_file, pc_err))) {
+      perror(pc_err);
+      clean_exit(-1);
+    }
+
+    live_read = 0;
+    printf("input: %s\n", read_file);
+
+  } else {
+    if (!dev)
+      if (!(dev = pcap_lookupdev(pc_err))) {
+	perror(pc_err);
+	clean_exit(-1);
+      }
+    
+    if ((pd = pcap_open_live(dev, snaplen, promisc, to, pc_err)) == NULL) {
+      perror(pc_err);
+      clean_exit(-1);
+    }
+
+    if (pcap_lookupnet(dev, &net.s_addr, &mask.s_addr, pc_err) == -1) {
+      perror(pc_err);
+      memset(&net, 0, sizeof(net));
+      memset(&mask, 0, sizeof(mask));
+    } 
+
+    if (!quiet) {
+      printf("interface: %s", dev);
+      if (net.s_addr && mask.s_addr) {
+	printf(" (%s/", inet_ntoa(net));
+	printf("%s)", inet_ntoa(mask)); 
+      }
+      printf("\n");
+    }
+  }
+
+
+  if (argv[optind]) {
+    filter = get_filter(&argv[optind]); 
+
+    if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) {
+      free(filter); 
+      filter = get_filter(&argv[optind-1]); 
+
+#ifdef NEED_RESTART
+      PCAP_RESTART();
+#endif
+      if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) {
+	pcap_perror(pd, "pcap compile");
+	clean_exit(-1);
+      } else match_data = NULL;
+    }
+
+    if (!quiet) printf("filter: %s\n", filter); 
+    
+    if (pcap_setfilter(pd, &pcapfilter)) {
+      pcap_perror(pd, "pcap set");
+      clean_exit(-1);
+    }
+  }
+
+  if (match_data) {
+    if (bin_match) {
+      int i = 0, n;
+      char *s, *d;
+      int len;
+
+      if (re_match_word || re_ignore_case) {
+	fprintf(stderr, "fatal: regex switches are incompatible with binary matching\n");
+	clean_exit(-1);
+      }
+
+      len = strlen(match_data);
+      if (len % 2 != 0 || !strishex(match_data)) {
+	fprintf(stderr, "fatal: invalid hex string specified\n");
+	clean_exit(-1);
+      }
+
+      bin_data = malloc(len / 2);
+      memset(bin_data, 0, len / 2);
+      d = bin_data;
+
+      if ((s = strchr(match_data, 'x'))) 
+	len -= ++s - match_data - 1;
+      else s = match_data;
+
+      while (i <= len) {
+	sscanf(s+i, "%2x", &n);
+	*d++ = n;
+	i += 2;
+      }
+
+      match_len = len / 2;
+      match_func = &bin_match_func;
+
+    } else {
+
+#ifdef USE_PCRE
+      int pcre_options = PCRE_UNGREEDY;
+
+      if (re_ignore_case) 
+	pcre_options |= PCRE_CASELESS;
+      
+      re_err = malloc(512);
+#else
+      re_syntax_options = RE_SYNTAX_EGREP;
+      
+      if (re_ignore_case) {
+	char *s;
+	int i;
+	
+	pattern.translate = (char*)malloc(256);
+	s = pattern.translate;
+	for (i = 0; i < 256; i++) 
+	  s[i] = i;
+	for (i = 'A'; i <= 'Z'; i++) 
+	  s[i] = i + 32;
+
+	s = match_data;
+	while (*s) 
+	  *s++ = tolower(*s);
+      } else pattern.translate = NULL;
+#endif
+
+      if (re_match_word) {
+	char *word_regex = malloc(strlen(match_data) * 3 + strlen(WORD_REGEX));
+	sprintf(word_regex, WORD_REGEX, match_data, match_data, match_data);
+	match_data = word_regex;
+      }
+
+#ifdef USE_PCRE
+      pattern = pcre_compile(match_data, pcre_options, (const char **)&re_err, &err_offset, 0);
+      if (!pattern) {
+	fprintf(stderr, "compile failed: %s\n", re_err);
+	clean_exit(-1);
+      }
+
+      pattern_extra = pcre_study(pattern, 0, (const char **)&re_err);
+      
+      free(re_err);
+      re_err = NULL;
+#else
+      re_err = re_compile_pattern(match_data, strlen(match_data), &pattern);
+      if (re_err) {
+	fprintf(stderr, "regex compile: %s\n", re_err);
+	clean_exit(-1);
+      }
+
+      pattern.fastmap = (char*)malloc(256);
+      if (re_compile_fastmap(&pattern)) {
+	perror("fastmap compile failed");
+	clean_exit(-1);
+      }
+#endif
+
+      match_func = &re_match_func;
+    }
+
+    if (!quiet && match_data && strlen(match_data)) 
+      printf("%smatch: %s%s\n", invert_match?"don't ":"", 
+	     (bin_data && !strchr(match_data, 'x'))?"0x":"", match_data);
+  }
+
+
+  if (filter) free(filter);
+  if (re_match_word) free(match_data);
+
+
+  switch(pcap_datalink(pd)) {
+  case DLT_EN10MB:
+    link_offset = ETHHDR_SIZE;
+    break;
+
+  case DLT_IEEE802:
+    link_offset = TOKENRING_SIZE;
+    break;
+    
+  case DLT_FDDI:
+    link_offset = FDDIHDR_SIZE;
+    break;
+
+  case DLT_SLIP: 
+    link_offset = SLIPHDR_SIZE;
+    break;
+    
+  case DLT_PPP:
+    link_offset = PPPHDR_SIZE;
+    break;
+
+  case DLT_RAW: 
+    link_offset = RAWHDR_SIZE;
+    break;
+
+  case DLT_LOOP:
+  case DLT_NULL:
+    link_offset = LOOPHDR_SIZE;
+    break;
+
+  case DLT_LINUX_SLL:
+    link_offset = ISDNHDR_SIZE;
+    break;
+
+  default:
+    fprintf(stderr, "fatal: unsupported interface type %d\n", pcap_datalink(pd));
+    clean_exit(-1);
+  }
+  
+  if (dump_file) {
+    if (!(pd_dump = pcap_dump_open(pd, dump_file))) {
+      fprintf(stderr, "fatal: %s\n", pcap_geterr(pd));
+      clean_exit(-1);
+    } else printf("output: %s\n", dump_file);
+  }
+
+  update_windowsize(0);
+
+  while (pcap_loop(pd, 0, (pcap_handler)process, 0));
+
+  clean_exit(0);
+}	
+
+
+void process(u_char *data1, struct pcap_pkthdr* h, u_char *p) {
+  struct ip* ip_packet = (struct ip *)(p + link_offset);
+
+#if defined(AIX)
+#undef ip_hl
+  unsigned ip_hl = ip_packet->ip_ff.ip_fhl*4;
+#else
+  unsigned ip_hl = ip_packet->ip_hl*4;
+#endif
+
+  unsigned ip_off = ntohs(ip_packet->ip_off);
+  unsigned fragmented = ip_off & (IP_MF | IP_OFFMASK);
+  unsigned frag_offset = fragmented?(ip_off & IP_OFFMASK) * 8:0;
+
+  char *data;
+  int len;
+
+  switch (ip_packet->ip_p) {
+  case IPPROTO_TCP: {
+    struct tcphdr* tcp = (struct tcphdr *)(((char *)ip_packet) + ip_hl);
+    unsigned tcphdr_offset = fragmented?0:(tcp->th_off * 4);
+
+    if (!quiet) {
+      printf("#");
+      fflush(stdout);
+    }
+    
+    data = ((char*)tcp) + tcphdr_offset;
+
+    if ((len = ntohs(ip_packet->ip_len)) < h->caplen)
+      len -= ip_hl + tcphdr_offset;
+    else len = h->caplen - link_offset - ip_hl - tcphdr_offset;
+
+    if (((len || show_empty) && (((int)(*match_func)(data, len)) != invert_match))
+	|| keep_matching) { 
+
+      if (!live_read && want_delay)
+	dump_delay(h);
+
+      printf("\nT ");
+
+      if (print_time) 
+	print_time(h);
+
+      if (tcphdr_offset || !frag_offset) {
+	printf("%s:%d -", inet_ntoa(ip_packet->ip_src), ntohs(tcp->th_sport));
+	printf("> %s:%d", inet_ntoa(ip_packet->ip_dst), ntohs(tcp->th_dport));
+	printf(" [%s%s%s%s%s%s]",
+	       (tcp->th_flags & TH_ACK)?"A":"",
+	       (tcp->th_flags & TH_SYN)?"S":"",
+	       (tcp->th_flags & TH_RST)?"R":"",
+	       (tcp->th_flags & TH_FIN)?"F":"",
+	       (tcp->th_flags & TH_URG)?"U":"",
+	       (tcp->th_flags & TH_PUSH)?"P":"");
+      } else {
+	printf("%s -", inet_ntoa(ip_packet->ip_src));
+	printf("> %s", inet_ntoa(ip_packet->ip_dst));
+      }
+
+      if (fragmented) 
+	printf(" %s%d@%d:%d\n", frag_offset?"+":"", ntohs(ip_packet->ip_id),
+                                frag_offset, len); 
+      else printf("\n");
+      
+      if (pd_dump) {
+	pcap_dump((u_char*)pd_dump, h, p);
+	if (!quiet) dump(data, len);
+      } else dump(data, len);
+    }
+  }
+  break;
+
+  case IPPROTO_UDP: {
+    struct udphdr* udp = (struct udphdr *)(((char *)ip_packet) + ip_hl);
+    unsigned udphdr_offset = (fragmented)?0:sizeof(struct udphdr); 
+
+    if (!quiet) {
+      printf("#"); 
+      fflush(stdout);
+    }
+
+    data = ((char*)udp) + udphdr_offset;
+
+    if ((len = ntohs(ip_packet->ip_len)) < h->caplen)
+      len -= ip_hl + udphdr_offset;
+    else len = h->caplen - link_offset - ip_hl - udphdr_offset;
+
+    if (((len || show_empty) && (((int)(*match_func)(data, len)) != invert_match))
+	|| keep_matching) { 
+
+      if (!live_read && want_delay)
+	dump_delay(h);
+
+      printf("\nU ");
+
+      if (print_time) 
+	print_time(h);
+
+      if (udphdr_offset || !frag_offset) {
+#ifdef HAVE_DUMB_UDPHDR
+	printf("%s:%d -", inet_ntoa(ip_packet->ip_src), ntohs(udp->source));
+	printf("> %s:%d", inet_ntoa(ip_packet->ip_dst), ntohs(udp->dest));
+#else
+	printf("%s:%d -", inet_ntoa(ip_packet->ip_src), ntohs(udp->uh_sport));
+	printf("> %s:%d", inet_ntoa(ip_packet->ip_dst), ntohs(udp->uh_dport));
+#endif
+      } else {
+	printf("%s -", inet_ntoa(ip_packet->ip_src));
+	printf("> %s", inet_ntoa(ip_packet->ip_dst));
+      }
+
+      if (fragmented) 
+	printf(" %s%d@%d:%d\n", frag_offset?"+":"", ntohs(ip_packet->ip_id),
+	       frag_offset, len); 
+      else printf("\n");
+      
+      if (pd_dump) {
+	pcap_dump((u_char*)pd_dump, h, p);
+	if (!quiet) dump(data, len);
+      } else dump(data, len);
+    }
+  }
+  break;
+
+  case IPPROTO_ICMP: {
+    struct icmp* ic = (struct icmp *)(((char *)ip_packet) + ip_hl);
+    unsigned icmphdr_offset = fragmented?0:4;
+
+    if (!quiet) {
+      printf("#"); 
+      fflush(stdout);
+    }
+
+    data = ((char*)ic) + icmphdr_offset;
+
+    if ((len = ntohs(ip_packet->ip_len)) < h->caplen)
+      len -= ip_hl + icmphdr_offset;
+    else len = h->caplen - link_offset - ip_hl - icmphdr_offset;
+
+    if (((len || show_empty) && (((int)(*match_func)(data, len)) != invert_match))
+	|| keep_matching) { 
+
+      if (!live_read && want_delay)
+	dump_delay(h);
+
+      printf("\nI ");
+
+      if (print_time) 
+	print_time(h);
+
+      printf("%s -", inet_ntoa(ip_packet->ip_src));
+      printf("> %s", inet_ntoa(ip_packet->ip_dst));
+
+      if (icmphdr_offset || !frag_offset) 
+	printf(" %d:%d", ic->icmp_type, ic->icmp_code);
+
+      if (fragmented) 
+	printf(" %s%d@%d:%d\n", frag_offset?"+":"", ntohs(ip_packet->ip_id),
+                                frag_offset, len); 
+      else printf("\n");
+
+      if (pd_dump) {
+	pcap_dump((u_char*)pd_dump, h, p);
+	if (!quiet) dump(data, len);
+      } else dump(data, len);
+    }
+  }
+  break;
+  
+  }
+
+  if (match_after && keep_matching)
+    keep_matching--;
+}
+
+
+int re_match_func(char *data, int len) {
+#ifdef USE_PCRE
+  switch(pcre_exec(pattern, 0, data, len, 0, 0, 0, 0)) {
+   case PCRE_ERROR_NULL:
+   case PCRE_ERROR_BADOPTION:
+   case PCRE_ERROR_BADMAGIC: 
+   case PCRE_ERROR_UNKNOWN_NODE: 
+   case PCRE_ERROR_NOMEMORY:
+     perror("she's dead, jim\n");
+     clean_exit(-2);
+
+   case PCRE_ERROR_NOMATCH: 
+     return 0;
+  }
+#else
+  switch (re_search(&pattern, data, len, 0, len, 0)) {
+   case -2: 
+     perror("she's dead, jim\n");
+     clean_exit(-2);
+
+   case -1:
+     return 0;
+  }
+#endif
+  
+  if (max_matches && ++matches > max_matches)
+    clean_exit(0);
+
+  if (match_after && keep_matching != match_after)
+    keep_matching = match_after;
+
+  return 1;
+}
+
+
+int bin_match_func(char *data, int len) {
+  int stop = len - match_len;
+  int i = 0;
+
+  if (stop < 0)
+    return 0;
+
+  while (i <= stop) 
+    if (!memcmp(data+(i++), bin_data, match_len)) {
+      if (max_matches && ++matches > max_matches)
+	clean_exit(0);
+
+      if (match_after && keep_matching != match_after)
+	keep_matching = match_after;
+
+      return 1;
+    }
+
+  return 0;
+}
+
+
+int blank_match_func(char *data, int len) {
+  if (max_matches && ++matches > max_matches)
+    clean_exit(0);
+
+  return 1;
+}
+
+void dump_line_by_line( char *data, int len )
+{
+	unsigned width;
+	char *c;
+	
+	c=data;
+
+	while( c< data + len) {
+		if (*c=='\n') {
+			puts("");
+		} else {
+	  		putchar(isprint(*c)?*c:'.');
+		}
+		c++;
+
+	}
+	puts("");
+
+}
+
+
+void _dump(char *data, int len) {  
+  if (len > 0) {
+    unsigned width = show_hex?16:(ws_col-5);
+    char *str = data;
+    int j, i = 0;
+
+    while (i < len) {
+      printf("  ");
+
+      if (show_hex) 
+	for (j = 0; j < width; j++) {
+	  if (i+j < len) 
+	    printf("%02x ", (unsigned char)str[j]);
+	  else printf("   ");
+
+	  if ((j+1) % (width/2) == 0)
+	    printf("   ");
+	}
+
+      for (j = 0; j < width; j++) 
+	if (i+j < len) 
+	  printf("%c", isprint(str[j])?str[j]:'.');
+	else printf(" ");
+      
+      str += width;
+      i += j;
+
+      printf("\n");
+    }
+  }
+}
+
+
+char *get_filter(char **argv) {
+  char **arg = argv, *theirs, *mine;
+  char *from, *to;
+  int len = 0;
+
+  if (!*arg)
+    return NULL;
+
+  while (*arg) 
+    len += strlen(*arg++) + 1;
+
+  if (!(theirs = (char*)malloc(len + 1)) || 
+      !(mine = (char*)malloc(len + sizeof(IP_ONLY))))
+    return NULL;
+
+  memset(theirs, 0, len + 1);
+  memset(mine, 0, len + sizeof(IP_ONLY));
+
+  arg = argv;
+  to = theirs;
+
+  while ((from = *arg++)) {
+    while ((*to++ = *from++));
+    *(to-1) = ' ';
+  }
+
+  sprintf(mine, IP_ONLY, theirs);
+
+  free(theirs);
+  return mine;
+}
+
+
+int strishex(char *str) {
+  char *s;
+  if ((s = strchr(str, 'x'))) 
+    s++;
+  else s = str;
+
+  while (*s) 
+    if (!isxdigit(*s++))
+      return 0;
+
+  return 1;
+}
+
+
+void print_time_absolute(struct pcap_pkthdr *h) {
+#ifdef MACOSX
+  struct tm *t = localtime((const time_t *)&h->ts.tv_sec);
+#else
+  struct tm *t = localtime(&h->ts.tv_sec);
+#endif
+
+  printf("%02d/%02d/%02d %02d:%02d:%02d.%06d ",
+	 t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour,
+	 t->tm_min, t->tm_sec, h->ts.tv_usec);
+}
+
+
+void print_time_diff_init(struct pcap_pkthdr *h) {
+  print_time = &print_time_diff;
+
+  prev_ts.tv_sec = h->ts.tv_sec;
+  prev_ts.tv_usec = h->ts.tv_usec;
+  
+  print_time(h);
+}
+
+void print_time_diff(struct pcap_pkthdr *h) { 
+  unsigned secs, usecs;
+
+  secs = h->ts.tv_sec - prev_ts.tv_sec;
+  if (h->ts.tv_usec >= prev_ts.tv_usec)
+    usecs = h->ts.tv_usec - prev_ts.tv_usec;
+  else {
+    secs--; 
+    usecs = 1000000 - (prev_ts.tv_usec - h->ts.tv_usec);
+  }
+
+  printf("+%d.%06d ", secs, usecs);
+
+  prev_ts.tv_sec = h->ts.tv_sec;
+  prev_ts.tv_usec = h->ts.tv_usec;
+}
+
+void dump_delay_proc_init(struct pcap_pkthdr *h) {
+  dump_delay = &dump_delay_proc;
+
+  prev_delay_ts.tv_sec = h->ts.tv_sec;
+  prev_delay_ts.tv_usec = h->ts.tv_usec;
+
+  dump_delay(h);
+}
+
+void dump_delay_proc(struct pcap_pkthdr *h) {
+  unsigned secs, usecs;
+
+  secs = h->ts.tv_sec - prev_delay_ts.tv_sec;
+  if (h->ts.tv_usec >= prev_delay_ts.tv_usec)
+    usecs = h->ts.tv_usec - prev_delay_ts.tv_usec;
+  else {
+    secs--; 
+    usecs = 1000000 - (prev_delay_ts.tv_usec - h->ts.tv_usec);
+  }
+
+  sleep(secs);
+  usleep(usecs);
+  
+  prev_delay_ts.tv_sec = h->ts.tv_sec;
+  prev_delay_ts.tv_usec = h->ts.tv_usec;
+}
+
+
+void update_windowsize(int e) {
+  const struct winsize ws;
+  
+  if (!ioctl(0, TIOCGWINSZ, &ws)) {
+    ws_row = ws.ws_row;
+    ws_col = ws.ws_col;
+  } else {
+    ws_row = 24;
+    ws_col = 80;
+  }
+}
+
+
+void usage(int e) {
+  printf("usage: ngrep <-hLXViwqpevxlDtT> <-IO pcap_dump> <-n num> <-d dev> <-A num>\n"
+	 "                               <-s snaplen> <match expression> <bpf filter>\n");
+  exit(e);
+}
+
+
+void version(void) {
+  printf("ngrep: V%s, %s\n", VERSION, rcsver);
+  exit(0);
+}
+
+
+void clean_exit(int sig) {
+  struct pcap_stat s;
+  if (!quiet && sig >= 0) printf("exit\n");
+
+#ifdef USE_PCRE
+  if (re_err) free(re_err);
+  if (pattern) pcre_free(pattern);
+  if (pattern_extra) pcre_free(pattern_extra);
+#else
+  if (pattern.translate) free(pattern.translate);
+  if (pattern.fastmap) free(pattern.fastmap);
+#endif
+
+  if (bin_data) free(bin_data);
+  
+  if (!quiet && sig >= 0 && !read_file &&
+      pd && !pcap_stats(pd, &s)) 
+    printf("%d received, %d dropped\n", s.ps_recv, s.ps_drop);
+
+  if (pd) pcap_close(pd);
+  if (pd_dump) pcap_dump_close(pd_dump);
+
+  exit(sig);
+}
+
+

+ 51 - 0
utils/ngrep/ngrep.h

@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 2001  Jordan Ritter <[email protected]>
+ *
+ * Please refer to the COPYRIGHT file for more information. 
+ *
+ */
+
+#define VERSION "1.40.1"
+
+
+#define ETHHDR_SIZE 14 
+#define TOKENRING_SIZE 22
+#define PPPHDR_SIZE 4 
+#define SLIPHDR_SIZE 16
+#define RAWHDR_SIZE 0
+#define LOOPHDR_SIZE 4 
+#define FDDIHDR_SIZE 21
+#define ISDNHDR_SIZE 16
+
+#ifndef IP_OFFMASK
+#define IP_OFFMASK 0x1fff
+#endif
+
+#define WORD_REGEX "((^%s\\W)|(\\W%s$)|(\\W%s\\W))"
+#define IP_ONLY "ip and ( %s)"
+
+
+char *get_filter(char **);
+void process(u_char *, struct pcap_pkthdr*, u_char *);
+void _dump(char *, int); 
+void dump_line_by_line(char *, int); 
+void clean_exit(int);
+void usage(int);
+void version(void);
+
+int re_match_func(char *, int); 
+int bin_match_func(char *, int);
+int blank_match_func(char *, int); 
+
+int strishex(char *);
+
+void print_time_absolute(struct pcap_pkthdr *);
+void print_time_diff_init(struct pcap_pkthdr *);
+void print_time_diff(struct pcap_pkthdr *);
+
+void dump_delay_proc_init(struct pcap_pkthdr *);
+void dump_delay_proc(struct pcap_pkthdr *);
+
+void update_windowsize(int);

+ 112 - 0
utils/ngrep/show_eol.patch

@@ -0,0 +1,112 @@
+diff -Naur ngrep.old/ngrep.8 ngrep.patched/ngrep.8
+--- ngrep.old/ngrep.8	Mon Dec 31 23:07:02 2001
++++ ngrep.patched/ngrep.8	Fri Aug 30 17:33:28 2002
+@@ -48,6 +48,10 @@
+ .IP -h
+ Display help/usage information.
+ 
++.IP -L
++Display captured packets in line-by-line manner. Good for post-processing
++of textual protocols.
++
+ .IP -X
+ Treat the match expression as a hexadecimal string.  See the
+ explanation of \fImatch expression\fP below.
+diff -Naur ngrep.old/ngrep.c ngrep.patched/ngrep.c
+--- ngrep.old/ngrep.c	Mon Dec 31 23:11:00 2001
++++ ngrep.patched/ngrep.c	Fri Aug 30 17:33:28 2002
+@@ -54,6 +54,8 @@
+ #include <signal.h>
+ #include <sys/ioctl.h>
+ 
++#include <locale.h>
++
+ #ifdef USE_PCRE
+ #include "pcre-3.4/pcre.h"
+ #else
+@@ -62,6 +64,9 @@
+ 
+ #include "ngrep.h"
+ 
++#define dump(_a,_b) ( show_eol ? \
++	dump_line_by_line((_a),(_b)) : _dump((_a),(_b)) )
++
+ 
+ static char rcsver[] = "$Revision$";
+ 
+@@ -72,6 +77,8 @@
+ int matches = 0, max_matches = 0;
+ int live_read = 1, want_delay = 0;
+ 
++int show_eol=0;
++
+ char pc_err[PCAP_ERRBUF_SIZE];
+ #ifdef USE_PCRE
+ int err_offset;
+@@ -117,8 +124,13 @@
+   signal(SIGPIPE, clean_exit);
+   signal(SIGWINCH, update_windowsize);
+ 
+-  while ((c = getopt(argc, argv, "hXViwqpevxlDtTs:n:d:A:I:O:")) != EOF) {
++  setlocale(LC_ALL, "");
++
++  while ((c = getopt(argc, argv, "LhXViwqpevxlDtTs:n:d:A:I:O:")) != EOF) {
+     switch (c) {
++	case 'L':
++		show_eol=1;
++		break;
+     case 'I':  
+       read_file = optarg;
+       break;
+@@ -649,8 +661,28 @@
+   return 1;
+ }
+ 
++void dump_line_by_line( char *data, int len )
++{
++	unsigned width;
++	char *c;
++	
++	c=data;
++
++	while( c< data + len) {
++		if (*c=='\n') {
++			puts("");
++		} else {
++	  		putchar(isprint(*c)?*c:'.');
++		}
++		c++;
++
++	}
++	puts("");
++
++}
++
+ 
+-void dump(char *data, int len) {  
++void _dump(char *data, int len) {  
+   if (len > 0) {
+     unsigned width = show_hex?16:(ws_col-5);
+     char *str = data;
+@@ -811,7 +843,7 @@
+ 
+ 
+ void usage(int e) {
+-  printf("usage: ngrep <-hXViwqpevxlDtT> <-IO pcap_dump> <-n num> <-d dev> <-A num>\n"
++  printf("usage: ngrep <-hLXViwqpevxlDtT> <-IO pcap_dump> <-n num> <-d dev> <-A num>\n"
+ 	 "                               <-s snaplen> <match expression> <bpf filter>\n");
+   exit(e);
+ }
+diff -Naur ngrep.old/ngrep.h ngrep.patched/ngrep.h
+--- ngrep.old/ngrep.h	Mon Dec 31 23:02:00 2001
++++ ngrep.patched/ngrep.h	Fri Aug 30 17:33:29 2002
+@@ -29,7 +29,8 @@
+ 
+ char *get_filter(char **);
+ void process(u_char *, struct pcap_pkthdr*, u_char *);
+-void dump(char *, int); 
++void _dump(char *, int); 
++void dump_line_by_line(char *, int); 
+ void clean_exit(int);
+ void usage(int);
+ void version(void);