File Coverage

lib/SMB/Packer.pm
Criterion Covered Total %
statement 82 89 92.1
branch 14 16 87.5
condition 13 27 48.1
subroutine 25 29 86.2
pod 24 25 96.0
total 158 186 84.9


line stmt bran cond sub pod time code
1             # SMB Perl library, Copyright (C) 2014-2018 Mikhael Goikhman, migo@cpan.org
2             #
3             # This program is free software: you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation, either version 3 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program. If not, see .
15              
16             package SMB::Packer;
17              
18 3     3   574 use strict;
  3         3  
  3         73  
19 3     3   11 use warnings;
  3         3  
  3         55  
20              
21 3     3   456 use bytes;
  3         12  
  3         11  
22 3     3   456 use Encode 'encode';
  3         7792  
  3         3183  
23              
24             sub new ($$) {
25 3     3 1 448 my $class = shift;
26              
27 3         6 my $self = bless {}, $class;
28              
29 3         7 return $self->reset;
30             }
31              
32             sub reset ($) {
33 14     14 1 3140 my $self = shift;
34              
35 14         29 $self->{data} = '';
36 14         22 $self->{offset} = 0;
37 14         28 $self->{marks} = {};
38 14         27 $self->{stubs} = {};
39              
40 14         38 return $self;
41             }
42              
43 10     10 1 44 sub data { $_[0]->{data} }
44 23     23 1 1633 sub size { length($_[0]->{data}) }
45 19     19 1 91 sub offset { $_[0]->{offset} }
46              
47             sub zero ($$) {
48 1     1 1 2 my $self = shift;
49 1   50     4 my $n_bytes = shift // die;
50              
51 1         5 substr($self->{data}, $self->{offset}, $n_bytes) = "\0" x $n_bytes;
52              
53 1         5 return $self->skip($n_bytes);
54             }
55              
56             sub skip ($$) {
57 2     2 1 4 my $self = shift;
58 2   50     8 my $n_bytes = shift // die;
59              
60             my $n_avail = $self->{offset} + $n_bytes > $self->size
61 2 100       7 ? $self->size - $self->{offset} : $n_bytes;
62              
63 2 100       8 $self->zero($n_bytes - $n_avail) if $n_avail < $n_bytes;
64              
65 2         4 $self->{offset} += $n_avail;
66              
67 2         6 return $self;
68             }
69              
70             sub stub ($$$) {
71 3     3 1 8 my $self = shift;
72 3   50     10 my $name = shift // '';
73 3   50     8 my $type = shift // '';
74              
75 3 50 66     27 die "type must be either size of bytes or uint{8,16{,_be},32{,_be},64}"
76             unless $type =~ /^uint(8|16(_be)?|32(_be)?|64)$/ || $type =~ /^\d+$/;
77 3         12 $self->{stubs}{$name} = [ $self->{offset}, $type ];
78              
79 3 100       23 $type =~ /^\d+$/
80             ? $self->bytes("\0" x $type)
81             : $self->$type(0);
82              
83 3         12 return $self;
84             }
85              
86             sub fill ($$) {
87 3     3 1 7 my $self = shift;
88 3   50     10 my $name = shift // '';
89 3   50     7 my $data = shift // die;
90              
91 3         6 my $curr_offset = $self->{offset};
92 3 50       5 my ($offset, $type) = @{$self->{stubs}{$name} || die "No previously set stub '$name'"};
  3         11  
93 3         6 $self->{offset} = $offset;
94 3 100       20 $type =~ /^\d+$/
95             ? $self->bytes($data)
96             : $self->$type($data);
97 3         4 $self->{offset} = $curr_offset;
98              
99 3         11 return $self;
100             }
101              
102             sub mark ($$) {
103 1     1 1 3 my $self = shift;
104 1   50     4 my $name = shift // '';
105              
106 1         4 $self->{marks}{$name} = $self->{offset};
107              
108 1         4 return $self;
109             }
110              
111             sub jump ($$) {
112 1     1 1 3 my $self = shift;
113 1   50     5 my $name = shift // '';
114              
115 1   50     7 $self->{offset} = $self->{marks}{$name} || 0;
116              
117 1         5 return $self;
118             }
119              
120             sub diff ($$) {
121 0     0 1 0 my $self = shift;
122 0   0     0 my $name = shift // '';
123              
124 0   0     0 return $self->{offset} - ($self->{marks}{$name} || 0);
125             }
126              
127             my %UINT_MODS = (
128             +1 => 'C',
129             +2 => 'v',
130             +4 => 'V',
131             -1 => 'C',
132             -2 => 'n',
133             -4 => 'N',
134             );
135              
136             sub uint ($$$$) {
137 103     103 0 120 my $self = shift;
138 103         103 my $n_bytes = shift;
139 103 100       144 my $be_factor = shift() ? -1 : 1;
140 103         128 my $i = shift;
141              
142 103         297 return $self->bytes(pack($UINT_MODS{$be_factor * $n_bytes}, $i));
143             }
144              
145             sub str ($$;$) {
146 15     15 1 18 my $self = shift;
147 15         17 my $str = shift;
148 15   100     40 my $enc = shift || 'UTF-16LE';
149              
150 15         31 return $self->bytes(encode($enc, $str));
151             }
152              
153             sub bytes ($$) {
154 136     136 1 5982 my $self = shift;
155 136         136 my $data = shift;
156              
157 136 100       223 $data = join('', @$data) if ref($data) eq 'ARRAY';
158              
159 136         220 substr($self->{data}, $self->{offset}, length($data)) = $data;
160 136         164 $self->{offset} += length($data);
161              
162 136         389 return $self;
163             }
164              
165 14     14 1 3485 sub uint8 { uint($_[0], 1, 0, $_[1]); }
166 52     52 1 1505 sub uint16 { uint($_[0], 2, 0, $_[1]); }
167 28     28 1 574 sub uint32 { uint($_[0], 4, 0, $_[1]); }
168 6     6 1 1491 sub uint16_be { uint($_[0], 2, 1, $_[1]); }
169 3     3 1 591 sub uint32_be { uint($_[0], 4, 1, $_[1]); }
170 3     3 1 7 sub uint64 { uint32($_[0], $_[1] & 0xffffffff); uint32($_[0], $_[1] >> 32); }
  3         5  
171 1     1 1 4 sub utf16 { str($_[0], $_[1]); }
172 0     0 1   sub utf16_be { str($_[0], $_[1], 'UTF-16BE'); }
173 0     0 1   sub fid1 { uint16($_[0], $_[1]); }
174 0     0 1   sub fid2 { uint64($_[0], $_[1][0]); uint64($_[0], $_[1][1]); }
  0            
175              
176             1;
177              
178             __END__