File Coverage

blib/lib/Net/Frame/Simple.pm
Criterion Covered Total %
statement 30 211 14.2
branch 0 78 0.0
condition 0 24 0.0
subroutine 10 30 33.3
pod 13 13 100.0
total 53 356 14.8


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