File Coverage

lib/SMB/Connection.pm
Criterion Covered Total %
statement 24 111 21.6
branch 0 32 0.0
condition 0 22 0.0
subroutine 8 29 27.5
pod 2 20 10.0
total 34 214 15.8


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::Connection;
17              
18 1     1   5 use strict;
  1         2  
  1         20  
19 1     1   4 use warnings;
  1         1  
  1         16  
20              
21 1     1   430 use bytes;
  1         11  
  1         5  
22              
23 1     1   23 use parent 'SMB';
  1         9  
  1         4  
24              
25 1     1   339 use SMB::Parser;
  1         1  
  1         23  
26 1     1   323 use SMB::Packer;
  1         1  
  1         22  
27 1     1   332 use SMB::v1::Commands;
  1         2  
  1         24  
28 1     1   341 use SMB::v2::Commands;
  1         2  
  1         950  
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           my $self = $class->SUPER::new(
51             %options,
52             socket => $socket,
53             id => $id,
54             parser => SMB::Parser->new,
55             packer => SMB::Packer->new,
56             );
57              
58 0 0         unless ($self->log_level == SMB::LOG_LEVEL_NONE) {
59 0           my $addr_with_port = $self->get_socket_addr;
60 0 0         my ($id0, $str) = $id =~ /^-(.*)/ ? ($1, 'server') : ($id, 'client');
61 0           $self->{id_str} = "$str #$id0 [$addr_with_port]";
62             }
63              
64 0           $self->msg("Connected");
65              
66 0           return $self;
67             }
68              
69             sub DESTROY ($) {
70 0     0     my $self = shift;
71              
72 0           $self->close;
73             }
74              
75             sub close ($) {
76 0     0 0   my $self = shift;
77              
78 0           my $socket = $self->socket;
79 0 0 0       return unless $socket && $socket->opened;
80              
81 0           $self->msg("Disconnected");
82              
83 0           $socket->close;
84 0           $self->socket(undef);
85             }
86              
87             sub get_socket_addr ($;$) {
88 0     0 0   my $this = shift;
89 0   0       my $socket = shift || ref($this) && $this->socket || return;
90              
91 0           my $host = $socket->peerhost();
92 0           my $port = $socket->peerport();
93              
94 0 0         return wantarray ? ($host, $port) : "$host:$port";
95             }
96              
97             sub recv_nbss ($) {
98 0     0 0   my $self = shift;
99              
100 0           my $socket = $self->socket;
101 0           my $data1; # NBSS header
102             my $data2; # SMB packet
103 0           my $header_label = 'NetBIOS Session Service header';
104 0   0       my $len = $socket->read($data1, 4) //
105             return $self->err("Socket read failed: $!");
106 0 0         if ($len != 4) {
107 0           $self->err("Can't read $header_label (got $len bytes)");
108 0           return;
109             }
110 0           my ($packet_type, $packet_flags, $packet_len) = unpack('CCn', $data1);
111 0 0 0       if ($packet_type != 0 || $packet_flags > 1) {
112 0           $self->err("Only supported $header_label with type=0 flags=0|1");
113 0           return;
114             }
115 0 0         $packet_len += 1 << 16 if $packet_flags;
116 0   0       $len = $socket->read($data2, $packet_len) // 0;
117 0 0         if ($len != $packet_len) {
118 0           $self->err("Can't read full packet (expected $packet_len, got $len bytes)");
119 0           return;
120             }
121              
122 0           $self->parser->set($data1 . $data2, 4);
123             }
124              
125             sub recv_command ($) {
126 0     0 0   my $self = shift;
127              
128 0 0         $self->recv_nbss
129             or return;
130              
131 0           my $smb_num = $self->parse_uint8;
132 0           my $smb_str = $self->parse_bytes(3);
133 0 0 0       if ($smb_str ne 'SMB' || $smb_num != 0xff && $smb_num != 0xfe) {
      0        
134 0           $self->err("Neither SMB1 nor SMB2 signature found, giving up");
135 0           $self->mem(chr($smb_num) . $smb_str, "Signature");
136 0           return;
137             }
138 0           my $is_smb1 = $smb_num == 0xff;
139 0           $self->mem($self->parser->data, "<- SMB Packet");
140              
141 0 0         my $command = $is_smb1
142             ? $self->parse_smb1
143             : $self->parse_smb2;
144              
145 0 0         if ($command) {
146 0           $self->dbg("%s", $command->to_string);
147             } else {
148 0 0         $self->err("Failed to parse SMB%d packet", $is_smb1 ? 1 : 2);
149             }
150              
151 0           return $command;
152             }
153              
154             sub send_nbss ($$) {
155 0     0 0   my $self = shift;
156 0           my $data = shift;
157              
158 0           $self->mem($data, "-> NetBIOS Packet");
159              
160 0 0         if (!$self->socket->write($data, length($data))) {
161 0           $self->err("Can't write full packet");
162 0           return;
163             }
164             }
165              
166             sub send_command ($$) {
167 0     0 0   my $self = shift;
168 0           my $command = shift;
169              
170 0           $self->dbg("%s", $command->to_string);
171              
172 0           $self->packer->reset;
173              
174 0 0         $command->is_smb1
175             ? $self->pack_smb1($command, is_response => 1)
176             : $self->pack_smb2($command, is_response => 1);
177              
178 0           $self->send_nbss($self->packer->data);
179             }
180              
181             sub log ($$$) {
182 0     0 1   my $self = shift;
183 0           my $level = shift;
184 0           my $format = shift;
185 0 0         return if $level > $self->{log_level};
186              
187 0           $format =~ s/(:?$)/ - $self->{id_str}$1/;
188 0           $self->SUPER::log($level, $format, @_);
189             }
190              
191             1;