1#!/usr/bin/perl -w
2#
3# Sniff traffic and format as a stream of packet contents
4#
5use strict;
6
7use Getopt::Long qw(:config permute);  # allow mixed args.
8
9# Options variables
10my $debug  = 0;
11my $saveto;
12my $readfrom;
13my $interface = 'any';
14my $dumpspec = 'tcp port 80';
15my $helpmeplease = 0;
16
17GetOptions ('debug!'      => \$debug,
18            'write=s'     => \$saveto,
19            'file=s'      => \$readfrom,
20            'interface=s' => \$interface,
21            'dumpspec=s'  => \$dumpspec,
22            'help'        => \$helpmeplease  );
23
24usage() if ( $helpmeplease );
25
26if ( defined($saveto) ) {
27  open( SAVETO, '>>', $saveto ) or die "Couldn't save to '$saveto'";
28}
29
30if ( defined($readfrom) ) {
31  if ( $readfrom ne '-' ) {
32    open( STDIN, '<', $readfrom ) or die "Couldn't open '$readfrom'";
33  }
34}
35else {
36  my @tcpdumpoptions = ('-i', $interface, '-s0', '-l', '-xx', '-n', '-q', $dumpspec );
37  open( STDIN, '-|', "tcpdump", @tcpdumpoptions ) or die "Couldn't start tcpdump process";
38}
39
40my $timestamp;
41my $source = '';
42my $dest = '';
43my $lastsource = '';
44my $lastdest = '';
45my $show;
46my $packet;
47my $stream;
48
49while( <STDIN> ) {
50  $show = 0;
51  if ( /^([012]\d:[0-5]\d:[0-5]\d\.\d{6})\sIP\s([0-9.:]+)\s>\s([0-9.:]+):\ tcp/ ) {
52    $timestamp = $1;
53    $source = $2;
54    $dest = $3;
55  }
56  elsif ( /^\s+(0x....):\s(( [0-9a-f]{4}){1,8})/i ) {
57    my $pos = hex($1);
58    my $hex = $2;
59    next unless defined($hex);
60
61    if ( $pos == 64 ) {
62      $hex = substr( $hex, 10 );
63      $pos += 4;
64    }
65
66    if ( $pos >= 68 ) {
67      my @hex = split /\s+/, $hex;
68      my $ascii = "";
69      foreach my $xch ( @hex ) {
70        next if ( $xch eq '' );
71        $ascii .= chr(hex(substr($xch,0,2)));
72        $ascii .= chr(hex(substr($xch,2,2)));
73      }
74      $show = 1;
75      $_ = $ascii;
76    }
77  }
78  elsif ( /^\.\./ ) {
79    s/^\.\.......//;
80    $show = 1;
81  }
82  else {
83    $show = 1;
84  }
85
86  if ( $show ) {
87    if ( $source ne $lastsource || $dest ne $lastdest ) {
88      putline( "\n\n=============== $timestamp   $source  ==>   $dest\n" );
89      $lastsource = $source;
90      $lastdest   = $dest;
91    }
92    putline( $_ );
93  }
94}
95
96
97
98
99###########################################################
100sub putline {
101  my $line = shift;
102  print $line;
103  print SAVETO $line if ( defined($saveto) );
104}
105
106
107###########################################################
108sub usage {
109  print <<EOERROR ;
110
111Usage: sniffstream [options]
112
113The sniffstream program will format the output of "tcpdump -s0 -n -q -xx"
114for easier reading and comparison, with a view to seeing the actions
115involved in a DAV communication session. By default it will run the
116tcpdump command internally.
117
118It will also somewhat format the output of "tcpdump -s0 -n -q -A".
119
120Options:
121
122 --write <filename>        Append the stream to the named file.
123 --file (-|<filename>)     Format the input from the named file, or stdin.
124 --interface <ifname>      Run tcpdump against the specified interface.
125 --dumpspec <spec>         Run tcpdump with that capture specification .
126
127The default interface is 'any' and the default dumpspec is 'tcp port 80'.
128
129EOERROR
130  exit 1;
131
132}
133