# 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__