File Coverage

lib/SMB/Connection.pm
Criterion Covered Total %
statement 24 113 21.2
branch 0 36 0.0
condition 0 26 0.0
subroutine 8 29 27.5
pod 2 20 10.0
total 34 224 15.1


line stmt bran cond sub pod time code
1             # SMB Perl library, Copyright (C) 2014 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::Connection;
17              
18 1     1   4 use strict;
  1         2  
  1         26  
19 1     1   5 use warnings;
  1         557  
  1         23  
20              
21 1     1   5 use bytes;
  1         1  
  1         6  
22              
23 1     1   18 use parent 'SMB';
  1         1  
  1         4  
24              
25 1     1   428 use SMB::Parser;
  1         4  
  1         23  
26 1     1   453 use SMB::Packer;
  1         2  
  1         27  
27 1     1   512 use SMB::v1::Commands;
  1         3  
  1         33  
28 1     1   607 use SMB::v2::Commands;
  1         3  
  1         1254  
29              
30 0     0 0   sub parse_uint8 { $_[0]->parser->uint8; }
31 0     0 0   sub parse_uint16 { $_[0]->parser->uint16; }
32 0     0 0   sub parse_uint32 { $_[0]->parser->uint32; }
33 0     0 0   sub parse_bytes { $_[0]->parser->bytes($_[1]); }
34 0     0 0   sub parse_smb1 { SMB::v1::Commands->parse($_[0]->parser) }
35 0     0 0   sub parse_smb2 { SMB::v2::Commands->parse($_[0]->parser) }
36              
37 0     0 0   sub pack_uint8 { $_[0]->packer->uint8($_[1]); }
38 0     0 0   sub pack_uint16 { $_[0]->packer->uint16($_[1]); }
39 0     0 0   sub pack_uint32 { $_[0]->packer->uint32($_[1]); }
40 0     0 0   sub pack_bytes { $_[0]->packer->bytes($_[1]); }
41 0     0 0   sub pack_smb1 { SMB::v1::Commands->pack(shift()->packer, shift, @_) }
42 0     0 0   sub pack_smb2 { SMB::v2::Commands->pack(shift()->packer, shift, @_) }
43              
44             sub new ($$$%) {
45 0     0 1   my $class = shift;
46 0   0       my $socket = shift || die "No socket";
47 0   0       my $id = shift || die "No id";
48 0           my %options = @_;
49              
50 0   0       my $quiet = delete $options{quiet} || 0;
51 0   0       my $verbose = delete $options{verbose} || 0;
52              
53 0           my $self = $class->SUPER::new(
54             %options,
55             quiet => $quiet,
56             verbose => $verbose,
57             socket => $socket,
58             id => $id,
59             parser => SMB::Parser->new,
60             packer => SMB::Packer->new,
61             );
62              
63 0 0         unless ($self->disable_log) {
64 0           my $addr_with_port = $self->get_socket_addr;
65 0 0         my ($id0, $str) = $id =~ /^-(.*)/ ? ($1, 'server') : ($id, 'client');
66 0           $self->{id_str} = "$str #$id0 [$addr_with_port]";
67             }
68              
69 0           $self->msg("Connected");
70              
71 0           return $self;
72             }
73              
74             sub DESTROY ($) {
75 0     0     my $self = shift;
76              
77 0           $self->close;
78             }
79              
80             sub close ($) {
81 0     0 0   my $self = shift;
82              
83 0           my $socket = $self->socket;
84 0 0 0       return unless $socket && $socket->opened;
85              
86 0           $self->msg("Disconnected");
87              
88 0           $socket->close;
89 0           $self->socket(undef);
90             }
91              
92             sub get_socket_addr ($;$) {
93 0     0 0   my $this = shift;
94 0   0       my $socket = shift || ref($this) && $this->socket || return;
95              
96 0           my $host = $socket->peerhost();
97 0           my $port = $socket->peerport();
98              
99 0 0         return wantarray ? ($host, $port) : "$host:$port";
100             }
101              
102             sub recv_nbss ($) {
103 0     0 0   my $self = shift;
104              
105 0           my $socket = $self->socket;
106 0           my $data1; # NBSS header
107             my $data2; # SMB packet
108 0           my $header_label = 'NetBIOS Session Service header';
109 0   0       my $len = $socket->read($data1, 4) //
110             return $self->err("Read failed: $!");
111 0 0         if ($len != 4) {
112 0           $self->err("Can't read $header_label (got $len bytes)");
113 0           return;
114             }
115 0           my ($packet_type, $packet_flags, $packet_len) = unpack('CCn', $data1);
116 0 0 0       if ($packet_type != 0 || $packet_flags > 1) {
117 0           $self->err("Only supported $header_label with type=0 flags=0|1");
118 0           return;
119             }
120 0 0         $packet_len += 1 << 16 if $packet_flags;
121 0   0       $len = $socket->read($data2, $packet_len) // 0;
122 0 0         if ($len != $packet_len) {
123 0           $self->err("Can't read full packet (expected $packet_len, got $len bytes)");
124 0           return;
125             }
126              
127 0           $self->parser->set($data1 . $data2, 4);
128             }
129              
130             sub recv_command ($) {
131 0     0 0   my $self = shift;
132              
133 0 0         $self->recv_nbss
134             or return;
135              
136 0           my $smb_num = $self->parse_uint8;
137 0           my $smb_str = $self->parse_bytes(3);
138 0 0 0       if ($smb_str ne 'SMB' || $smb_num != 0xff && $smb_num != 0xfe) {
      0        
139 0           $self->err("Neither SMB1 nor SMB2 signature found, giving up");
140 0           $self->mem(chr($smb_num) . $smb_str, "Signature");
141 0           return;
142             }
143 0           my $is_smb1 = $smb_num == 0xff;
144 0 0         $self->mem($self->parser->data, "<- SMB Packet")
145             if $self->verbose;
146              
147 0 0         my $command = $is_smb1
148             ? $self->parse_smb1
149             : $self->parse_smb2;
150              
151 0 0         if ($command) {
152 0           $self->msg("%s", $command->dump);
153             } else {
154 0 0         $self->err("Failed to parse SMB%d packet", $is_smb1 ? 1 : 2);
155             }
156              
157 0           return $command;
158             }
159              
160             sub send_nbss ($$) {
161 0     0 0   my $self = shift;
162 0           my $data = shift;
163              
164 0 0         $self->mem($data, "-> NetBIOS Packet")
165             if $self->verbose;
166              
167 0 0         if (!$self->socket->write($data, length($data))) {
168 0           $self->err("Can't write full packet");
169 0           return;
170             }
171             }
172              
173             sub send_command ($$) {
174 0     0 0   my $self = shift;
175 0           my $command = shift;
176              
177 0           $self->msg("%s", $command->dump);
178              
179 0           $self->packer->reset;
180              
181 0 0         $command->is_smb1
182             ? $self->pack_smb1($command, is_response => 1)
183             : $self->pack_smb2($command, is_response => 1);
184              
185 0           $self->send_nbss($self->packer->data);
186             }
187              
188             sub log ($$$) {
189 0     0 1   my $self = shift;
190 0           my $is_err = shift;
191 0           my $format = shift;
192 0 0         return if $self->{disable_log};
193 0           $format =~ s/(:?$)/ - $self->{id_str}$1/;
194 0           $self->SUPER::log($is_err, $format, @_);
195             }
196              
197             1;