package Net::Analysis::TCPMonologue;
# $Id: TCPMonologue.pm 136 2005-10-21 00:14:54Z abworrall $
use 5.008000;
our $VERSION = '0.02';
use strict;
use warnings;
use overload
q("") => sub { $_[0]->as_string() },
'eq' => sub { return "$_[0]" eq "$_[1]" }; # Needed for Test::is_deeply
use Carp qw(carp croak confess);
use Params::Validate qw(:all);
use Net::Analysis::Packet qw(:all);
# {{{ POD
=head1 NAME
Net::Analysis::TCPMonologue - simple class to contain a TCP monologue
=head1 SYNOPSIS
use Net::Analysis::Packet;
use Net::Analysis::Monologue;
my $mono = Net::Analysis::Monologue->new();
$mono->add_packet($pkt);
if ($mono->data() =~ /foo/) {
print "Mono contained 'foo'\n";
}
print "Monologue was " .$mono->length().
"b long, over " .$mono->t_elapsed ()." seconds\n";
=head1 DESCRIPTION
A TCP monologue is a series of packets that travel in one direction, e.g. a
HTTP response. A monologue ends when a data packet travels in the other
direction. Pairs of monologues will often be linked as transactions.
As packets are added, this object updates some info. It assumes that the packet
you've added belongs in the monologue, and that you're adding them in the
correct order.
The payload of the monologue lives in C<$mono->{data}>.
=head1 METHODS
=cut
# }}}
#### Public methods
#
# {{{ new
# {{{ POD
=head2 new ( )
Takes no arguments.
=cut
# }}}
sub new {
my ($class) = shift;
my %h; # = validate (@_, {});
my ($self) = bless ({}, $class);
return $self;
}
# }}}
# {{{ add_packet
=head2 add_packet ($pkt)
Adds any data, increments the packet counter, and keeps note of the time.
=cut
sub add_packet {
my ($self, $pkt) = @_;
if (!exists $self->{data}) {
# No data ? Must be the first packet; trigger some init
$self->_init_from_first_packet($pkt);
}
# Keep track of which packets crontibuted which bytes
push (@{$self->{which_pkts}}, [length($self->{data}), $pkt]);
$self->{n_packets}++;
$self->{data} .= $pkt->[PKT_SLOT_DATA];
# Now update the 'last packet' time counters
if (pkt_time($pkt) > $self->{time}) {
$self->{time} = pkt_time($pkt);
}
#print "Adding packet $pkt to $self\n";
return 1;
}
# }}}
# {{{ data
=head2 data ()
The actual data of the monologue; the bytes sent.
=cut
sub data {
my ($self) = @_;
return $self->{data};
}
# }}}
# {{{ t_start
=head2 t_start ()
Returns an object representing the time the monologue started. Can be treated
like a float, giving fractional epoch seconds. Only accurate to the
microsecond.
=cut
sub t_start {
my ($self) = @_;
return $self->{t_start};
}
# }}}
# {{{ t_end
=head2 t_end ()
Same as C<t_start()>, but giving the time the monologue ended (or the last
packet so far, if you call it before the monologue has ended.)
=cut
sub t_end {
my ($self) = @_;
return $self->{time};
}
# }}}
# {{{ t_elapsed
=head2 t_elapsed ()
Returns an object representing C<t_end - t_start> for this monologue. Can be
treated like a float, giving duration in fractional seconds.
=cut
sub t_elapsed {
my ($self) = @_;
return ($self->{time} - $self->{t_start});
}
# }}}
# {{{ n_packets
=head2 n_packets ()
How many data packets were in the monologue.
=cut
sub n_packets {
my ($self) = @_;
return ($self->{n_packets});
}
# }}}
# {{{ length
=head2 length ()
How long the monologue data was, in bytes. Excludes all the various packet
headers.
=cut
sub length {
my ($self) = @_;
return length($self->{data});
}
# }}}
# {{{ first_packet
=head2 first_packet ()
Returns the first L<Net::Analysis::Packet> in the monologue. You can use it to
extract any TCP or IP information about the monologue.
=cut
sub first_packet {
my ($self) = @_;
return $self->{first_packet};
}
# }}}
# {{{ which_pkt
=head2 which_pkt ($byte_offset)
Given a byte offset from within the monologue, return the packet which
contributed the byte at that offset, or undef. Bytes are counted from zero.
This can be useful to retrieve timestamps of areas inside long-lived
monologues.
=cut
sub which_pkt {
my ($self, $n) = @_;
return undef if ($n < 0 || $n >= CORE::length($self->{data}));
my $prev_pkt;
for my $row (@{ $self->{which_pkts} }) {
if ($row->[0] > $n) {
# This row contains bytes ahead of $n; previous is what we want
die "which_pkt confusion" if (!defined $prev_pkt);
return $prev_pkt;
}
$prev_pkt = $row->[1];
}
return $prev_pkt;
}
# }}}
# {{{ as_string
sub as_string {
my ($self) = @_;
my $str = '';
if (exists $self->{data}) {
my $dur = $self->t_elapsed();
$str .= sprintf ("[Mono from %21.21s]%10.06fs, %3dpkts, %6db",
$self->{from}, $dur, $self->n_packets(),
$self->length());
} else {
$str .= "[Mono undefined]";
}
return $str;
}
# }}}
#### Private helper methods
#
# {{{ _init_from_first_packet
sub _init_from_first_packet {
my ($self, $pkt) = @_;
$self->{n_packets} = 0;
$self->{data} = '';
# Initialise the monologue
$self->{to} = $pkt->[PKT_SLOT_TO];
$self->{from} = $pkt->[PKT_SLOT_FROM];
$self->{time} = pkt_time($pkt) + 0; # Make a cloned copy
# Keep copies of the first ever time, and the packet itself
$self->{t_start} = $self->{time};
$self->{first_packet} = $pkt;
# Keep track of which packets contributed which bytes
$self->{which_pkts} = [];
}
# }}}
1;
__END__
# {{{ POD
=head2 EXPORT
None by default.
=head1 AUTHOR
Adam B. Worrall, E<lt>worrall@cpan.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2004 by Adam B. Worrall
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut
# }}}
# {{{ -------------------------={ E N D }=----------------------------------
# Local variables:
# folded-file: t
# end:
# }}}
syntax highlighted by Code2HTML, v. 0.9.1