File Coverage

blib/lib/Net/Tshark.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             package Net::Tshark;
3 1     1   20402 use strict;
  1         2  
  1         30  
4 1     1   4 use warnings;
  1         2  
  1         34  
5            
6             our $VERSION = '0.04';
7            
8 1     1   1402 use IPC::Run;
  1         50286  
  1         36  
9 1     1   783 use File::Which qw(which);
  1         1212  
  1         67  
10 1     1   673 use Net::Tshark::Packet;
  0            
  0            
11            
12             # These thresholds are used to prevent the possibility
13             # of an infinite loop while waiting for packet data.
14             use constant MAX_POLLING_ITERATIONS => 100;
15             use constant MAX_PACKETS_RETURNED_AT_ONCE => 10_000;
16            
17             sub new
18             {
19             my ($class) = @_;
20            
21             # Try to find tshark
22             my $tshark_path = which('tshark');
23             if (!defined $tshark_path)
24             {
25             if ($^O eq 'MSWin32' && -x "C:\\Program Files\\Wireshark\\tshark.exe")
26             {
27             $tshark_path = "C:\\Program Files\\Wireshark\\tshark.exe";
28             }
29             else
30             {
31             warn 'Could not find TShark installed. Is it in your PATH?';
32             return;
33             }
34             }
35            
36             my $self = {
37             in => q(),
38             out => q(),
39             err => q(),
40             tshark_path => $tshark_path,
41             };
42            
43             return bless $self, $class;
44             }
45            
46             sub DESTROY
47             {
48             my ($self) = @_;
49             return $self->stop;
50             }
51            
52             sub start
53             {
54             my ($self, %args) = @_;
55             my ($interface, $capture_filter, $display_filter, $duration, $promiscuous)
56             = @args{qw(interface capture_filter display_filter duration promiscuous)};
57            
58             # Construct the command to execute tshark
59             my @command = ($self->{tshark_path});
60             push @command, '-a duration:', int($duration) if ($duration);
61             push @command, '-f', $capture_filter if ($capture_filter);
62             push @command, '-i', $interface if (defined $interface);
63             push @command, '-l'; # Flush the standard output after each packet
64             push @command, '-p' if (defined $promiscuous && !$promiscuous);
65             push @command, '-R', $display_filter if ($display_filter);
66             push @command, '-T', 'pdml'; # Output XML
67            
68             # Start a tshark process and pipe its input, output, and error streams
69             # so that we can read and write to it while it runs
70             $self->{tshark} = IPC::Run::start \@command, \$self->{in}, \$self->{out},
71             \$self->{err};
72            
73             return 1;
74             }
75            
76             sub is_running
77             {
78             my ($self) = @_;
79             return defined $self->{tshark};
80             }
81            
82             sub stop
83             {
84             my ($self) = @_;
85            
86             if (defined $self->{tshark})
87             {
88            
89             # Send Ctrl-C to gracefully end the process
90             $self->{tshark}->signal('INT');
91            
92             # Get all of its stdout
93             $self->__get_all_output;
94            
95             # Make sure the process has terminated
96             $self->{tshark}->kill_kill;
97             $self->{tshark}->finish;
98             undef $self->{tshark};
99             }
100            
101             return;
102             }
103            
104             sub get_packet
105             {
106             my ($self) = @_;
107            
108             # Get the decoded string for one packet.
109             my $pkt_string = $self->__get_decoded_packet
110             or return;
111            
112             # Create a packet object from the string and return it.
113             return Net::Tshark::Packet->new($pkt_string);
114             }
115            
116             sub get_packets
117             {
118             my ($self) = @_;
119            
120             my @packets;
121             for (1 .. MAX_PACKETS_RETURNED_AT_ONCE)
122             {
123             my $packet = $self->get_packet;
124             last if !defined $packet;
125            
126             push @packets, $packet;
127             }
128            
129             return @packets;
130             }
131            
132             sub __get_decoded_packet
133             {
134             my ($self) = @_;
135            
136             for (1 .. MAX_POLLING_ITERATIONS)
137             {
138             # Wait for us to see an entire packet
139             if (my ($packet) = $self->{out} =~ /(.*?<\/packet>)/s)
140             {
141            
142             # Remove the packet from the buffer and process it
143             $self->{out} =~ s/\Q$packet\E//;
144            
145             return $packet;
146             }
147            
148             # Get the latest output from the tshark process
149             # and quit if there is no more output to get
150             last if !$self->__get_more_output;
151             }
152            
153             return;
154             }
155            
156             sub __get_more_output
157             {
158             my ($self) = @_;
159             return if !defined $self->{tshark};
160            
161             my $buf_len = length $self->{out};
162             $self->{tshark}->pump_nb;
163             return length $self->{out} > $buf_len;
164             }
165            
166             sub __get_all_output
167             {
168             my ($self) = @_;
169            
170             for (1 .. MAX_POLLING_ITERATIONS)
171             {
172             last if !$self->__get_more_output;
173             }
174            
175             return;
176             }
177            
178             1;
179            
180             __END__