File Coverage

blib/lib/Net/Frame/Simple.pm
Criterion Covered Total %
statement 30 208 14.4
branch 0 76 0.0
condition 0 18 0.0
subroutine 10 30 33.3
pod 13 13 100.0
total 53 345 15.3


line stmt bran cond sub pod time code
1             #
2             # $Id: Simple.pm,v 6683a807d2d6 2016/10/05 16:54:08 gomor $
3             #
4             package Net::Frame::Simple;
5 1     1   3869 use warnings; use strict;
  1     1   1  
  1         30  
  1         3  
  1         1  
  1         27  
6              
7             our $VERSION = '1.07';
8              
9 1     1   431 use Class::Gomor::Array;
  1         8578  
  1         37  
10 1     1   5 use Exporter;
  1         1  
  1         105  
11             our @ISA = qw(Class::Gomor::Array Exporter);
12             our @EXPORT_OK = qw(
13             $NoComputeLengths
14             $NoComputeChecksums
15             );
16             our @AS = qw(
17             raw
18             reply
19             timestamp
20             firstLayer
21             padding
22             ref
23             truncated
24             _canMatchLayer
25             _getKey
26             _getKeyReverse
27             );
28             our @AA = qw(
29             layers
30             );
31             __PACKAGE__->cgBuildIndices;
32             __PACKAGE__->cgBuildAccessorsScalar(\@AS);
33             __PACKAGE__->cgBuildAccessorsArray (\@AA);
34              
35 1     1   4 no strict 'vars';
  1         1  
  1         20  
36              
37 1     1   3 use Carp;
  1         1  
  1         53  
38 1     1   455 use Time::HiRes qw(gettimeofday);
  1         933  
  1         3  
39 1     1   562 use Net::Frame::Layer qw(:consts);
  1         37669  
  1         141  
40              
41 1     1   507 use Net::Frame::Layer::UDP;
  1         1118  
  1         34  
42 1     1   414 use Net::Frame::Layer::TCP;
  1         1578  
  1         1411  
43              
44             our $NoComputeLengths = 0;
45             our $NoComputeChecksums = 0;
46              
47             sub _gettimeofday {
48 0     0     my ($sec, $usec) = gettimeofday();
49 0           sprintf("%d.%06d", $sec, $usec);
50             }
51              
52             sub new {
53 0     0 1   my $self = shift->SUPER::new(
54             timestamp => _gettimeofday(),
55             firstLayer => NF_LAYER_UNKNOWN,
56             truncated => 0,
57             layers => [],
58             @_,
59             );
60              
61 0 0         $self->[$__raw] ? $self->unpack : $self->pack;
62 0           $self;
63             }
64              
65             sub newFromDump {
66 0     0 1   my $self = shift;
67 0           my ($h) = @_;
68             $self->new(
69             timestamp => $h->{timestamp},
70             firstLayer => $h->{firstLayer},
71             raw => $h->{raw},
72 0           );
73             }
74              
75             # If there are multiple layers of the same type, the upper will be kept
76             sub _setRef {
77 0     0     my $self = shift;
78 0           my ($l) = @_;
79 0           $self->[$__ref]->{$l->layer} = $l;
80             }
81              
82             sub unpack {
83 0     0 1   my $self = shift;
84              
85 0           my $encapsulate = $self->[$__firstLayer];
86              
87 0 0         if ($encapsulate eq NF_LAYER_UNKNOWN) {
88 0           print("Unable to unpack frame from this layer type.\n");
89 0           return undef;
90             }
91              
92 0           my @layers;
93 0           my $n = 0;
94 0           my $raw = $self->[$__raw];
95 0           my $rawLength = length($raw);
96 0           my $oRaw = $raw;
97 0           my $prevLayer;
98             # No more than a thousand nested layers, maybe should be a parameter
99 0           for (1..1000) {
100 0 0         last unless $raw;
101              
102 0           $encapsulate =~ s/[^-:\w]//g; # Fix potential code injection
103 0           my $layer = 'Net::Frame::Layer::'.$encapsulate;
104 0           eval "require $layer";
105 0 0         if ($@) {
106 0           print("*** $layer module not found.\n".
107             "*** Either install it (if avail), or implement it.\n".
108             "*** You can also send the pcap file to perl\@gomor.org.\n");
109 0 0         if ($prevLayer) {
110 0           $prevLayer->nextLayer(NF_LAYER_NOT_AVAILABLE);
111             }
112 0           last;
113             }
114 0 0         my $l = $layer->new(raw => $raw)->unpack
115             or last;
116              
117 0           $encapsulate = $l->encapsulate;
118 0           $raw = $l->payload;
119              
120 0           push @layers, $l;
121             # If there are multiple layers of the same type, the upper will be kept
122 0           $self->_setRef($l);
123              
124 0 0         last unless $encapsulate;
125              
126 0 0         if ($encapsulate eq NF_LAYER_UNKNOWN) {
127 0           print("Unable to unpack next layer, not yet implemented in layer: ".
128 0           "$n:@{[$l->layer]}\n");
129 0           last;
130             }
131              
132 0           $prevLayer = $l;
133 0           $oRaw = $raw;
134             }
135              
136 0 0         if (@layers > 0) {
137 0           $self->[$__layers] = \@layers;
138 0           $self->_getPadding($rawLength);
139 0           $self->_searchCanGetKeyLayer;
140 0           $self->_searchCanGetKeyReverseLayer;
141 0           $self->_searchCanMatchLayer;
142 0           return $self;
143             }
144              
145 0           undef;
146             }
147              
148             sub computeLengths {
149 0     0 1   my $self = shift;
150 0           my $layers = $self->[$__layers];
151 0           for my $l (reverse @$layers) {
152 0           $l->computeLengths($layers);
153             }
154 0           return 1;
155             }
156              
157             sub computeChecksums {
158 0     0 1   my $self = shift;
159 0           my $layers = $self->[$__layers];
160 0           for my $l (reverse @$layers) {
161 0           $l->computeChecksums($layers);
162             }
163 0           return 1;
164             }
165              
166             sub pack {
167 0     0 1   my $self = shift;
168              
169             # If there are multiple layers of the same type,
170             # the upper will be kept for the reference
171 0           $self->_setRef($_) for @{$self->[$__layers]};
  0            
172              
173 0 0         $self->computeLengths unless $NoComputeLengths;
174 0 0         $self->computeChecksums unless $NoComputeChecksums;
175              
176 0           my $raw = '';
177 0           my $last;
178 0           for (@{$self->[$__layers]}) {
  0            
179 0           $raw .= $_->pack;
180 0           $last = $_;
181             }
182 0 0 0       if ($last && defined($last->payload)) {
183 0           $raw .= $last->payload;
184             }
185              
186 0 0         $raw .= $self->[$__padding] if $self->[$__padding];
187              
188 0           $self->_searchCanGetKeyLayer;
189 0           $self->_searchCanGetKeyReverseLayer;
190 0           $self->_searchCanMatchLayer;
191              
192 0           $self->[$__raw] = $raw;
193             }
194              
195             sub _getPadding {
196 0     0     my $self = shift;
197 0           my ($rawLength) = @_;
198              
199 0           my $last = ${$self->[$__layers]}[-1];
  0            
200              
201             # Last layer has no payload, so no padding
202 0 0 0       return if (! defined($last->payload) || ! length($last->payload));
203              
204             # FIX: be it available or not, we need to parse payload/padding difference
205             # So, I comment these lines for now
206             #if ($last->nextLayer eq NF_LAYER_NOT_AVAILABLE) {
207             #return;
208             #}
209              
210 0           my $tLen = 0;
211 0           for my $l (@{$self->[$__layers]}) {
  0            
212 0 0         if ($l->layer eq 'IPv4') {
    0          
213 0           $tLen += $l->length;
214 0           last;
215             }
216             elsif ($l->layer eq 'IPv6') {
217 0           $tLen += $l->getLength;
218 0           $tLen += $l->getPayloadLength;
219 0           last;
220             }
221 0           $tLen += $l->getLength;
222             }
223              
224             # No padding
225 0 0         return if $rawLength == $tLen;
226              
227 0           my $pLen = 0;
228 0           my $padding;
229 0 0         if ($rawLength > $tLen) {
230 0           $pLen = $rawLength - $tLen;
231 0           $padding = substr($self->[$__raw], $tLen, $pLen);
232 0           $self->[$__padding] = $padding;
233             }
234             else {
235 0           $self->[$__truncated] = 1;
236             }
237              
238             # Now, split padding between true padding and true payload
239 0           my $payloadLength = length($last->payload);
240 0 0         if ($payloadLength > $pLen) {
241 0           my $payload = substr($last->payload, 0, ($payloadLength - $pLen));
242 0           $last->payload($payload);
243             }
244             else {
245 0           $last->payload(undef);
246             }
247             }
248              
249             sub send {
250 0     0 1   my $self = shift;
251 0           my ($oWrite) = @_;
252 0           $oWrite->send($self->[$__raw]);
253             }
254              
255 0 0   0 1   sub reSend { my $self = shift; $self->send(shift()) unless $self->[$__reply] }
  0            
256              
257             sub _searchCanMatchLayer {
258 0     0     my $self = shift;
259 0           for my $l (reverse @{$self->[$__layers]}) {
  0            
260 0 0         if ($l->can('match')) {
261 0           $self->[$___canMatchLayer] = $l;
262 0           last;
263             }
264             }
265 0           undef;
266             }
267              
268             sub _searchCanGetKeyLayer {
269 0     0     my $self = shift;
270 0           for my $l (reverse @{$self->[$__layers]}) {
  0            
271 0 0         if ($l->can('getKey')) {
272 0           $self->[$___getKey] = $l->getKey;
273 0           last;
274             }
275             }
276             }
277              
278             sub _searchCanGetKeyReverseLayer {
279 0     0     my $self = shift;
280 0           for my $l (reverse @{$self->[$__layers]}) {
  0            
281 0 0         if ($l->can('getKeyReverse')) {
282 0           $self->[$___getKeyReverse] = $l->getKeyReverse;
283 0           last;
284             }
285             }
286             }
287              
288             sub _recv {
289 0     0     my $self = shift;
290 0           my ($oDump) = @_;
291              
292 0           my $layer = $self->[$___canMatchLayer];
293              
294 0           for my $this ($oDump->getFramesFor($self)) {
295 0 0         next unless $this->[$__timestamp] gt $self->[$__timestamp];
296              
297             # We must put ICMPv4 before, because the other will
298             # always match for UDP.
299 0 0 0       if (exists $this->[$__ref]->{ICMPv4}
    0 0        
300             && (exists $this->[$__ref]->{UDP} || exists $this->[$__ref]->{TCP})) {
301 0 0         if (exists $this->[$__ref]->{$layer->layer}) {
302             return $this
303 0 0         if $this->[$__ref]->{$layer->layer}->getKey eq $layer->getKey;
304             }
305             }
306             elsif (exists $this->[$__ref]->{$layer->layer}) {
307 0 0         return $this if $layer->match($this->[$__ref]->{$layer->layer});
308             }
309             }
310              
311 0           undef;
312             }
313              
314             sub recv {
315 0     0 1   my $self = shift;
316 0           my ($oDump) = @_;
317              
318             # We already have the reply
319 0 0         $self->[$__reply] and return $self->[$__reply];
320              
321             # Is there anything waiting ?
322 0 0         my $h = $oDump->next or return undef;
323              
324 0           my $oSimple = Net::Frame::Simple->newFromDump($h);
325 0           $oDump->store($oSimple);
326              
327 0 0         if (my $reply = $self->_recv($oDump)) {
328 0           $self->cgDebugPrint(1, "Reply received");
329 0           return $self->[$__reply] = $reply;
330             }
331              
332 0           undef;
333             }
334              
335             # Needed by Net::Frame::Dump
336 0 0   0 1   sub getKey { shift->[$___getKey] || 'all' }
337 0 0   0 1   sub getKeyReverse { shift->[$___getKeyReverse] || 'all' }
338              
339             sub print {
340 0     0 1   my $self = shift;
341              
342 0           my $str = '';
343 0           my $last;
344 0           for my $l (@{$self->[$__layers]}) {
  0            
345 0           $str .= $l->print."\n";
346 0           $last = $l;
347             }
348 0           $str =~ s/\n$//s;
349              
350             # Print remaining to be decoded, if any
351 0 0 0       if ($last && $last->payload) {
352 0           $str .= "\n".$last->layer.': payload:'.CORE::unpack('H*', $last->payload);
353             }
354              
355             # Print the padding, if any
356 0 0         if ($self->[$__padding]) {
357 0           $str .= "\n".'Padding: '.CORE::unpack('H*', $self->[$__padding]);
358             }
359              
360 0           $str;
361             }
362              
363             sub dump {
364 0     0 1   my $self = shift;
365              
366 0           my $last;
367 0           my $raw = '';
368 0           for my $l (@{$self->[$__layers]}) {
  0            
369 0           $raw .= $l->raw;
370 0           $last = $l;
371             }
372              
373 0 0 0       if ($last && defined($last->payload)) {
374 0           $raw .= $last->payload;
375             }
376              
377 0 0         $raw .= $self->[$__padding] if $self->[$__padding];
378              
379 0           CORE::unpack('H*', $raw);
380             }
381              
382             1;
383              
384             __END__