File Coverage

lib/SMB/Packer.pm
Criterion Covered Total %
statement 95 97 97.9
branch 14 16 87.5
condition 19 29 65.5
subroutine 29 31 93.5
pod 25 26 96.1
total 182 199 91.4


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 5     5   1661 use strict;
  5         14  
  5         167  
19 5     5   30 use warnings;
  5         10  
  5         142  
20              
21 5     5   1351 use bytes;
  5         38  
  5         28  
22 5     5   865 use if (1 << 32 == 1), 'bigint'; # support native uint64 on 32-bit platforms
  5         23  
  5         26  
23 5     5   1444 use Encode 'encode';
  5         23848  
  5         8431  
24              
25             sub new ($$) {
26 6     6 1 1105 my $class = shift;
27              
28 6         15 my $self = bless {}, $class;
29              
30 6         28 return $self->reset;
31             }
32              
33             sub reset ($) {
34 23     23 1 3677 my $self = shift;
35              
36 23         66 $self->{data} = '';
37 23         45 $self->{offset} = 0;
38 23         53 $self->{marks} = {};
39 23         50 $self->{stubs} = {};
40              
41 23         70 return $self;
42             }
43              
44 20     20 1 103 sub data { $_[0]->{data} }
45 76     76 1 2002 sub size { length($_[0]->{data}) }
46 26     26 1 131 sub offset { $_[0]->{offset} }
47              
48             sub zero ($$) {
49 15     15 1 18 my $self = shift;
50 15   50     27 my $n_bytes = shift // die;
51              
52 15         33 substr($self->{data}, $self->{offset}, $n_bytes) = "\0" x $n_bytes;
53              
54 15         30 return $self->skip($n_bytes);
55             }
56              
57             sub skip ($$) {
58 34     34 1 49 my $self = shift;
59 34   50     62 my $n_bytes = shift // die;
60              
61             my $n_avail = $self->{offset} + $n_bytes > $self->size
62 34 100       67 ? $self->size - $self->{offset} : $n_bytes;
63              
64 34         50 $self->{offset} += $n_avail;
65              
66 34 100       79 $self->zero($n_bytes - $n_avail) if $n_avail < $n_bytes;
67              
68 34         58 return $self;
69             }
70              
71             sub stub ($$$) {
72 9     9 1 16 my $self = shift;
73 9   50     20 my $name = shift // '';
74 9   50     20 my $type = shift // '';
75              
76 9 50 66     64 die "type must be either size of bytes or uint{8,16{,_be},32{,_be},64}"
77             unless $type =~ /^uint(8|16(_be)?|32(_be)?|64)$/ || $type =~ /^\d+$/;
78 9         32 $self->{stubs}{$name} = [ $self->{offset}, $type ];
79              
80 9 100       59 $type =~ /^\d+$/
81             ? $self->bytes("\0" x $type)
82             : $self->$type(0);
83              
84 9         24 return $self;
85             }
86              
87             sub fill ($$) {
88 9     9 1 14 my $self = shift;
89 9   50     30 my $name = shift // '';
90 9   50     17 my $data = shift // die;
91              
92 9         18 my $curr_offset = $self->{offset};
93 9 50       10 my ($offset, $type) = @{$self->{stubs}{$name} || die "No previously set stub '$name'"};
  9         32  
94 9         17 $self->{offset} = $offset;
95 9 100       55 $type =~ /^\d+$/
96             ? $self->bytes($data)
97             : $self->$type($data);
98 9         16 $self->{offset} = $curr_offset;
99              
100 9         24 return $self;
101             }
102              
103             sub mark ($$) {
104 11     11 1 20 my $self = shift;
105 11   50     32 my $name = shift // '';
106              
107 11         31 $self->{marks}{$name} = $self->{offset};
108              
109 11         31 return $self;
110             }
111              
112             sub jump ($$) {
113 4     4 1 13 my $self = shift;
114 4   50     18 my $name = shift // '';
115              
116 4   50     23 $self->{offset} = $self->{marks}{$name} || 0;
117              
118 4         17 return $self;
119             }
120              
121             sub diff ($$) {
122 17     17 1 19 my $self = shift;
123 17   100     34 my $name = shift // '';
124              
125 17   100     85 return $self->{offset} - ($self->{marks}{$name} || 0);
126             }
127              
128             sub align ($;$$) {
129 11     11 1 19 my $self = shift;
130 11         13 my $name = shift;
131 11   100     32 my $step = shift || 4;
132              
133 11         26 $self->skip(($step - $self->diff($name) % $step) % $step);
134              
135 11         30 return $self;
136             }
137              
138             my %UINT_MODS = (
139             +1 => 'C',
140             +2 => 'v',
141             +4 => 'V',
142             -1 => 'C',
143             -2 => 'n',
144             -4 => 'N',
145             );
146              
147             sub uint ($$$$) {
148 306     306 0 394 my $self = shift;
149 306         337 my $n_bytes = shift;
150 306 100       470 my $be_factor = shift() ? -1 : 1;
151 306         335 my $i = shift;
152              
153 306         970 return $self->bytes(pack($UINT_MODS{$be_factor * $n_bytes}, $i));
154             }
155              
156             sub str ($$;$) {
157 23     23 1 34 my $self = shift;
158 23         38 my $str = shift;
159 23   100     87 my $enc = shift || 'UTF-16LE';
160              
161 23         67 return $self->bytes(encode($enc, $str));
162             }
163              
164             sub bytes ($$) {
165 357     357 1 11388 my $self = shift;
166 357         448 my $data = shift;
167              
168 357 100       693 $data = join('', @$data) if ref($data) eq 'ARRAY';
169              
170 357         636 substr($self->{data}, $self->{offset}, length($data)) = $data;
171 357         440 $self->{offset} += length($data);
172              
173 357         1033 return $self;
174             }
175              
176 40     40 1 3950 sub uint8 { uint($_[0], 1, 0, $_[1]); }
177 95     95 1 1937 sub uint16 { uint($_[0], 2, 0, $_[1]); }
178 162     162 1 1050 sub uint32 { uint($_[0], 4, 0, $_[1]); }
179 6     6 1 1773 sub uint16_be { uint($_[0], 2, 1, $_[1]); }
180 3     3 1 738 sub uint32_be { uint($_[0], 4, 1, $_[1]); }
181 39     39 1 91 sub uint64 { uint32($_[0], $_[1] & 0xffffffff); uint32($_[0], $_[1] >> 32); }
  39         75  
182 1     1 1 5 sub utf16 { str($_[0], $_[1]); }
183 0     0 1 0 sub utf16_be { str($_[0], $_[1], 'UTF-16BE'); }
184 0     0 1 0 sub fid1 { uint16($_[0], $_[1]); }
185 1     1 1 10 sub fid2 { uint64($_[0], $_[1][0]); uint64($_[0], $_[1][1]); }
  1         4  
186              
187             1;
188              
189             __END__