File Coverage

lib/SMB/v2/Command/QueryInfo.pm
Criterion Covered Total %
statement 18 86 20.9
branch 0 48 0.0
condition 0 40 0.0
subroutine 6 10 60.0
pod 0 4 0.0
total 24 188 12.7


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::v2::Command::QueryInfo;
17              
18 1     1   8 use strict;
  1         3  
  1         34  
19 1     1   7 use warnings;
  1         3  
  1         37  
20              
21 1     1   8 use parent 'SMB::v2::Command';
  1         3  
  1         6  
22              
23 1     1   80 use SMB::Packer;
  1         3  
  1         28  
24 1     1   7 use SMB::Time;
  1         2  
  1         144  
25              
26             our $start_time = time();
27              
28             use constant {
29 1         1944 TYPE_FILE => 1,
30             TYPE_FILESYSTEM => 2,
31             TYPE_SECURITY => 3,
32             TYPE_QUOTA => 4,
33              
34             FILE_LEVEL_DIRECTORY => 1,
35             FILE_LEVEL_FULLDIRECTORY => 2,
36             FILE_LEVEL_BOTHDIRECTORY => 3,
37             FILE_LEVEL_BASIC => 4,
38             FILE_LEVEL_STANDARD => 5,
39             FILE_LEVEL_INTERNAL => 6,
40             FILE_LEVEL_EA => 7,
41             FILE_LEVEL_ACCESS => 8,
42             FILE_LEVEL_NAME => 9,
43             FILE_LEVEL_NAMES => 12,
44             FILE_LEVEL_POSITION => 14,
45             FILE_LEVEL_FULLEA => 15,
46             FILE_LEVEL_MODE => 16,
47             FILE_LEVEL_ALIGNMENT => 17,
48             FILE_LEVEL_ALL => 18,
49             FILE_LEVEL_ALTERNATENAME => 21,
50             FILE_LEVEL_STREAM => 22,
51             FILE_LEVEL_PIPE => 23,
52             FILE_LEVEL_PIPELOCAL => 24,
53             FILE_LEVEL_PIPEREMOTE => 25,
54             FILE_LEVEL_COMPRESSION => 28,
55             FILE_LEVEL_QUOTA => 32,
56             FILE_LEVEL_NETWORKOPEN => 34,
57             FILE_LEVEL_ATTRIBUTETAG => 35,
58             FILE_LEVEL_IDBOTHDIRECTORY => 37,
59             FILE_LEVEL_IDFULLDIRECTORY => 38,
60              
61             FS_LEVEL_VOLUMEINFORMATION => 1,
62             FS_LEVEL_SIZEINFORMATION => 3,
63             FS_LEVEL_DEVICEINFORMATION => 4,
64             FS_LEVEL_ATTRIBUTEINFORMATION => 5,
65             FS_LEVEL_CONTROLINFORMATION => 6,
66             FS_LEVEL_FULLSIZEINFORMATION => 7,
67             FS_LEVEL_OBJECTIDINFORMATION => 8,
68             FS_LEVEL_SECTORSIZEINFORMATION => 11,
69 1     1   8 };
  1         3  
70              
71             my @type_names = (undef, "FILE", "FS", "SECURITY", "QUOTA");
72              
73             sub init ($) {
74 0     0 0   $_[0]->set(
75             type => 0,
76             level => 0,
77             max_length => 65536,
78             additional => 0,
79             flags => 0,
80             buffer => undef,
81             fid => 0,
82             openfile => undef,
83             files => undef,
84             )
85             }
86              
87             sub parse ($$) {
88 0     0 0   my $self = shift;
89 0           my $parser = shift;
90              
91 0 0         if ($self->is_response) {
92 0           my $offset = $parser->uint16;
93 0           my $length = $parser->uint32;
94 0           $self->buffer($parser->bytes($length));
95             } else {
96 0           $self->type($parser->uint8);
97 0           $self->level($parser->uint8);
98 0           $self->max_length($parser->uint32);
99 0           my $offset = $parser->uint16;
100 0           $parser->skip(2); # reserved
101 0           my $length = $parser->uint32;
102 0           $self->additional($parser->uint32);
103 0           $self->flags($parser->uint32);
104 0           $self->fid($parser->fid2);
105 0           $self->buffer($parser->bytes($length));
106             }
107              
108 0           return $self;
109             }
110              
111             sub pack ($$) {
112 0     0 0   my $self = shift;
113 0           my $packer = shift;
114              
115 0           my $buffer = $self->buffer;
116              
117 0 0         if ($self->is_response) {
118 0 0 0       $packer
119             ->uint16($packer->diff('smb-header') + 6)
120             ->uint32(defined $buffer ? length($buffer) : 0)
121             ->bytes($buffer // '')
122             ;
123             } else {
124 0 0 0       $packer
      0        
125             ->uint8($self->type)
126             ->uint8($self->level)
127             ->uint32($self->max_length)
128             ->uint16($packer->diff('smb-header') + 32)
129             ->uint16(0) # reserved
130             ->uint32(defined $buffer ? length($buffer) : 0)
131             ->uint32($self->additional)
132             ->uint32($self->flags)
133             ->fid2($self->fid || die "No fid set")
134             ->bytes($buffer // '')
135             ;
136             }
137             }
138              
139             sub prepare_info ($%) {
140 0     0 0   my $self = shift;
141 0           my %params = @_;
142              
143 0           my $type = $self->type;
144 0           my $level = $self->level;
145              
146 0 0         my $openfile = $self->openfile
147             or return $self->msg("Called prepare_fs_response without openfile");
148 0 0         my $file = $openfile->file
149             or return $self->msg("Called prepare_fs_response without file");
150              
151 0   0       my $filename = $file->filename // "IPC\$\\$file->{name}";
152 0           my $packer = SMB::Packer->new;
153              
154 0   0       my $type_name = $type_names[$type] || "UNKNOWN";
155             $self->msg("Info $type_name level=$level for $filename")
156 0 0         unless $params{quiet};
157              
158 0 0         if ($type == TYPE_FILE) {
    0          
159 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_BASIC) {
160 0           $packer
161             ->uint64($file->creation_time)
162             ->uint64($file->last_access_time)
163             ->uint64($file->last_write_time)
164             ->uint64($file->change_time)
165             ->uint32($file->attributes)
166             ->uint32(0) # reserved
167             ;
168             }
169 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_STANDARD) {
170 0           $packer
171             ->uint64($file->allocation_size)
172             ->uint64($file->end_of_file)
173             ->uint32(1) # number of links
174             ->uint8($openfile->delete_on_close) # delete pending
175             ->uint8($file->is_directory)
176             ->uint16(0) # reserved
177             ;
178             }
179 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_INTERNAL) {
180 0           $packer
181             ->uint64($file->id)
182             ;
183             }
184 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_EA) {
185 0           $packer
186             ->uint32(0) # ea (external attributes) size
187             ;
188             }
189 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_ACCESS) {
190 0           $packer
191             ->uint32(0x00000080) # access flags (READ ATTRIBUTES)
192             ;
193             }
194 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_POSITION) {
195 0           $packer
196             ->uint64(0) # current byte offset
197             ;
198             }
199 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_MODE) {
200 0           $packer
201             ->uint32(0) # mode
202             ;
203             }
204 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_ALIGNMENT) {
205 0           $packer
206             ->uint32(0) # alignment requirement
207             ;
208             }
209 0 0 0       if ($level == FILE_LEVEL_ALL || $level == FILE_LEVEL_NAME) {
210 0           my $filename = $file->name;
211              
212 0           $packer
213             ->uint32(length($filename) * 2)
214             ->utf16($filename)
215             ;
216             }
217              
218 0 0         if ($level == FILE_LEVEL_NETWORKOPEN) {
219 0           $packer
220             ->uint64($file->creation_time)
221             ->uint64($file->last_access_time)
222             ->uint64($file->last_write_time)
223             ->uint64($file->change_time)
224             ->uint64($file->allocation_size)
225             ->uint64($file->end_of_file)
226             ->uint32($file->attributes)
227             ->uint32(0) # reserved
228             ;
229             }
230              
231 0 0         if ($packer->size == 0) {
232 0           $self->err('Ignoring unsupported FILE level $level, expect problems');
233             }
234             }
235             elsif ($type == TYPE_FILESYSTEM) {
236 0 0         if ($level == FS_LEVEL_VOLUMEINFORMATION) {
    0          
    0          
    0          
237 0           my $name = "SMB.pm";
238 0           $packer
239             ->uint64(to_nttime($start_time)) # created time
240             ->uint32($file->id) # volume serial number
241             ->uint32(length($name) * 2)
242             ->utf16($name)
243             ->uint16(0x017f) # reserved
244             ;
245             }
246             elsif ($level == FS_LEVEL_ATTRIBUTEINFORMATION) {
247 0   0       my $fs_type = (split(/\n/, `LANG=C df --output=fstype $filename 2>/dev/null`))[1] || "unknown";
248 0           $packer
249             ->uint32(0x00007) # attributes
250             ->uint32(255) # max filename length
251             ->uint32(length($fs_type) * 2)
252             ->utf16($fs_type)
253             ;
254             }
255             elsif ($level == FS_LEVEL_DEVICEINFORMATION) {
256 0           $packer
257             ->uint32(0x7) # device type (Disk)
258             ->uint32(0x20) # characterictics
259             ;
260             }
261             elsif ($level == FS_LEVEL_FULLSIZEINFORMATION) {
262 0           $packer
263             ->uint64(0x0) # allocation size
264             ->uint64(0x0) # caller free units
265             ->uint64(0x0) # actual free units
266             ->uint32(0x0) # sectors per unit
267             ->uint32(0x0) # bytes per sector
268             ;
269             }
270             else {
271 0           $self->err('Ignoring unsupported FS level $level, expect problems');
272             }
273             }
274             else {
275 0           $self->err('Ignoring unsupported INFO type $type, expect problems');
276             }
277              
278 0           $self->buffer($packer->data);
279             }
280              
281             1;