Net-Analysis/t/21_Net-Analysis-TCPSession.t
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl Net::Analysis-Utils.t'
use strict;
use Data::Dumper;
use Test::More;
use t::TestFileIntoPackets;
use t::TestEtherealGlue;
use Net::Analysis::TCPSession qw(:const); # Get the constants
use Net::Analysis::Constants qw(:tcpseshstates :packetclasses);
use Net::Analysis::Packet qw(:all);
=head1 HOW TO ADD A NEW TEST FILE
Half of this st of tests steps very carefully through a simple TCP session.
The other half attempt to extract the monologues from various TCP capture
files, comparing results with some reference monologues generated by
ethereal/wireshark.
If you want to add a new TCP capture test case, this is what you need to do ...
First get a tcp capture file that contains the session you want. It shouldn't
contain any other packets or sessions, so use etheral/wireshark or tcpdump
filters to reduce it down.
Then load it into ethereal/wireshark, and use the 'view session' option to
display the TCP session as a series of red/blue monologues.
Now save this in hex-mode from ethereal/wireshark. This is considered the
'reference' version of the monologues derived from the TCP session, which
TCPSession.pm should match.
Add the capture & hex-dumped files as
t/tN_descrip.tcp
t/tN_descrip.hex
using the next value of N, and a short descrip of what characteristics this
trace contains.
=cut
#########################
BEGIN {
plan tests => 5 + scalar(@{list_testfiles()});
use_ok('Net::Analysis::TCPSession')
};
#### Check the constructor constructs ...
#
my $sesh = Net::Analysis::TCPSession->new();
isnt ($sesh, undef, "TCPSession->new()");
#### Validate our helper function reads in packets OK (redundant, really)
#
my (@pkts) = @{tcpfile_into_packets ("t/t1_google.tcp")};
is (scalar(@pkts), 11, 'read in 11 packets from t1_google');
#### Test that the session moves between states correctly.
#
my (@found);
my (@expected) = (# sesh->status(), ret of process_packet()
[SESH_CONNECTING, PKT_OK],
[SESH_CONNECTING, PKT_OK],
[SESH_ESTABLISHED, PKT_ESTABLISHED_SESSION],
[SESH_ESTABLISHED, PKT_OK],
[SESH_ESTABLISHED, PKT_FLIPPED_DIR], #pkt 4
[SESH_ESTABLISHED, PKT_OK],
[SESH_ESTABLISHED, PKT_OK],
[SESH_ESTABLISHED, PKT_OK],
[SESH_ESTABLISHED, PKT_OK],
[SESH_HALF_CLOSED, PKT_OK],
[SESH_CLOSED, PKT_TERMINATED_SESSION]
);
foreach my $pkt (@pkts) {
my $ret = $sesh->process_packet (packet => $pkt);
#printf "%-90.90s [%-2.2s] %s\n", "$pkt", $ret, "$sesh";
push (@found, [$sesh->status(), $ret]);
}
is_deeply (\@found, \@expected, "session status is correct");
#### Test that our session sets packet states correctly.
#
# This mess done by inspection of packet trace.
# Really need some data dups too :/
#
($sesh, @found) = ( $sesh = Net::Analysis::TCPSession->new(), () );
@expected = (PKT_NONDATA) x 65;
@expected[4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36, # *Not 38 !
40,42,44,46,48,50,52,54,56,58,60,62] = (PKT_DATA) x 29;
$expected[38] = (PKT_FUTURE_DATA);
foreach my $pkt ( @{tcpfile_into_packets ("t/t3_data_resend.tcp")} ) {
$sesh->process_packet (packet => $pkt);
#printf "%-90.90s %s\n", "$pkt", '';#"$sesh";
push (@found, pkt_class($pkt));
}
is_deeply (\@found, \@expected, "packet classes are correct");
#### Now run over all our testfiles, building monologues
#
test_monologues($_) for (list_testfiles());
######## Support Functions #########
sub test_monologues {
my ($t) = @_;
# Load in the monologues as generated by ethereal
my $actual_mono = hexdump_to_monologues ("t/$t.hex");
# And now use our code to do the same
my $mono = get_monologues ($t);
if (1) {
is_deeply ($mono, $actual_mono, "test '$t': (".
scalar(@$mono)." monologues) reassembled OK");
} else {
# More useful debugging ...
for my $i (0..$#$mono) {
printf "actual: % 6.6d, found: % 6.6d\n", length($actual_mono->[$i]),
length($mono->[$i]);
if (open (OUT1, ">$t.$i.in")) { print OUT1 $actual_mono->[$i] }
if (open (OUT2, ">$t.$i.out")) { print OUT2 $mono->[$i] }
}
}
}
sub get_monologues {
my ($test_name) = @_;
my ($D) = 0;
my (@pkts) = @{tcpfile_into_packets ("t/$test_name.tcp")};
my $sesh = Net::Analysis::TCPSession->new ();
my (@mono);
foreach my $pkt (@pkts) {
my $ret = $sesh->process_packet (packet => $pkt);
($ret == PKT_REJECTED) && die "logical failure: ".$sesh->errstr();
if ($ret == PKT_FLIPPED_DIR) {
push (@mono, $sesh->previous_monologue()->data());
}
printf "%-100.100s %s\n", "$pkt", "$sesh" if ($D);
}
if ($sesh->has_current_monologue()) {
push (@mono, $sesh->current_monologue()->data());
}
return \@mono;
}
__DATA__