File Coverage

blib/lib/Net/Frame/Simple.pm
Criterion Covered Total %
statement 30 210 14.2
branch 0 76 0.0
condition 0 18 0.0
subroutine 10 30 33.3
pod 13 13 100.0
total 53 347 15.2


line stmt bran cond sub pod time code
1             #
2             # $Id: Simple.pm,v f95f896d91d6 2017/05/07 12:57:38 gomor $
3             #
4             package Net::Frame::Simple;
5 1     1   3745 use warnings; use strict;
  1     1   2  
  1         25  
  1         4  
  1         2  
  1         28  
6              
7             our $VERSION = '1.08';
8              
9 1     1   366 use Class::Gomor::Array;
  1         8458  
  1         36  
10 1     1   5 use Exporter;
  1         2  
  1         119  
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   7 no strict 'vars';
  1         2  
  1         21  
36              
37 1     1   4 use Carp;
  1         2  
  1         47  
38 1     1   388 use Time::HiRes qw(gettimeofday);
  1         910  
  1         4  
39 1     1   525 use Net::Frame::Layer qw(:consts);
  1         39658  
  1         174  
40              
41 1     1   393 use Net::Frame::Layer::UDP;
  1         1213  
  1         34  
42 1     1   374 use Net::Frame::Layer::TCP;
  1         1823  
  1         1435  
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             # currLayers is used to keep track of already processed layers.
152 0           my $currLayers;
153 0           for my $l (reverse @$layers) {
154 0           unshift @$currLayers, $l;
155 0           $l->computeLengths($currLayers);
156             }
157 0           return 1;
158             }
159              
160             sub computeChecksums {
161 0     0 1   my $self = shift;
162 0           my $layers = $self->[$__layers];
163 0           for my $l (reverse @$layers) {
164 0           $l->computeChecksums($layers);
165             }
166 0           return 1;
167             }
168              
169             sub pack {
170 0     0 1   my $self = shift;
171              
172             # If there are multiple layers of the same type,
173             # the upper will be kept for the reference
174 0           $self->_setRef($_) for @{$self->[$__layers]};
  0            
175              
176 0 0         $self->computeLengths unless $NoComputeLengths;
177 0 0         $self->computeChecksums unless $NoComputeChecksums;
178              
179 0           my $raw = '';
180 0           my $last;
181 0           for (@{$self->[$__layers]}) {
  0            
182 0           $raw .= $_->pack;
183 0           $last = $_;
184             }
185 0 0 0       if ($last && defined($last->payload)) {
186 0           $raw .= $last->payload;
187             }
188              
189 0 0         $raw .= $self->[$__padding] if $self->[$__padding];
190              
191 0           $self->_searchCanGetKeyLayer;
192 0           $self->_searchCanGetKeyReverseLayer;
193 0           $self->_searchCanMatchLayer;
194              
195 0           $self->[$__raw] = $raw;
196             }
197              
198             sub _getPadding {
199 0     0     my $self = shift;
200 0           my ($rawLength) = @_;
201              
202 0           my $last = ${$self->[$__layers]}[-1];
  0            
203              
204             # Last layer has no payload, so no padding
205 0 0 0       return if (! defined($last->payload) || ! length($last->payload));
206              
207             # FIX: be it available or not, we need to parse payload/padding difference
208             # So, I comment these lines for now
209             #if ($last->nextLayer eq NF_LAYER_NOT_AVAILABLE) {
210             #return;
211             #}
212              
213 0           my $tLen = 0;
214 0           for my $l (@{$self->[$__layers]}) {
  0            
215 0 0         if ($l->layer eq 'IPv4') {
    0          
216 0           $tLen += $l->length;
217 0           last;
218             }
219             elsif ($l->layer eq 'IPv6') {
220 0           $tLen += $l->getLength;
221 0           $tLen += $l->getPayloadLength;
222 0           last;
223             }
224 0           $tLen += $l->getLength;
225             }
226              
227             # No padding
228 0 0         return if $rawLength == $tLen;
229              
230 0           my $pLen = 0;
231 0           my $padding;
232 0 0         if ($rawLength > $tLen) {
233 0           $pLen = $rawLength - $tLen;
234 0           $padding = substr($self->[$__raw], $tLen, $pLen);
235 0           $self->[$__padding] = $padding;
236             }
237             else {
238 0           $self->[$__truncated] = 1;
239             }
240              
241             # Now, split padding between true padding and true payload
242 0           my $payloadLength = length($last->payload);
243 0 0         if ($payloadLength > $pLen) {
244 0           my $payload = substr($last->payload, 0, ($payloadLength - $pLen));
245 0           $last->payload($payload);
246             }
247             else {
248 0           $last->payload(undef);
249             }
250             }
251              
252             sub send {
253 0     0 1   my $self = shift;
254 0           my ($oWrite) = @_;
255 0           $oWrite->send($self->[$__raw]);
256             }
257              
258 0 0   0 1   sub reSend { my $self = shift; $self->send(shift()) unless $self->[$__reply] }
  0            
259              
260             sub _searchCanMatchLayer {
261 0     0     my $self = shift;
262 0           for my $l (reverse @{$self->[$__layers]}) {
  0            
263 0 0         if ($l->can('match')) {
264 0           $self->[$___canMatchLayer] = $l;
265 0           last;
266             }
267             }
268 0           undef;
269             }
270              
271             sub _searchCanGetKeyLayer {
272 0     0     my $self = shift;
273 0           for my $l (reverse @{$self->[$__layers]}) {
  0            
274 0 0         if ($l->can('getKey')) {
275 0           $self->[$___getKey] = $l->getKey;
276 0           last;
277             }
278             }
279             }
280              
281             sub _searchCanGetKeyReverseLayer {
282 0     0     my $self = shift;
283 0           for my $l (reverse @{$self->[$__layers]}) {
  0            
284 0 0         if ($l->can('getKeyReverse')) {
285 0           $self->[$___getKeyReverse] = $l->getKeyReverse;
286 0           last;
287             }
288             }
289             }
290              
291             sub _recv {
292 0     0     my $self = shift;
293 0           my ($oDump) = @_;
294              
295 0           my $layer = $self->[$___canMatchLayer];
296              
297 0           for my $this ($oDump->getFramesFor($self)) {
298 0 0         next unless $this->[$__timestamp] gt $self->[$__timestamp];
299              
300             # We must put ICMPv4 before, because the other will
301             # always match for UDP.
302 0 0 0       if (exists $this->[$__ref]->{ICMPv4}
    0 0        
303             && (exists $this->[$__ref]->{UDP} || exists $this->[$__ref]->{TCP})) {
304 0 0         if (exists $this->[$__ref]->{$layer->layer}) {
305             return $this
306 0 0         if $this->[$__ref]->{$layer->layer}->getKey eq $layer->getKey;
307             }
308             }
309             elsif (exists $this->[$__ref]->{$layer->layer}) {
310 0 0         return $this if $layer->match($this->[$__ref]->{$layer->layer});
311             }
312             }
313              
314 0           undef;
315             }
316              
317             sub recv {
318 0     0 1   my $self = shift;
319 0           my ($oDump) = @_;
320              
321             # We already have the reply
322 0 0         $self->[$__reply] and return $self->[$__reply];
323              
324             # Is there anything waiting ?
325 0 0         my $h = $oDump->next or return undef;
326              
327 0           my $oSimple = Net::Frame::Simple->newFromDump($h);
328 0           $oDump->store($oSimple);
329              
330 0 0         if (my $reply = $self->_recv($oDump)) {
331 0           $self->cgDebugPrint(1, "Reply received");
332 0           return $self->[$__reply] = $reply;
333             }
334              
335 0           undef;
336             }
337              
338             # Needed by Net::Frame::Dump
339 0 0   0 1   sub getKey { shift->[$___getKey] || 'all' }
340 0 0   0 1   sub getKeyReverse { shift->[$___getKeyReverse] || 'all' }
341              
342             sub print {
343 0     0 1   my $self = shift;
344              
345 0           my $str = '';
346 0           my $last;
347 0           for my $l (@{$self->[$__layers]}) {
  0            
348 0           $str .= $l->print."\n";
349 0           $last = $l;
350             }
351 0           $str =~ s/\n$//s;
352              
353             # Print remaining to be decoded, if any
354 0 0 0       if ($last && $last->payload) {
355 0           $str .= "\n".$last->layer.': payload:'.CORE::unpack('H*', $last->payload);
356             }
357              
358             # Print the padding, if any
359 0 0         if ($self->[$__padding]) {
360 0           $str .= "\n".'Padding: '.CORE::unpack('H*', $self->[$__padding]);
361             }
362              
363 0           $str;
364             }
365              
366             sub dump {
367 0     0 1   my $self = shift;
368              
369 0           my $last;
370 0           my $raw = '';
371 0           for my $l (@{$self->[$__layers]}) {
  0            
372 0           $raw .= $l->raw;
373 0           $last = $l;
374             }
375              
376 0 0 0       if ($last && defined($last->payload)) {
377 0           $raw .= $last->payload;
378             }
379              
380 0 0         $raw .= $self->[$__padding] if $self->[$__padding];
381              
382 0           CORE::unpack('H*', $raw);
383             }
384              
385             1;
386              
387             __END__