File Coverage

blib/lib/Nagios/NRPE/Packet.pm
Criterion Covered Total %
statement 90 119 75.6
branch 5 12 41.6
condition 4 11 36.3
subroutine 15 17 88.2
pod 9 9 100.0
total 123 168 73.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =pod
4              
5             =head1 NAME
6              
7             Nagios::NRPE::Packet - Assembly and de-assembly of an NRPE packet
8              
9             =head1 SYNOPSIS
10              
11             use IO::Socket;
12             use IO::Socket::INET;
13             # Import necessary constants into Namespace
14             use Nagios::NRPE::Packet qw(NRPE_PACKET_VERSION_3
15             NRPE_PACKET_QUERY
16             MAX_PACKETBUFFER_LENGTH
17             STATE_UNKNOWN
18             STATE_CRITICAL
19             STATE_WARNING
20             STATE_OK);
21              
22             my $packet = Nagios::NRPE::Packet->new();
23              
24             my $socket = IO::Socket::INET->new(
25             PeerAddr => $host,
26             PeerPort => $port,
27             Proto => 'tcp',
28             Type => SOCK_STREAM) or die "ERROR: $@ \n";
29              
30             print $socket $packet->assemble(type => QUERY_PACKET,
31             buffer => "check_load 1 2 3",
32             version => NRPE_PACKET_VERSION_3 );
33              
34             my $data = <$socket>
35             my $response = $packet->deassemble($data);
36              
37             print $response->{buffer};
38             exit $response->{result_code};
39              
40             =head1 DESCRIPTION
41              
42             This class is meant to be used when an active connection exists and is ready to send the
43             packet.
44              
45             =head1 CONSTRUCTION
46              
47             =over
48              
49             =item new
50              
51             Takes the following options as a hashref
52              
53             =back
54              
55             =head1 SUBROUTINES
56              
57             Following functions can be used after the creation of the packet
58              
59             =over 2
60              
61             =item assemble()
62              
63             Takes a hash of options defining the packet to be sent and returns the assembled packet. You can print this
64             to an open socket and send it to either a server or the client depending on your situation.
65              
66             * check
67              
68             A string defining the check to be run or the output of a check eg: "check_cpu"
69             NOTE: Nagios can accept arguments appended to the check in the form: "check_somecheck!ARG1!ARG2!ARG..."
70              
71             * version
72              
73             The NRPE version you want to use (only V2 and V3 work V1 is not supported, deafult is V3).
74              
75             See CONSTANTS for options here.
76              
77             * type
78              
79             The TYPE of packet you wish to send, which is either QUERY or RESPONSE.
80              
81             See CONSTANTS for options here.
82              
83             * result_code
84              
85             This is the exit code of the check script that is run, and check_nrpe.pl will exit with this value from the
86             RESPONSE packet.
87              
88             A set value for the QUERY type packet is 2324.
89              
90             =item assemble_v2()
91              
92             A helper function to assemble a V2 packet.
93              
94             =item assemble_v3()
95              
96             A helper function to assemble a V3 packet.
97              
98             =item deassemble()
99              
100             Takes a packet recieved by either client or server and deassembles them. The returned hashref contains
101             the following values for a V3 packet:
102              
103             packet_version
104             packet_type
105             crc32_value
106             result_code
107             alignment
108             buffer_length
109             buffer
110              
111             and the following values for a V2 packet:
112              
113             packet_version
114             packet_type
115             crc32_value
116             result_code
117             buffer
118              
119             =item deassemble_v2()
120              
121             Helper function for deassembleing a V2 packet
122              
123             =item deassemble_v3()
124              
125             Helper function for deassembleing a V3 packet
126              
127             =item validate($packet)
128              
129             Validates the contents of a packet using CRC32 checksumming. Returns undef
130             if not succesful.
131              
132              
133             =item packet_dump
134              
135             Debugging function for hexdumping a binary string.
136              
137             =back
138              
139             =head1 CONSTANTS
140              
141             These constants can be exported upon request with the 'use' pragma like this:
142              
143             # Will only import the constant NRPE_PACKET_VERSION_3 into your namespace
144             use Nagios::NRPE::Packet qw(NRPE_PACKET_VERSION_3);
145              
146             =over 2
147              
148             =item * NRPE_PACKET_VERSION_3
149             NRPE_PACKET_VERSION_2
150             NRPE_PACKET_VERSION_1
151              
152             The value of the NRPE version you want/need to use.
153              
154             =item * QUERY_PACKET
155             RESPONSE_PACKET
156              
157             The packet type you want to send or recieve
158              
159             =item * MAX_PACKETBUFFER_LENGTH
160             MAX_COMMAND_ARGUMENTS
161              
162             A threshhold on the send data
163              
164             =item * NRPE_HELLO_COMMAND
165              
166             unknown
167              
168             =item * DEFAULT_SOCKET_TIMEOUT
169             DEFAULT_CONNECTION_TIMEOUT
170              
171             The default timeout for a connection and its corresponding socket
172              
173             =item * STATE_UNKNOWN
174             STATE_CRITICAL
175             STATE_WARNING
176             STATE_OK
177              
178             States returned by the check
179              
180             =back
181              
182             =head1 COPYRIGHT AND LICENSE
183              
184             This software is copyright (c) 2017 by the authors (see AUTHORS file).
185              
186             This is free software; you can redistribute it and/or modify it under
187             the same terms as the Perl 5 programming language system itself.
188              
189             =cut
190              
191             package Nagios::NRPE::Packet;
192              
193             our $VERSION = '1.0.2';
194              
195 2     2   92417 use 5.010_000;
  2         10  
196             require Exporter;
197             require overload;
198              
199             BEGIN {
200 2     2   41 @ISA = qw(Exporter);
201 2         58 @EXPORT_OK = qw(NRPE_PACKET_VERSION_3
202             NRPE_PACKET_VERSION_2
203             NRPE_PACKET_VERSION_1
204             NRPE_PACKET_QUERY
205             NRPE_PACKET_RESPONSE
206             MAX_PACKETBUFFER_LENGTH
207             MAX_COMMAND_ARGUMENTS
208             NRPE_HELLO_COMMAND
209             DEFAULT_SOCKET_TIMEOUT
210             DEFAULT_CONNECTION_TIMEOUT
211             STATE_UNKNOWN
212             STATE_CRITICAL
213             STATE_WARNING
214             STATE_OK);
215             }
216              
217 2     2   15 use warnings;
  2         5  
  2         113  
218 2     2   14 use strict;
  2         12  
  2         63  
219              
220 2     2   14 use Carp;
  2         5  
  2         1010  
221 2     2   3293 use Convert::Binary::C;
  2         2990  
  2         88  
222 2     2   1363 use Digest::CRC 'crc32';
  2         6133  
  2         255  
223              
224             use constant {
225              
226             # packet version identifier
227 2         3594 NRPE_PACKET_VERSION_3 => 3,
228             NRPE_PACKET_VERSION_2 => 2,
229             NRPE_PACKET_VERSION_1 => 1,
230              
231             # id code for queries and responses to queries
232             NRPE_PACKET_QUERY => 1,
233             NRPE_PACKET_RESPONSE => 2,
234              
235             # max amount of data we'll send in one query/response
236             MAX_PACKETBUFFER_LENGTH => 1024,
237             MAX_COMMAND_ARGUMENTS => 16,
238             NRPE_HELLO_COMMAND => "_NRPE_CHECK",
239             DEFAULT_SOCKET_TIMEOUT => 10,
240             DEFAULT_CONNECTION_TIMEOUT => 300,
241              
242             # /* service state return codes */
243             STATE_UNKNOWN => 3,
244             STATE_CRITICAL => 2,
245             STATE_WARNING => 1,
246             STATE_OK => 0,
247 2     2   22 };
  2         6  
248              
249             sub new {
250 2     2 1 1623 my ( $class, %options ) = @_;
251 2         6 my $self = {};
252              
253             # taken with modifications from common.h in nagios-nrpe
254 2         58 my $c = Convert::Binary::C->new( ByteOrder => 'BigEndian', Alignment => 0 );
255 2         6 $self->{c} = $c;
256 2         7 bless $self, $class;
257             }
258              
259             sub assemble {
260 2     2 1 4677 my ( $self, %options ) = @_;
261             croak "ERROR: Cannot send Packet with empty buffer!"
262 2 50       10 if ( not defined $options{check} );
263 2         6 my $packed;
264 2 100       9 if ( $options{version} eq NRPE_PACKET_VERSION_2 ) {
265 1         10 $packed = $self->assemble_v2(%options);
266             }
267             else {
268 1         5 $packed = $self->assemble_v3(%options);
269             }
270 2         29 return $packed;
271              
272             }
273              
274             sub assemble_v3 {
275 1     1 1 4 my ( $self, %options ) = @_;
276 1         2 my $unpacked = {};
277 1         3 my $len = length( $options{check} ) + 1;
278              
279 1         2 $unpacked->{alignment} = 0;
280 1         2 $unpacked->{buffer_length} = $len;
281 1         2 $unpacked->{buffer} = $options{check};
282 1         12 $unpacked->{crc32_value} = "\x00\x00\x00\x00";
283 1   50     9 $unpacked->{packet_type} = $options{type} || NRPE_PACKET_QUERY;
284 1         2 $unpacked->{packet_version} = NRPE_PACKET_VERSION_3;
285 1   50     16 $unpacked->{result_code} = $options{result_code} || 2324;
286              
287 1         185 $self->{c}->parse(<
288             struct Packet{
289             unsigned short packet_version;
290             unsigned short packet_type;
291             unsigned int crc32_value;
292             unsigned short result_code;
293             unsigned short alignment;
294             int buffer_length;
295             char buffer[$len];
296             };
297             PACKET_STRUCT
298 1         17 $self->{c}->tag( 'Packet.buffer', Format => 'String' );
299              
300 1         16 my $packed = $self->{c}->pack( 'Packet', $unpacked );
301              
302 1         7 $unpacked->{crc32_value} = crc32($packed);
303 1         65 $packed = $self->{c}->pack( 'Packet', $unpacked );
304 1         8 return $packed;
305              
306             }
307              
308             sub assemble_v2 {
309              
310 1     1 1 7 my ( $self, %options ) = @_;
311 1         3 my $unpacked = {};
312              
313 1         5 $unpacked->{buffer} = $options{check};
314 1         5 $unpacked->{crc32_value} = "\x00\x00\x00\x00";
315 1   50     9 $unpacked->{packet_type} = $options{type} || NRPE_PACKET_QUERY;
316 1         4 $unpacked->{packet_version} = NRPE_PACKET_VERSION_2;
317 1   50     11 $unpacked->{result_code} = $options{result_code} || 2324;
318              
319 1         303 $self->{c}->parse(<
320             struct Packet{
321             unsigned short packet_version;
322             unsigned short packet_type;
323             unsigned int crc32_value;
324             unsigned short result_code;
325             char buffer[1024];
326             };
327             PACKET_STRUCT
328 1         23 $self->{c}->tag( 'Packet.buffer', Format => 'String' );
329              
330 1         52 my $packed = $self->{c}->pack( 'Packet', $unpacked );
331              
332 1         13 $unpacked->{crc32_value} = crc32($packed);
333 1         78 $packed = $self->{c}->pack( 'Packet', $unpacked );
334 1         9 return $packed;
335              
336             }
337              
338             sub validate {
339 0     0 1 0 my ( $self, $packet ) = @_;
340 0         0 my $unpacked = $self->{c}->unpack( 'Packet', $packet );
341 0         0 my $checksum = $unpacked->{crc32_value};
342 0         0 $unpacked->{crc32_value} = "\x00\x00\x00\x00";
343 0         0 my $packed = $self->{c}->pack( 'Packet', $unpacked );
344 0 0       0 if ( crc32($packed) != $checksum ) {
345 0         0 return undef;
346             }
347             else {
348 0         0 return 1;
349             }
350             }
351              
352             sub deassemble {
353 2     2 1 18 my ( $self, $packet ) = @_;
354 2         18 my $ver = unpack( "n", $packet );
355 2         7 my $unpacked = {};
356 2 100       10 if ( $ver eq NRPE_PACKET_VERSION_2 ) {
357 1         7 $unpacked = $self->deassemble_v2($packet);
358             }
359             else {
360 1         8 $unpacked = $self->deassemble_v3($packet);
361             }
362 2         6 return $unpacked;
363             }
364              
365             sub deassemble_v3 {
366 1     1 1 5 my ( $self, $packet ) = @_;
367 1         7 my @arr = unpack( "n2 N n2 N Z*", $packet );
368 1         4 my $unpacked = {};
369 1         5 $unpacked->{packet_version} = $arr[0];
370 1         2 $unpacked->{packet_type} = $arr[1];
371 1         3 $unpacked->{crc32_value} = $arr[2];
372 1         2 $unpacked->{result_code} = $arr[3];
373 1         2 $unpacked->{alignment} = $arr[4];
374 1         2 $unpacked->{buffer_length} = $arr[5];
375 1         1 $unpacked->{buffer} = $arr[6];
376 1         3 return $unpacked;
377             }
378              
379             sub deassemble_v2 {
380 1     1 1 4 my ( $self, $packet ) = @_;
381 1         8 my @arr = unpack( "n2 N n Z*", $packet );
382 1         4 my $unpacked = {};
383 1         4 $unpacked->{packet_version} = $arr[0];
384 1         5 $unpacked->{packet_type} = $arr[1];
385 1         3 $unpacked->{crc32_value} = $arr[2];
386 1         4 $unpacked->{result_code} = $arr[3];
387 1         4 $unpacked->{buffer} = $arr[4];
388 1         5 return $unpacked;
389             }
390              
391             sub packet_dump {
392 0     0 1   my $packet = shift;
393 0           my $i;
394             my $k;
395 0           my $dump;
396 0           my $l;
397 0           my $ascii;
398 0           my $c;
399 0           for ( $i = 0 ; $i < length($packet) ; $i += 16 ) {
400 0           $l = $i + 16;
401 0 0         if ( $l > length($packet) ) {
402 0           $l = length($packet);
403             }
404 0           $dump = sprintf( "%04d - %04d: ", $i, $l );
405 0           $ascii = "";
406 0           for ( $k = $i ; $k < $l ; $k++ ) {
407 0           $c = ( ord( substr( $packet, $k, 1 ) ) );
408 0           $dump .= sprintf( "%02x ", $c );
409 0 0 0       if ( ( $c >= 32 ) && ( $c <= 126 ) ) {
410 0           $ascii .= chr($c);
411             }
412             else {
413 0           $ascii .= ".";
414             }
415             }
416 0           for ( $k = 0 ; $k < ( $i + 16 - $l ) ; $k++ ) {
417 0           $dump .= " ";
418             }
419 0           print( "packet_dump() " . $dump . " [" . $ascii . "]" . "\n" );
420             }
421             }
422              
423             1;